[
  {
    "path": ".gitignore",
    "content": "docs\n*.Makefile\n*.mk\n*.ncb\n*.ninja\n*.props\n*.pyc\n*.rules\n*.scons\n*.sdf\n*.sln\n*.suo\n*.targets\n*.user\n*.vcproj\n*.vcxproj\n*.vcxproj.filters\n*.vpj\n*.vpw\n*.vpwhistu\n*.vtg\n*.xcodeproj\n*~\n.*.sw?\n.DS_Store\n.cproject\n.gdb_history\n.gdbinit\n.metadata\n.project\ntags\nThumbs.db\nv8.log\nnode_modules\n\n"
  },
  {
    "path": "Gruntfile.js",
    "content": "\"use strict\";\n\nmodule.exports = function(grunt) {\n\n  grunt.initConfig({\n    jsdoc: {\n      tdl: {\n        src: ['tdl/*.js'],\n        options: {\n          destination: 'docs/gen',\n          configure: 'jsdoc.conf.json',\n          template: 'node_modules/ink-docstrap/template',\n          private: false,\n        },\n      },\n    },\n    clean: [\n        'docs/gen',\n    ],\n    uglify: {\n      my_target: {\n        files: {\n          'build/tdl.min.js': [\n            'tdl/base.js',\n            'tdl/buffers.js',\n            'tdl/clock.js',\n            'tdl/fast.js',\n            'tdl/fps.js',\n            'tdl/framebuffers.js',\n            'tdl/fullscreen.js',\n            'tdl/io.js',\n            'tdl/loader.js',\n            'tdl/log.js',\n            'tdl/math.js',\n            'tdl/misc.js',\n            'tdl/models.js',\n            'tdl/particles.js',\n            'tdl/primitives.js',\n            'tdl/programs.js',\n            'tdl/quaternions.js',\n            'tdl/screenshot.js',\n            'tdl/shader.js',\n            'tdl/string.js',\n            'tdl/sync.js',\n            'tdl/textures.js',\n            'tdl/webgl.js',\n          ],\n        },\n      },\n    },\n  });\n\n  grunt.loadNpmTasks('grunt-contrib-clean');\n  grunt.loadNpmTasks('grunt-jsdoc');\n  grunt.loadNpmTasks('grunt-contrib-uglify');\n\n  grunt.registerTask('default', ['clean', 'jsdoc', 'uglify']);\n};\n\n"
  },
  {
    "path": "README.md",
    "content": "TDL\n===\n\nPlease check out [TWGL](http://twgljs.org). It's arguably the spiritual successor to TDL.\n\nTDL is a **low-level** library for WebGL apps. It currently focuses on speed of rendering rather than ease of use.\n\nSome [terse docs can be found at here](docs.md)\n\nNote: By **low-level** I mean TDL doesn't currently provide any 3D knowledge. \nThere are almost no built in shaders. There is no scene graph. There are just some objects for wrapping WebGL\nshaders and helping to easily associate vertex data with attributes and update uniforms. \n\nExample: Assuming a shaders like this.\n\n    <script id=\"vshader\" type=\"not-js\">\n    attribute vec4 position;\n    attribute vec2 texcoord;\n    \n    uniform mat4 u_worldMatrix;\n    uniform mat4 u_projectionMatrix;\n    \n    varying vec2 v_texcoord;\n    \n    void main() {\n      gl_Position = u_projectionMatrix * u_worldMatrix * position;\n      v_texcoord = texcoord;\n    }\n    </script>\n    \n    <script id=\"fshader\" type=\"not-js\">\n    varying vec2 v_texcoord;\n    uniform sampler2D u_texture;\n    \n    void main() {\n      gl_FragColor = texture2D(u_texture, v_texcoord);\n    }\n    </script>\n\nIn WebGL you'd do this\n\n    // At init time:\n    var program = UtilToCompileShaders(\"vshader\", \"fshader\");\n    var positionLoc = gl.getAttribLocation(program, \"position\");\n    var texcoordLoc = gl.getAttribLocation(program, \"texcoord\");\n    var worldMatLoc = gl.getUniformLocation(program, \"u_worldMatrix\");\n    var projectionMatLoc = gl.getUniformLocation(program, \"u_projectionMatrix\");\n    var textureLoc = gl.getUniformLocation(program, \"u_texture\");\n\n    var positions = gl.createBuffer();\n    gl.bindBuffer(gl.ARRAY_BUFFER, positions);\n    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positionData), gl.STATIC_DRAW);\n    \n    var tecoords = gl.createBuffer();\n    gl.bindBuffer(gl.ARRAY_BUFFER, texcoords);\n    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoordData), gl.STATIC_DRAW);\n    \n    var texture = gl.createTexture();\n    gl.bindTexture(gl.TEXTURE_2D, texture);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, someImage);\n\n\n    // At draw time\n    gl.bindBuffer(gl.ARRAY_BUFFER, positions);\n    gl.enableVertexAttribArray(programLoc);\n    gl.vertexAttribPointer(programLoc, 3, gl.FLOAT, false, 0, 0);\n    \n    gl.bindBuffer(gl.ARRAY_BUFFER, positions);\n    gl.enableVertexAttribArray(texcoordLoc);\n    gl.vertexAttribPointer(tecoordLoc, 2, gl.FLOAT, false, 0, 0);\n    \n    gl.useProgram(program);\n    gl.uniformMatrix4f(projectionMatLoc, false, projectionMatrix);\n    \n    for (var i = 0; i < 3; ++i)\n    {\n        gl.uniformMatrix4f(worldMatLoc, false, computeWorldMatrix(i));\n        gl.drawArrays(gl.TRIANGLES, 0, num);\n    }\n    \nIn TDL that would be shortened to\n\n    // At init time.\n    var program = tdl.programs.loadProgramFromScriptTags(\"vshader\", \"fshader\");\n    var arrays = {\n      position: new tdl.primitives.AttribBuffer(3, positionData),\n      texcoord: new tdl.primitives.AttribBuffer(2, texcoordData),\n    };\n    var textures = {\n      u_texture: new tdl.textures.loadTexture(someImage),\n    }\n    var model = new tdl.models.Model(program, arrays, textures);\n  \n    \n    // At Draw time\n    var sharedUniforms = {\n      u_projectionMatrix: projectionMatrix,\n    };\n    var perObjectUniforms = {\n      u_worldMatrix: worldMatrix,\n    };\n    \n    model.drawPrep(sharedUniforms);\n    \n    for (var i = 0; i < 3; ++i)\n    {\n        perObjectUnifirms.u_worldMatrix = computeWorldMatrix(i);\n        model.draw(perObjectuniforms);\n    }\n\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"tdl\",\n  \"version\": \"0.0.8\",\n  \"authors\": [\n    {\n      \"name\": \"Gregg Tavares\",\n      \"email\": \"github@greggman.com\",\n      \"homepage\": \"http://games.greggman.com\"\n    }\n  ],\n  \"description\": \"A JavaScript library for WebGL\",\n  \"main\": \"tdl/base.js\",\n  \"moduleType\": [\n    \"amd\"\n  ],\n  \"keywords\": [\n    \"webgl\",\n    \"tdl\"\n  ],\n  \"license\": \"MIT\",\n  \"homepage\": \"https://github.com/greggman/tdl\",\n  \"repository\": \"git://github.com/greggman/tdl.git\",\n  \"ignore\": [\n    \"**/.*\",\n    \"*.md\",\n    \"Gruntfile.js\",\n    \"package.json\",\n    \"bower.json\",\n    \"node_modules\",\n    \"docs\",\n    \"build\",\n    \"example\",\n    \"js\",\n    \"bower_components\",\n    \"test\",\n    \"tests\"\n  ]\n}\n"
  },
  {
    "path": "docs.md",
    "content": "TDL Docs\n========\nI hope the code is pretty straight forward. There's some simple examples here\n\n<a href=\"http://greggman.github.com/tdl/example/example.html\">http://greggman.github.com/tdl/example/example.html</a>\n\n<a href=\"http://greggman.github.com/tdl/example/example2.html\">http://greggman.github.com/tdl/example/example2.html</a>\n\n<a href=\"http://greggman.github.com/tdl/example/picking.html\">http://greggman.github.com/tdl/example/picking.html</a>\n\nMore complex samples can be found at <a href=\"http://webglsamples.googlecode.com\">http://webglsamples.googlecode.com</a>\n\nBriefly...\n\nYour startup code should look like this\n\n    canvas = document.getElementById(\"canvas\");\n    gl = tdl.webgl.setupWebGL(canvas);\n    if (!gl) {\n      return;  // Do nothing\n    }\n\nWhere \"canvas\" is the id of the canvas you want to draw into. \ntdl.webgl.setupWebGL will replace the contents of the containing div \nwith a link to getting a WebGL capable browser if the user's browser \ndoes not support WebGL. \n\nOtherwise...\n\nLoading Shaders\n---------------\n\n    var program = tdl.programs.loadProgram(vertexShaderSource, fragmentShaderSource);\n\nCompiles your shaders and creates a Program object.\n\nLoading Textures\n----------------\n\n    var textures = {\n      name1: tdl.textures.loadTexture(url),\n      name2: tdl.textures.loadTexture(url)\n    };\n\nLoads your textures. The property names must match whatever you called the samplers \nin your shaders. loadTexture can take `[url]` for an image, `[r,g,b,a]` for solid \ntexture. `[url,url,url,url,url,url]` for a cubemap and also `[url]` for a cubemap \nwhere all 6 faces are in a cross. It can also take an img or canvas tag.\n\nCreate Vertices or a Mesh\n-------------------------\n\n    var arrays = tdl.primitives.createSphere(1, 10, 10);\n\nCreates vertices\n\nThe tdl.primitives functions return an object like this \n\n    {\n        position: AttribBuffer,\n        normal: AttribBuffer,\n        texCoord: AttribBuffer\n    };\n\nThe property names must match the attributes in your vertex shader if you want to \nadd more.\n \nA call to tdl.primitives.addTangentsAndBinormals adds the fields \"tangent\" and \n\"binormal\"\n\nCreate a Model\n--------------\n\nOnce you have a program, a texture object and an arrays object you make a new \nmodel with\n\n    var model = new tdl.models.Model(program, array, textures);\n\n\nRendering\n---------\n\nTo draw the model there are 2 functions, `model.drawPrep(uniformMap)` and \n`model.draw(uniformMap)`.\n\nBoth of them take an object with uniformName/value pairs.\n\nmodel.drawPrep binds the program, binds all the textures and attributes and \nsets whatever uniforms you pass in.\n\nmodel.draw sets any more uniforms you pass in and then calls gl.drawElements.  \nThe idea is you call `model.drawPrep` once and then `model.draw` to draw a \nbunch of the same model, changing as few uniforms as possible. This is the \nfastest way to use WebGL.\n\nYour rendering loop should look something like this\n\n    function render() {\n      var time = tdl.webgl.animationTime();\n      model.drawPrep({...});\n      model.draw({...});\n      tdl.webgl.requestAnimationFrame(render, canvas);\n    }\n    render();  // call the first render manually to start it off.  \n\n\nMath\n----\n\nThe math is a little funky. There are 2 math libraries, math.js and fast.js.  \nmath.js comes from O3D and uses `JavaScript` arrays. A Matrix in that \nlibrary is a an array of numbers.  fast.js uses `Float32Array` for its storage \nand most functions take a destination object as the first argument. \nTheoretically this is faster because you can avoid a certain number of \nallocations. It also means the numbers in the array do not have to be queried \nand converted from `JavaScript` Number to floats before calling glUniform.\n"
  },
  {
    "path": "example/example-requirejs.html",
    "content": "<!--\r\n * Copyright 2009, Google Inc.\r\n * All rights reserved.\r\n *\r\n * Redistribution and use in source and binary forms, with or without\r\n * modification, are permitted provided that the following conditions are\r\n * met:\r\n *\r\n *     * Redistributions of source code must retain the above copyright\r\n * notice, this list of conditions and the following disclaimer.\r\n *     * Redistributions in binary form must reproduce the above\r\n * copyright notice, this list of conditions and the following disclaimer\r\n * in the documentation and/or other materials provided with the\r\n * distribution.\r\n *     * Neither the name of Google Inc. nor the names of its\r\n * contributors may be used to endorse or promote products derived from\r\n * this software without specific prior written permission.\r\n *\r\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n-->\r\n<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<meta charset=\"utf-8\">\r\n<title>WebGL TDL Example</title>\r\n<style>\r\nhtml, body {\r\n  width: 100%;\r\n  height: 100%;\r\n  border: 0px;\r\n  padding: 0px;\r\n  margin: 0px;\r\n  background-color: red;\r\n  font-family: sans-serif;\r\n  overflow: hidden;\r\n  color: #fff;\r\n}\r\na {\r\n  color: #fff;\r\n}\r\n#info {\r\n    font-size: small;\r\n    position: absolute;\r\n\ttop: 0px; width: 100%;\r\n\tpadding: 5px;\r\n\ttext-align: center;\r\n\tz-index: 2;\r\n}\r\nCANVAS {\r\n  background-color: gray;\r\n}\r\n.fpsContainer {\r\n  position: absolute;\r\n  top: 10px;\r\n  left: 10px;\r\n  z-index: 2;\r\n  color: white;\r\n  font-family: sans-serif;\r\n  background-color: rgba(0,0,0,0.5);\r\n  border-radius: 10px;\r\n  padding: 10px;\r\n}\r\n#viewContainer {\r\n  width: 100%;\r\n  height: 100%;\r\n}\r\n</style>\r\n<script data-main=\"example-requirejs.js\" src=\"../js/require.js\"></script>\r\n</head>\r\n<body>\r\n<div id=\"info\"><a href=\"http://github.com/greggman/tdl\" target=\"_blank\">tdl.js</a> - example</div>\r\n<div class=\"fpsContainer\">\r\n  <div class=\"fps\">fps: <span id=\"fps\"></div>\r\n</div>\r\n<div id=\"viewContainer\">\r\n<canvas id=\"canvas\" width=\"1024\" height=\"1024\" style=\"width: 100%; height: 100%;\"></canvas>\r\n</div>\r\n</body>\r\n<script id=\"sphereVertexShader\" type=\"text/something-not-javascript\">\r\nuniform mat4 worldViewProjection;\r\nuniform vec3 lightWorldPos;\r\nuniform mat4 world;\r\nuniform mat4 viewInverse;\r\nuniform mat4 worldInverseTranspose;\r\nattribute vec4 position;\r\nattribute vec3 normal;\r\nattribute vec2 texCoord;\r\nvarying vec4 v_position;\r\nvarying vec2 v_texCoord;\r\nvarying vec3 v_normal;\r\nvarying vec3 v_surfaceToLight;\r\nvarying vec3 v_surfaceToView;\r\nvoid main() {\r\n  v_texCoord = texCoord;\r\n  v_position = (worldViewProjection * position);\r\n  v_normal = (worldInverseTranspose * vec4(normal, 0)).xyz;\r\n  v_surfaceToLight = lightWorldPos - (world * position).xyz;\r\n  v_surfaceToView = (viewInverse[3] - (world * position)).xyz;\r\n  gl_Position = v_position;\r\n}\r\n\r\n</script>\r\n<script id=\"sphereFragmentShader\" type=\"text/something-not-javascript\">\r\nprecision mediump float;\r\nuniform vec4 lightColor;\r\nvarying vec4 v_position;\r\nvarying vec2 v_texCoord;\r\nvarying vec3 v_normal;\r\nvarying vec3 v_surfaceToLight;\r\nvarying vec3 v_surfaceToView;\r\n\r\nuniform sampler2D diffuseSampler;\r\nuniform vec4 specular;\r\nuniform sampler2D bumpSampler;\r\nuniform float shininess;\r\nuniform float specularFactor;\r\n\r\nvec4 lit(float l ,float h, float m) {\r\n  return vec4(1.0,\r\n              max(l, 0.0),\r\n              (l > 0.0) ? pow(max(0.0, h), m) : 0.0,\r\n              1.0);\r\n}\r\nvoid main() {\r\n  vec4 diffuse = texture2D(diffuseSampler, v_texCoord);\r\n  vec3 normal = normalize(v_normal);\r\n  vec3 surfaceToLight = normalize(v_surfaceToLight);\r\n  vec3 surfaceToView = normalize(v_surfaceToView);\r\n  vec3 halfVector = normalize(surfaceToLight + surfaceToView);\r\n  vec4 litR = lit(dot(normal, surfaceToLight),\r\n                    dot(normal, halfVector), shininess);\r\n  gl_FragColor = vec4((\r\n  lightColor * (diffuse * litR.y\r\n                        + specular * litR.z * specularFactor)).rgb,\r\n      diffuse.a);\r\n}\r\n</script>\r\n</html>\r\n\r\n\r\n"
  },
  {
    "path": "example/example-requirejs.js",
    "content": "var main = function(\n   TDLBuffers,\n   TDLFast,\n   TDLFps,\n   TDLLog,\n   TDLMath,\n   TDLModels,\n   TDLPrimitives,\n   TDLPrograms,\n   TDLTextures,\n   TDLWebGL) {\n  // globals\n  var gl;                   // the gl context.\n  var canvas;               // the canvas\n  var math;                 // the math lib.\n  var fast;                 // the fast math lib.\n  var g_fpsTimer;           // object to measure frames per second;\n  var g_logGLCalls = true;  // whether or not to log webgl calls\n  var g_debug = false;      // whether or not to debug.\n  var g_drawOnce = false;   // draw just one frame.\n\n  //g_drawOnce = true;\n  //g_debug = true;\n\n  var g_eyeSpeed          = 0.5;\n  var g_eyeHeight         = 2;\n  var g_eyeRadius         = 9;\n\n  function ValidateNoneOfTheArgsAreUndefined(functionName, args) {\n    for (var ii = 0; ii < args.length; ++ii) {\n      if (args[ii] === undefined) {\n        TDLLog.error(\"undefined passed to gl.\" + functionName + \"(\" +\n                     TDLWebGL.glFunctionArgsToString(functionName, args) + \")\");\n      }\n    }\n  }\n\n  function Log(msg) {\n    if (g_logGLCalls) {\n      TDLLog.log(msg);\n    }\n  }\n\n  function LogGLCall(functionName, args) {\n    if (g_logGLCalls) {\n      ValidateNoneOfTheArgsAreUndefined(functionName, args)\n      TDLLog.log(\"gl.\" + functionName + \"(\" +\n                  TDLWebGL.glFunctionArgsToString(functionName, args) + \")\");\n    }\n  }\n\n  function createProgramFromTags(vertexTagId, fragmentTagId) {\n    return TDLPrograms.loadProgram(\n        document.getElementById(vertexTagId).text,\n        document.getElementById(fragmentTagId).text);\n  }\n\n  /**\n   * Sets up Planet.\n   */\n  function setupSphere() {\n    var textures = {\n      diffuseSampler: TDLTextures.loadTexture('assets/sometexture.png')};\n    var program = createProgramFromTags(\n        'sphereVertexShader',\n        'sphereFragmentShader');\n    var arrays = TDLPrimitives.createSphere(0.4, 10, 12);\n\n    return new TDLModels.Model(program, arrays, textures);\n  }\n\n  function initialize() {\n    math = TDLMath;\n    fast = TDLFast;\n    canvas = document.getElementById(\"canvas\");\n    g_fpsTimer = new TDLFps.FPSTimer();\n\n    gl = TDLWebGL.setupWebGL(canvas);\n    if (!gl) {\n      return false;\n    }\n    if (g_debug) {\n      gl = TDLWebGL.makeDebugContext(gl, undefined, LogGLCall);\n    }\n\n    Log(\"--Setup Sphere---------------------------------------\");\n    var sphere = setupSphere();\n\n    var then = 0.0;\n    var clock = 0.0;\n    var fpsElem = document.getElementById(\"fps\");\n\n    // pre-allocate a bunch of arrays\n    var projection = new Float32Array(16);\n    var view = new Float32Array(16);\n    var world = new Float32Array(16);\n    var worldInverse = new Float32Array(16);\n    var worldInverseTranspose = new Float32Array(16);\n    var viewProjection = new Float32Array(16);\n    var worldViewProjection = new Float32Array(16);\n    var viewInverse = new Float32Array(16);\n    var viewProjectionInverse = new Float32Array(16);\n    var eyePosition = new Float32Array(3);\n    var target = new Float32Array(3);\n    var up = new Float32Array([0,1,0]);\n    var lightWorldPos = new Float32Array(3);\n    var v3t0 = new Float32Array(3);\n    var v3t1 = new Float32Array(3);\n    var v3t2 = new Float32Array(3);\n    var v3t3 = new Float32Array(3);\n    var m4t0 = new Float32Array(16);\n    var m4t1 = new Float32Array(16);\n    var m4t2 = new Float32Array(16);\n    var m4t3 = new Float32Array(16);\n    var zero4 = new Float32Array(4);\n    var one4 = new Float32Array([1,1,1,1]);\n\n    // Sphere uniforms.\n    var sphereConst = {\n      viewInverse: viewInverse,\n      lightWorldPos: lightWorldPos,\n      specular: one4,\n      shininess: 50,\n      specularFactor: 0.2};\n    var spherePer = {\n      lightColor: new Float32Array([0,0,0,1]),\n      world: world,\n      worldViewProjection: worldViewProjection,\n      worldInverse: worldInverse,\n      worldInverseTranspose: worldInverseTranspose};\n\n    var frameCount = 0;\n    function render() {\n      ++frameCount;\n      if (!g_drawOnce) {\n        TDLWebGL.requestAnimationFrame(render, canvas);\n      }\n      var now = (new Date()).getTime() * 0.001;\n      var elapsedTime;\n      if(then == 0.0) {\n        elapsedTime = 0.0;\n      } else {\n        elapsedTime = now - then;\n      }\n      then = now;\n\n      g_fpsTimer.update(elapsedTime);\n      fpsElem.innerHTML = g_fpsTimer.averageFPS;\n\n      clock += elapsedTime;\n      eyePosition[0] = Math.sin(clock * g_eyeSpeed) * g_eyeRadius;\n      eyePosition[1] = g_eyeHeight;\n      eyePosition[2] = Math.cos(clock * g_eyeSpeed) * g_eyeRadius;\n\n      gl.colorMask(true, true, true, true);\n      gl.depthMask(true);\n      gl.clearColor(0,0,0,0);\n      gl.clearDepth(1);\n      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);\n\n      gl.enable(gl.CULL_FACE);\n      gl.enable(gl.DEPTH_TEST);\n\n      fast.matrix4.perspective(\n          projection,\n          math.degToRad(60),\n          canvas.clientWidth / canvas.clientHeight,\n          1,\n          5000);\n      fast.matrix4.lookAt(\n          view,\n          eyePosition,\n          target,\n          up);\n      fast.matrix4.mul(viewProjection, view, projection);\n      fast.matrix4.inverse(viewInverse, view);\n      fast.matrix4.inverse(viewProjectionInverse, viewProjection);\n\n      fast.matrix4.getAxis(v3t0, viewInverse, 0); // x\n      fast.matrix4.getAxis(v3t1, viewInverse, 1); // y;\n      fast.matrix4.getAxis(v3t2, viewInverse, 2); // z;\n      fast.mulScalarVector(v3t0, 10, v3t0);\n      fast.mulScalarVector(v3t1, 10, v3t1);\n      fast.mulScalarVector(v3t2, 10, v3t2);\n      fast.addVector(lightWorldPos, eyePosition, v3t0);\n      fast.addVector(lightWorldPos, lightWorldPos, v3t1);\n      fast.addVector(lightWorldPos, lightWorldPos, v3t2);\n\n  //      view: view,\n  //      projection: projection,\n  //      viewProjection: viewProjection,\n\n      Log(\"--Draw sphere---------------------------------------\");\n      sphere.drawPrep(sphereConst);\n      var across = 6;\n      var lightColor = spherePer.lightColor;\n      var half = (across - 1) * 0.5;\n      for (var xx = 0; xx < across; ++xx) {\n        for (var yy = 0; yy < across; ++yy) {\n          for (var zz = 0; zz < across; ++zz) {\n            lightColor[0] = xx / across;\n            lightColor[1] = yy / across;\n            lightColor[2] = zz / across;\n            var scale = (xx + yy + zz) % 4 / 4 + 0.5;\n            fast.matrix4.scaling(m4t0, [scale, scale, scale]);\n            fast.matrix4.translation(m4t1, [xx - half, yy - half, zz - half]);\n            fast.matrix4.mul(world, m4t0, m4t1);\n            fast.matrix4.mul(worldViewProjection, world, viewProjection);\n            fast.matrix4.inverse(worldInverse, world);\n            fast.matrix4.transpose(worldInverseTranspose, worldInverse);\n            sphere.draw(spherePer);\n          }\n        }\n      }\n\n      // Set the alpha to 255.\n      gl.colorMask(false, false, false, true);\n      gl.clearColor(0,0,0,1);\n      gl.clear(gl.COLOR_BUFFER_BIT);\n\n      // turn off logging after 1 frame.\n      g_logGLCalls = false;\n    }\n    render();\n    return true;\n  }\n  initialize();\n}\n\nrequirejs(\n    [ '../tdl/buffers',\n      '../tdl/fast',\n      '../tdl/fps',\n      '../tdl/log',\n      '../tdl/math',\n      '../tdl/models',\n      '../tdl/primitives',\n      '../tdl/programs',\n      '../tdl/textures',\n      '../tdl/webgl',\n    ],\n    main);\n"
  },
  {
    "path": "example/example.html",
    "content": "<!--\r\n * Copyright 2009, Google Inc.\r\n * All rights reserved.\r\n *\r\n * Redistribution and use in source and binary forms, with or without\r\n * modification, are permitted provided that the following conditions are\r\n * met:\r\n *\r\n *     * Redistributions of source code must retain the above copyright\r\n * notice, this list of conditions and the following disclaimer.\r\n *     * Redistributions in binary form must reproduce the above\r\n * copyright notice, this list of conditions and the following disclaimer\r\n * in the documentation and/or other materials provided with the\r\n * distribution.\r\n *     * Neither the name of Google Inc. nor the names of its\r\n * contributors may be used to endorse or promote products derived from\r\n * this software without specific prior written permission.\r\n *\r\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n-->\r\n<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<meta charset=\"utf-8\">\r\n<title>WebGL TDL Example</title>\r\n<style>\r\nhtml, body {\r\n  width: 100%;\r\n  height: 100%;\r\n  border: 0px;\r\n  padding: 0px;\r\n  margin: 0px;\r\n  background-color: red;\r\n  font-family: sans-serif;\r\n  overflow: hidden;\r\n  color: #fff;\r\n}\r\na {\r\n  color: #fff;\r\n}\r\n#info {\r\n    font-size: small;\r\n    position: absolute;\r\n\ttop: 0px; width: 100%;\r\n\tpadding: 5px;\r\n\ttext-align: center;\r\n\tz-index: 2;\r\n}\r\nCANVAS {\r\n  background-color: gray;\r\n}\r\n.fpsContainer {\r\n  position: absolute;\r\n  top: 10px;\r\n  left: 10px;\r\n  z-index: 2;\r\n  color: white;\r\n  font-family: sans-serif;\r\n  background-color: rgba(0,0,0,0.5);\r\n  border-radius: 10px;\r\n  padding: 10px;\r\n}\r\n#viewContainer {\r\n  width: 100%;\r\n  height: 100%;\r\n}\r\n</style>\r\n<script type=\"text/javascript\" src=\"../tdl/base.js\"></script>\r\n<script type=\"text/javascript\">\r\ntdl.require('tdl.buffers');\r\ntdl.require('tdl.fast');\r\ntdl.require('tdl.fps');\r\ntdl.require('tdl.log');\r\ntdl.require('tdl.math');\r\ntdl.require('tdl.models');\r\ntdl.require('tdl.primitives');\r\ntdl.require('tdl.programs');\r\ntdl.require('tdl.textures');\r\ntdl.require('tdl.webgl');\r\nwindow.onload = initialize;\r\n\r\n// globals\r\nvar gl;                   // the gl context.\r\nvar canvas;               // the canvas\r\nvar math;                 // the math lib.\r\nvar fast;                 // the fast math lib.\r\nvar g_fpsTimer;           // object to measure frames per second;\r\nvar g_logGLCalls = true;  // whether or not to log webgl calls\r\nvar g_debug = false;      // whether or not to debug.\r\nvar g_drawOnce = false;   // draw just one frame.\r\n\r\n//g_drawOnce = true;\r\n//g_debug = true;\r\n\r\nvar g_eyeSpeed          = 0.5;\r\nvar g_eyeHeight         = 2;\r\nvar g_eyeRadius         = 9;\r\n\r\nfunction ValidateNoneOfTheArgsAreUndefined(functionName, args) {\r\n  for (var ii = 0; ii < args.length; ++ii) {\r\n    if (args[ii] === undefined) {\r\n      tdl.error(\"undefined passed to gl.\" + functionName + \"(\" +\r\n                tdl.webgl.glFunctionArgsToString(functionName, args) + \")\");\r\n    }\r\n  }\r\n}\r\n\r\nfunction Log(msg) {\r\n  if (g_logGLCalls) {\r\n    tdl.log(msg);\r\n  }\r\n}\r\n\r\nfunction LogGLCall(functionName, args) {\r\n  if (g_logGLCalls) {\r\n    ValidateNoneOfTheArgsAreUndefined(functionName, args)\r\n    tdl.log(\"gl.\" + functionName + \"(\" +\r\n                tdl.webgl.glFunctionArgsToString(functionName, args) + \")\");\r\n  }\r\n}\r\n\r\nfunction createProgramFromTags(vertexTagId, fragmentTagId) {\r\n  return tdl.programs.loadProgram(\r\n      document.getElementById(vertexTagId).text,\r\n      document.getElementById(fragmentTagId).text);\r\n}\r\n\r\n/**\r\n * Sets up Planet.\r\n */\r\nfunction setupSphere() {\r\n  var textures = {\r\n    diffuseSampler: tdl.textures.loadTexture('assets/sometexture.png')};\r\n  var program = createProgramFromTags(\r\n      'sphereVertexShader',\r\n      'sphereFragmentShader');\r\n  var arrays = tdl.primitives.createSphere(0.4, 10, 12);\r\n\r\n  return new tdl.models.Model(program, arrays, textures);\r\n}\r\n\r\nfunction initialize() {\r\n  math = tdl.math;\r\n  fast = tdl.fast;\r\n  canvas = document.getElementById(\"canvas\");\r\n  g_fpsTimer = new tdl.fps.FPSTimer();\r\n\r\n  gl = tdl.webgl.setupWebGL(canvas);\r\n  if (!gl) {\r\n    return false;\r\n  }\r\n  if (g_debug) {\r\n    gl = tdl.webgl.makeDebugContext(gl, undefined, LogGLCall);\r\n  }\r\n\r\n  Log(\"--Setup Sphere---------------------------------------\");\r\n  var sphere = setupSphere();\r\n\r\n  var then = 0.0;\r\n  var clock = 0.0;\r\n  var fpsElem = document.getElementById(\"fps\");\r\n\r\n  // pre-allocate a bunch of arrays\r\n  var projection = new Float32Array(16);\r\n  var view = new Float32Array(16);\r\n  var world = new Float32Array(16);\r\n  var worldInverse = new Float32Array(16);\r\n  var worldInverseTranspose = new Float32Array(16);\r\n  var viewProjection = new Float32Array(16);\r\n  var worldViewProjection = new Float32Array(16);\r\n  var viewInverse = new Float32Array(16);\r\n  var viewProjectionInverse = new Float32Array(16);\r\n  var eyePosition = new Float32Array(3);\r\n  var target = new Float32Array(3);\r\n  var up = new Float32Array([0,1,0]);\r\n  var lightWorldPos = new Float32Array(3);\r\n  var v3t0 = new Float32Array(3);\r\n  var v3t1 = new Float32Array(3);\r\n  var v3t2 = new Float32Array(3);\r\n  var v3t3 = new Float32Array(3);\r\n  var m4t0 = new Float32Array(16);\r\n  var m4t1 = new Float32Array(16);\r\n  var m4t2 = new Float32Array(16);\r\n  var m4t3 = new Float32Array(16);\r\n  var zero4 = new Float32Array(4);\r\n  var one4 = new Float32Array([1,1,1,1]);\r\n\r\n  // Sphere uniforms.\r\n  var sphereConst = {\r\n    viewInverse: viewInverse,\r\n    lightWorldPos: lightWorldPos,\r\n    specular: one4,\r\n    shininess: 50,\r\n    specularFactor: 0.2};\r\n  var spherePer = {\r\n    lightColor: new Float32Array([0,0,0,1]),\r\n    world: world,\r\n    worldViewProjection: worldViewProjection,\r\n    worldInverse: worldInverse,\r\n    worldInverseTranspose: worldInverseTranspose};\r\n\r\n  var frameCount = 0;\r\n  function render() {\r\n    ++frameCount;\r\n    if (!g_drawOnce) {\r\n      tdl.webgl.requestAnimationFrame(render, canvas);\r\n    }\r\n    var now = (new Date()).getTime() * 0.001;\r\n    var elapsedTime;\r\n    if(then == 0.0) {\r\n      elapsedTime = 0.0;\r\n    } else {\r\n      elapsedTime = now - then;\r\n    }\r\n    then = now;\r\n\r\n    g_fpsTimer.update(elapsedTime);\r\n    fpsElem.innerHTML = g_fpsTimer.averageFPS;\r\n\r\n    clock += elapsedTime;\r\n    eyePosition[0] = Math.sin(clock * g_eyeSpeed) * g_eyeRadius;\r\n    eyePosition[1] = g_eyeHeight;\r\n    eyePosition[2] = Math.cos(clock * g_eyeSpeed) * g_eyeRadius;\r\n\r\n    gl.colorMask(true, true, true, true);\r\n    gl.depthMask(true);\r\n    gl.clearColor(0,0,0,0);\r\n    gl.clearDepth(1);\r\n    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);\r\n\r\n    gl.enable(gl.CULL_FACE);\r\n    gl.enable(gl.DEPTH_TEST);\r\n\r\n    fast.matrix4.perspective(\r\n        projection,\r\n        math.degToRad(60),\r\n        canvas.clientWidth / canvas.clientHeight,\r\n        1,\r\n        5000);\r\n    fast.matrix4.lookAt(\r\n        view,\r\n        eyePosition,\r\n        target,\r\n        up);\r\n    fast.matrix4.mul(viewProjection, view, projection);\r\n    fast.matrix4.inverse(viewInverse, view);\r\n    fast.matrix4.inverse(viewProjectionInverse, viewProjection);\r\n\r\n    fast.matrix4.getAxis(v3t0, viewInverse, 0); // x\r\n    fast.matrix4.getAxis(v3t1, viewInverse, 1); // y;\r\n    fast.matrix4.getAxis(v3t2, viewInverse, 2); // z;\r\n    fast.mulScalarVector(v3t0, 10, v3t0);\r\n    fast.mulScalarVector(v3t1, 10, v3t1);\r\n    fast.mulScalarVector(v3t2, 10, v3t2);\r\n    fast.addVector(lightWorldPos, eyePosition, v3t0);\r\n    fast.addVector(lightWorldPos, lightWorldPos, v3t1);\r\n    fast.addVector(lightWorldPos, lightWorldPos, v3t2);\r\n\r\n//      view: view,\r\n//      projection: projection,\r\n//      viewProjection: viewProjection,\r\n\r\n    Log(\"--Draw sphere---------------------------------------\");\r\n    sphere.drawPrep(sphereConst);\r\n    var across = 6;\r\n    var lightColor = spherePer.lightColor;\r\n    var half = (across - 1) * 0.5;\r\n    for (var xx = 0; xx < across; ++xx) {\r\n      for (var yy = 0; yy < across; ++yy) {\r\n        for (var zz = 0; zz < across; ++zz) {\r\n          lightColor[0] = xx / across;\r\n          lightColor[1] = yy / across;\r\n          lightColor[2] = zz / across;\r\n          var scale = (xx + yy + zz) % 4 / 4 + 0.5;\r\n          fast.matrix4.scaling(m4t0, [scale, scale, scale]);\r\n          fast.matrix4.translation(m4t1, [xx - half, yy - half, zz - half]);\r\n          fast.matrix4.mul(world, m4t0, m4t1);\r\n          fast.matrix4.mul(worldViewProjection, world, viewProjection);\r\n          fast.matrix4.inverse(worldInverse, world);\r\n          fast.matrix4.transpose(worldInverseTranspose, worldInverse);\r\n          sphere.draw(spherePer);\r\n        }\r\n      }\r\n    }\r\n\r\n    // Set the alpha to 255.\r\n    gl.colorMask(false, false, false, true);\r\n    gl.clearColor(0,0,0,1);\r\n    gl.clear(gl.COLOR_BUFFER_BIT);\r\n\r\n    // turn off logging after 1 frame.\r\n    g_logGLCalls = false;\r\n  }\r\n  render();\r\n  return true;\r\n}\r\n</script>\r\n</head>\r\n<body>\r\n<div id=\"info\"><a href=\"http://threedlibrary.googlecode.com\" target=\"_blank\">tdl.js</a> - example</div>\r\n<div class=\"fpsContainer\">\r\n  <div class=\"fps\">fps: <span id=\"fps\"></span></div>\r\n</div>\r\n<div id=\"viewContainer\">\r\n<canvas id=\"canvas\" width=\"1024\" height=\"1024\" style=\"width: 100%; height: 100%;\"></canvas>\r\n</div>\r\n</body>\r\n<script id=\"sphereVertexShader\" type=\"text/something-not-javascript\">\r\nuniform mat4 worldViewProjection;\r\nuniform vec3 lightWorldPos;\r\nuniform mat4 world;\r\nuniform mat4 viewInverse;\r\nuniform mat4 worldInverseTranspose;\r\nattribute vec4 position;\r\nattribute vec3 normal;\r\nattribute vec2 texCoord;\r\nvarying vec4 v_position;\r\nvarying vec2 v_texCoord;\r\nvarying vec3 v_normal;\r\nvarying vec3 v_surfaceToLight;\r\nvarying vec3 v_surfaceToView;\r\nvoid main() {\r\n  v_texCoord = texCoord;\r\n  v_position = (worldViewProjection * position);\r\n  v_normal = (worldInverseTranspose * vec4(normal, 0)).xyz;\r\n  v_surfaceToLight = lightWorldPos - (world * position).xyz;\r\n  v_surfaceToView = (viewInverse[3] - (world * position)).xyz;\r\n  gl_Position = v_position;\r\n}\r\n\r\n</script>\r\n<script id=\"sphereFragmentShader\" type=\"text/something-not-javascript\">\r\nprecision mediump float;\r\nuniform vec4 lightColor;\r\nvarying vec4 v_position;\r\nvarying vec2 v_texCoord;\r\nvarying vec3 v_normal;\r\nvarying vec3 v_surfaceToLight;\r\nvarying vec3 v_surfaceToView;\r\n\r\nuniform sampler2D diffuseSampler;\r\nuniform vec4 specular;\r\nuniform sampler2D bumpSampler;\r\nuniform float shininess;\r\nuniform float specularFactor;\r\n\r\nvec4 lit(float l ,float h, float m) {\r\n  return vec4(1.0,\r\n              max(l, 0.0),\r\n              (l > 0.0) ? pow(max(0.0, h), m) : 0.0,\r\n              1.0);\r\n}\r\nvoid main() {\r\n  vec4 diffuse = texture2D(diffuseSampler, v_texCoord);\r\n  vec3 normal = normalize(v_normal);\r\n  vec3 surfaceToLight = normalize(v_surfaceToLight);\r\n  vec3 surfaceToView = normalize(v_surfaceToView);\r\n  vec3 halfVector = normalize(surfaceToLight + surfaceToView);\r\n  vec4 litR = lit(dot(normal, surfaceToLight),\r\n                    dot(normal, halfVector), shininess);\r\n  gl_FragColor = vec4((\r\n  lightColor * (diffuse * litR.y\r\n                        + specular * litR.z * specularFactor)).rgb,\r\n      diffuse.a);\r\n}\r\n</script>\r\n</html>\r\n\r\n\r\n"
  },
  {
    "path": "example/example2.html",
    "content": "<!--\r\n * Copyright 2009, Google Inc.\r\n * All rights reserved.\r\n *\r\n * Redistribution and use in source and binary forms, with or without\r\n * modification, are permitted provided that the following conditions are\r\n * met:\r\n *\r\n *     * Redistributions of source code must retain the above copyright\r\n * notice, this list of conditions and the following disclaimer.\r\n *     * Redistributions in binary form must reproduce the above\r\n * copyright notice, this list of conditions and the following disclaimer\r\n * in the documentation and/or other materials provided with the\r\n * distribution.\r\n *     * Neither the name of Google Inc. nor the names of its\r\n * contributors may be used to endorse or promote products derived from\r\n * this software without specific prior written permission.\r\n *\r\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n-->\r\n<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<meta charset=\"utf-8\">\r\n<title>WebGL TDL Example</title>\r\n<style>\r\nCANVAS {\r\n  background-color: gray;\r\n}\r\n.fpsContainer {\r\n  position: absolute;\r\n  top: 90px;\r\n  left: 20px;\r\n  z-index: 2;\r\n  color: white;\r\n  font-family: sans-serif;\r\n  background-color: rgba(0,0,0,0.5);\r\n  border-radius: 10px;\r\n  padding: 10px;\r\n}\r\n</style>\r\n<script type=\"text/javascript\" src=\"../tdl/base.js\"></script>\r\n<script type=\"text/javascript\">\r\ntdl.require('tdl.buffers');\r\ntdl.require('tdl.fast');\r\ntdl.require('tdl.fps');\r\ntdl.require('tdl.log');\r\ntdl.require('tdl.math');\r\ntdl.require('tdl.misc');\r\ntdl.require('tdl.models');\r\ntdl.require('tdl.primitives');\r\ntdl.require('tdl.programs');\r\ntdl.require('tdl.textures');\r\ntdl.require('tdl.webgl');\r\nwindow.onload = initialize;\r\n\r\nvar g = {\r\n across: 10,\r\n hdiv: 10,\r\n vdiv: 12\r\n};\r\n\r\n// globals\r\nvar gl;                   // the gl context.\r\nvar canvas;               // the canvas\r\nvar math;                 // the math lib.\r\nvar fast;                 // the fast math lib.\r\nvar g_fpsTimer;           // object to measure frames per second;\r\nvar g_logGLCalls = true;  // whether or not to log webgl calls\r\nvar g_debug = false;      // whether or not to debug.\r\nvar g_drawOnce = false;   // draw just one frame.\r\n\r\n//g_drawOnce = true;\r\n//g_debug = true;\r\n\r\nvar g_eyeSpeed          = 0.5;\r\nvar g_eyeHeight         = 2;\r\nvar g_eyeRadius         = 9;\r\n\r\nfunction ValidateNoneOfTheArgsAreUndefined(functionName, args) {\r\n  for (var ii = 0; ii < args.length; ++ii) {\r\n    if (args[ii] === undefined) {\r\n      tdl.error(\"undefined passed to gl.\" + functionName + \"(\" +\r\n                tdl.webgl.glFunctionArgsToString(functionName, args) + \")\");\r\n    }\r\n  }\r\n}\r\n\r\nfunction Log(msg) {\r\n  if (g_logGLCalls) {\r\n    tdl.log(msg);\r\n  }\r\n}\r\n\r\nfunction LogGLCall(functionName, args) {\r\n  if (g_logGLCalls) {\r\n    ValidateNoneOfTheArgsAreUndefined(functionName, args)\r\n    tdl.log(\"gl.\" + functionName + \"(\" +\r\n                tdl.webgl.glFunctionArgsToString(functionName, args) + \")\");\r\n  }\r\n}\r\n\r\nfunction createProgramFromTags(vertexTagId, fragmentTagId) {\r\n  return tdl.programs.loadProgram(\r\n      document.getElementById(vertexTagId).text,\r\n      document.getElementById(fragmentTagId).text);\r\n}\r\n\r\n/**\r\n * Sets up Planet.\r\n */\r\nfunction setupSphere() {\r\n  var textures = {};\r\n  var program = createProgramFromTags(\r\n      'sphereVertexShader',\r\n      'sphereFragmentShader');\r\n  var arrays = tdl.primitives.createSphere(0.4, g.hdiv, g.vdiv);\r\n\r\n  return new tdl.models.Model(program, arrays, textures);\r\n}\r\n\r\nfunction initialize() {\r\n  math = tdl.math;\r\n  fast = tdl.fast;\r\n  canvas = document.getElementById(\"canvas\");\r\n  g_fpsTimer = new tdl.fps.FPSTimer();\r\n\r\n  gl = tdl.webgl.setupWebGL(canvas);\r\n  if (!gl) {\r\n    return false;\r\n  }\r\n  if (g_debug) {\r\n    gl = tdl.webgl.makeDebugContext(gl, undefined, LogGLCall);\r\n  }\r\n\r\n  tdl.misc.applyUrlSettings(g);\r\n\r\n  Log(\"--Setup Sphere---------------------------------------\");\r\n  var sphere = setupSphere();\r\n\r\n  var then = 0.0;\r\n  var clock = 0.0;\r\n  var fpsElem = document.getElementById(\"fps\");\r\n\r\n  // pre-allocate a bunch of arrays\r\n  var projection = new Float32Array(16);\r\n  var view = new Float32Array(16);\r\n  var world = new Float32Array(16);\r\n  var worldInverse = new Float32Array(16);\r\n  var worldInverseTranspose = new Float32Array(16);\r\n  var viewProjection = new Float32Array(16);\r\n  var worldViewProjection = new Float32Array(16);\r\n  var viewInverse = new Float32Array(16);\r\n  var viewProjectionInverse = new Float32Array(16);\r\n  var eyePosition = new Float32Array(3);\r\n  var target = new Float32Array(3);\r\n  var up = new Float32Array([0,1,0]);\r\n  var lightWorldPos = new Float32Array(3);\r\n  var v3t0 = new Float32Array(3);\r\n  var v3t1 = new Float32Array(3);\r\n  var v3t2 = new Float32Array(3);\r\n  var v3t3 = new Float32Array(3);\r\n  var m4t0 = new Float32Array(16);\r\n  var m4t1 = new Float32Array(16);\r\n  var m4t2 = new Float32Array(16);\r\n  var m4t3 = new Float32Array(16);\r\n  var zero4 = new Float32Array(4);\r\n  var one4 = new Float32Array([1,1,1,1]);\r\n\r\n  // Sphere uniforms.\r\n  var sphereConst = {\r\n    viewInverse: viewInverse,\r\n    lightWorldPos: lightWorldPos,\r\n    specular: one4,\r\n    shininess: 50,\r\n    specularFactor: 0.2};\r\n  var spherePer = {\r\n    lightColor: new Float32Array([0,0,0,1]),\r\n    world: world,\r\n    worldViewProjection: worldViewProjection,\r\n    worldInverse: worldInverse,\r\n    worldInverseTranspose: worldInverseTranspose};\r\n\r\n  var frameCount = 0;\r\n  function render() {\r\n    ++frameCount;\r\n    if (!g_drawOnce) {\r\n      tdl.webgl.requestAnimationFrame(render, canvas);\r\n    }\r\n    var now = (new Date()).getTime() * 0.001;\r\n    var elapsedTime;\r\n    if(then == 0.0) {\r\n      elapsedTime = 0.0;\r\n    } else {\r\n      elapsedTime = now - then;\r\n    }\r\n    then = now;\r\n\r\n    g_fpsTimer.update(elapsedTime);\r\n    fpsElem.innerHTML = g_fpsTimer.averageFPS;\r\n\r\n    clock += elapsedTime;\r\n    eyePosition[0] = Math.sin(clock * g_eyeSpeed) * g_eyeRadius;\r\n    eyePosition[1] = g_eyeHeight;\r\n    eyePosition[2] = Math.cos(clock * g_eyeSpeed) * g_eyeRadius;\r\n\r\n    gl.colorMask(true, true, true, true);\r\n    gl.depthMask(true);\r\n    gl.clearColor(0.5,0.5,0.5,1);\r\n    gl.clearDepth(1);\r\n    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);\r\n\r\n    gl.enable(gl.CULL_FACE);\r\n    gl.enable(gl.DEPTH_TEST);\r\n\r\n    fast.matrix4.perspective(\r\n        projection,\r\n        math.degToRad(60),\r\n        canvas.clientWidth / canvas.clientHeight,\r\n        1,\r\n        5000);\r\n    fast.matrix4.lookAt(\r\n        view,\r\n        eyePosition,\r\n        target,\r\n        up);\r\n    fast.matrix4.mul(viewProjection, view, projection);\r\n    fast.matrix4.inverse(viewInverse, view);\r\n    fast.matrix4.inverse(viewProjectionInverse, viewProjection);\r\n\r\n    fast.matrix4.getAxis(v3t0, viewInverse, 0); // x\r\n    fast.matrix4.getAxis(v3t1, viewInverse, 1); // y;\r\n    fast.matrix4.getAxis(v3t2, viewInverse, 2); // z;\r\n    fast.mulScalarVector(v3t0, 10, v3t0);\r\n    fast.mulScalarVector(v3t1, 10, v3t1);\r\n    fast.mulScalarVector(v3t2, 10, v3t2);\r\n    fast.addVector(lightWorldPos, eyePosition, v3t0);\r\n    fast.addVector(lightWorldPos, lightWorldPos, v3t1);\r\n    fast.addVector(lightWorldPos, lightWorldPos, v3t2);\r\n\r\n//      view: view,\r\n//      projection: projection,\r\n//      viewProjection: viewProjection,\r\n\r\n    Log(\"--Draw sphere---------------------------------------\");\r\n    sphere.drawPrep(sphereConst);\r\n    var across = g.across;\r\n    var lightColor = spherePer.lightColor;\r\n    var half = (across - 1) * 0.5;\r\n    for (var xx = 0; xx < across; ++xx) {\r\n      for (var yy = 0; yy < across; ++yy) {\r\n        for (var zz = 0; zz < across; ++zz) {\r\n          lightColor[0] = xx / across;\r\n          lightColor[1] = yy / across;\r\n          lightColor[2] = zz / across;\r\n          var scale = (xx + yy + zz) % 4 / 4 + 0.5;\r\n          fast.matrix4.scaling(m4t0, [scale, scale, scale]);\r\n          fast.matrix4.translation(m4t1, [xx - half, yy - half, zz - half]);\r\n          fast.matrix4.mul(world, m4t0, m4t1);\r\n          fast.matrix4.translation(world, [xx - half, yy - half, zz - half]);\r\n          fast.matrix4.mul(worldViewProjection, world, viewProjection);\r\n          fast.matrix4.inverse(worldInverse, world);\r\n          fast.matrix4.transpose(worldInverseTranspose, worldInverse);\r\n          sphere.draw(spherePer);\r\n        }\r\n      }\r\n    }\r\n\r\n    // turn off logging after 1 frame.\r\n    g_logGLCalls = false;\r\n  }\r\n  render();\r\n  return true;\r\n}\r\n</script>\r\n</head>\r\n<body>\r\n<h1>WebGL Spheres</h1>\r\n<div>\r\n<div class=\"fpsContainer\">\r\n  <div class=\"fps\">fps: <span id=\"fps\"></span></div>\r\n</div>\r\n<div id=\"viewContainer\">\r\n<canvas id=\"canvas\" width=\"800\" height=\"600\"></canvas>\r\n</div>\r\n</div>\r\n</body>\r\n<script id=\"sphereVertexShader\" type=\"text/something-not-javascript\">\r\nuniform mat4 worldViewProjection;\r\nuniform vec3 lightWorldPos;\r\nuniform mat4 world;\r\nuniform mat4 viewInverse;\r\nuniform mat4 worldInverseTranspose;\r\nattribute vec4 position;\r\nattribute vec3 normal;\r\nattribute vec2 texCoord;\r\nvarying vec4 v_position;\r\nvarying vec2 v_texCoord;\r\nvarying vec3 v_normal;\r\nvarying vec3 v_surfaceToLight;\r\nvarying vec3 v_surfaceToView;\r\nvoid main() {\r\n  v_texCoord = texCoord;\r\n  v_position = (worldViewProjection * position);\r\n  v_normal = (worldInverseTranspose * vec4(normal, 0)).xyz;\r\n  v_surfaceToLight = lightWorldPos - (world * position).xyz;\r\n  v_surfaceToView = (viewInverse[3] - (world * position)).xyz;\r\n  gl_Position = v_position;\r\n}\r\n\r\n</script>\r\n<script id=\"sphereFragmentShader\" type=\"text/something-not-javascript\">\r\nprecision mediump float;\r\nuniform vec4 lightColor;\r\nvarying vec4 v_position;\r\nvarying vec2 v_texCoord;\r\nvarying vec3 v_normal;\r\nvarying vec3 v_surfaceToLight;\r\nvarying vec3 v_surfaceToView;\r\n\r\nuniform vec4 specular;\r\nuniform sampler2D bumpSampler;\r\nuniform float shininess;\r\nuniform float specularFactor;\r\n\r\nvec4 lit(float l ,float h, float m) {\r\n  return vec4(1.0,\r\n              max(l, 0.0),\r\n              (l > 0.0) ? pow(max(0.0, h), m) : 0.0,\r\n              1.0);\r\n}\r\nvoid main() {\r\n  vec4 diffuse = vec4(1,1,1,1);\r\n  vec3 normal = normalize(v_normal);\r\n  vec3 surfaceToLight = normalize(v_surfaceToLight);\r\n  vec3 surfaceToView = normalize(v_surfaceToView);\r\n  vec3 halfVector = normalize(surfaceToLight + surfaceToView);\r\n  vec4 litR = lit(dot(normal, surfaceToLight),\r\n                    dot(normal, halfVector), shininess);\r\n  gl_FragColor = vec4((\r\n  lightColor * (diffuse * litR.y\r\n                        + specular * litR.z * specularFactor)).rgb,\r\n      diffuse.a);\r\n}\r\n</script>\r\n</html>\r\n\r\n\r\n"
  },
  {
    "path": "example/line.html",
    "content": "<!--\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n-->\n<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<title>WebGL TDL Example</title>\n<style>\nhtml, body {\n  width: 100%;\n  height: 100%;\n  border: 0px;\n  padding: 0px;\n  margin: 0px;\n  background-color: red;\n  font-family: sans-serif;\n  overflow: hidden;\n  color: #fff;\n}\na {\n  color: #fff;\n}\n#info {\n    font-size: small;\n    position: absolute;\n    top: 0px; width: 100%;\n    padding: 5px;\n    text-align: center;\n    z-index: 2;\n}\nCANVAS {\n  background-color: gray;\n}\n.fpsContainer {\n  position: absolute;\n  top: 10px;\n  left: 10px;\n  z-index: 2;\n  color: white;\n  font-family: sans-serif;\n  background-color: rgba(0,0,0,0.5);\n  border-radius: 10px;\n  padding: 10px;\n}\n#viewContainer {\n  width: 100%;\n  height: 100%;\n}\n</style>\n<script type=\"text/javascript\" src=\"../tdl/base.js\"></script>\n<script type=\"text/javascript\">\ntdl.require('tdl.buffers');\ntdl.require('tdl.fast');\ntdl.require('tdl.fps');\ntdl.require('tdl.log');\ntdl.require('tdl.math');\ntdl.require('tdl.models');\ntdl.require('tdl.primitives');\ntdl.require('tdl.programs');\ntdl.require('tdl.webgl');\nwindow.onload = initialize;\n\n// globals\nvar gl;                   // the gl context.\nvar canvas;               // the canvas\nvar math;                 // the math lib.\nvar fast;                 // the fast math lib.\nvar g_fpsTimer;           // object to measure frames per second;\nvar g_logGLCalls = true;  // whether or not to log webgl calls\nvar g_debug = false;      // whether or not to debug.\nvar g_drawOnce = false;   // draw just one frame.\n\n//g_drawOnce = true;\n//g_debug = true;\n\nvar g_eyeSpeed          = 0.5;\nvar g_eyeHeight         = 2;\nvar g_eyeRadius         = 9;\n\nfunction ValidateNoneOfTheArgsAreUndefined(functionName, args) {\n  for (var ii = 0; ii < args.length; ++ii) {\n    if (args[ii] === undefined) {\n      tdl.error(\"undefined passed to gl.\" + functionName + \"(\" +\n                tdl.webgl.glFunctionArgsToString(functionName, args) + \")\");\n    }\n  }\n}\n\nfunction Log(msg) {\n  if (g_logGLCalls) {\n    tdl.log(msg);\n  }\n}\n\nfunction LogGLCall(functionName, args) {\n  if (g_logGLCalls) {\n    ValidateNoneOfTheArgsAreUndefined(functionName, args)\n    tdl.log(\"gl.\" + functionName + \"(\" +\n                tdl.webgl.glFunctionArgsToString(functionName, args) + \")\");\n  }\n}\n\nfunction createProgramFromTags(vertexTagId, fragmentTagId) {\n  return tdl.programs.loadProgram(\n      document.getElementById(vertexTagId).text,\n      document.getElementById(fragmentTagId).text);\n}\n\n/**\n * Sets up Line.\n */\nfunction setupLine() {\n \n  var program = createProgramFromTags(\n      'lineVertexShader',\n      'lineFragmentShader');\n  var arrays = tdl.primitives.createLine([\n    0.0,0.0,0.0,\n    0.0,0.0,0.5,\n    0.5,0.0,0.5,\n    0.5,0.0,0.0\n    ]);\n\n  return new tdl.models.Model(program, arrays, null, 3);\n}\n\nfunction initialize() {\n\n  math = tdl.math;\n  fast = tdl.fast;\n  canvas = document.getElementById(\"canvas\");\n  g_fpsTimer = new tdl.fps.FPSTimer();\n\n  gl = tdl.webgl.setupWebGL(canvas);\n  if (!gl) {\n    return false;\n  }\n  if (g_debug) {\n    gl = tdl.webgl.makeDebugContext(gl, undefined, LogGLCall);\n  }\n\n  Log(\"--Setup Line---------------------------------------\");\n  var line = setupLine();\n\n  var then = 0.0;\n  var clock = 0.0;\n  var fpsElem = document.getElementById(\"fps\");\n\n  // pre-allocate a bunch of arrays\n  var projection = new Float32Array(16);\n  var view = new Float32Array(16);\n  var world = new Float32Array(16);\n  var worldInverse = new Float32Array(16);\n  var worldInverseTranspose = new Float32Array(16);\n  var viewProjection = new Float32Array(16);\n  var worldViewProjection = new Float32Array(16);\n  var viewInverse = new Float32Array(16);\n  var viewProjectionInverse = new Float32Array(16);\n  var eyePosition = new Float32Array(3);\n  var target = new Float32Array(3);\n  var up = new Float32Array([0,1,0]);\n  var lightWorldPos = new Float32Array(3);\n  var v3t0 = new Float32Array(3);\n  var v3t1 = new Float32Array(3);\n  var v3t2 = new Float32Array(3);\n  var v3t3 = new Float32Array(3);\n  var m4t0 = new Float32Array(16);\n  var m4t1 = new Float32Array(16);\n  var m4t2 = new Float32Array(16);\n  var m4t3 = new Float32Array(16);\n  var zero4 = new Float32Array(4);\n  var one4 = new Float32Array([1,1,1,1]);\n\n  // Line uniforms.\n  var lineConst = {\n    viewInverse: viewInverse,\n    lightWorldPos: lightWorldPos,\n    specular: one4,\n    shininess: 50,\n    specularFactor: 0.2};\n  var linePer = {\n    lightColor: new Float32Array([0,0,0,1]),\n    world: world,\n    worldViewProjection: worldViewProjection,\n    worldInverse: worldInverse,\n    worldInverseTranspose: worldInverseTranspose};\n\n  var frameCount = 0;\n  function render() {\n    ++frameCount;\n    if (!g_drawOnce) {\n      tdl.webgl.requestAnimationFrame(render, canvas);\n    }\n    var now = (new Date()).getTime() * 0.001;\n    var elapsedTime;\n    if(then == 0.0) {\n      elapsedTime = 0.0;\n    } else {\n      elapsedTime = now - then;\n    }\n    then = now;\n\n    g_fpsTimer.update(elapsedTime);\n    fpsElem.innerHTML = g_fpsTimer.averageFPS;\n\n    clock += elapsedTime;\n    eyePosition[0] = Math.sin(clock * g_eyeSpeed) * g_eyeRadius;\n    eyePosition[1] = g_eyeHeight;\n    eyePosition[2] = Math.cos(clock * g_eyeSpeed) * g_eyeRadius;\n\n    gl.colorMask(true, true, true, true);\n    gl.depthMask(true);\n    gl.clearColor(0,0,0,0);\n    gl.clearDepth(1);\n    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);\n\n    gl.enable(gl.CULL_FACE);\n    gl.enable(gl.DEPTH_TEST);\n\n    fast.matrix4.perspective(\n        projection,\n        math.degToRad(60),\n        canvas.clientWidth / canvas.clientHeight,\n        1,\n        5000);\n    fast.matrix4.lookAt(\n        view,\n        eyePosition,\n        target,\n        up);\n    fast.matrix4.mul(viewProjection, view, projection);\n    fast.matrix4.inverse(viewInverse, view);\n    fast.matrix4.inverse(viewProjectionInverse, viewProjection);\n\n    fast.matrix4.getAxis(v3t0, viewInverse, 0); // x\n    fast.matrix4.getAxis(v3t1, viewInverse, 1); // y;\n    fast.matrix4.getAxis(v3t2, viewInverse, 2); // z;\n    fast.mulScalarVector(v3t0, 10, v3t0);\n    fast.mulScalarVector(v3t1, 10, v3t1);\n    fast.mulScalarVector(v3t2, 10, v3t2);\n    fast.addVector(lightWorldPos, eyePosition, v3t0);\n    fast.addVector(lightWorldPos, lightWorldPos, v3t1);\n    fast.addVector(lightWorldPos, lightWorldPos, v3t2);\n\n//      view: view,\n//      projection: projection,\n//      viewProjection: viewProjection,\n\n    Log(\"--Draw line---------------------------------------\");\n    gl.lineWidth(1.0);\n    line.drawPrep(lineConst);\n    var across = 5;\n    var lightColor = linePer.lightColor;\n    var half = (across - 1) * 0.5;\n    for (var xx = 0; xx < across; ++xx) {\n      for (var yy = 0; yy < across; ++yy) {\n        for (var zz = 0; zz < across; ++zz) {\n          lightColor[0] = xx / across;\n          lightColor[1] = yy / across;\n          lightColor[2] = zz / across;\n          var scale = (xx + yy + zz) % 4 / 4 + 0.5;\n          fast.matrix4.scaling(m4t0, [scale, scale, scale]);\n          fast.matrix4.translation(m4t1, [xx - half, yy - half, zz - half]);\n          fast.matrix4.mul(world, m4t0, m4t1);\n          fast.matrix4.mul(worldViewProjection, world, viewProjection);\n          fast.matrix4.inverse(worldInverse, world);\n          fast.matrix4.transpose(worldInverseTranspose, worldInverse);\n          line.draw(linePer);\n        }\n      }\n    }\n\n    // Set the alpha to 255.\n    gl.colorMask(false, false, false, true);\n    gl.clearColor(0,0,0,1);\n    gl.clear(gl.COLOR_BUFFER_BIT);\n\n    // turn off logging after 1 frame.\n    g_logGLCalls = false;\n  }\n  render();\n  return true;\n}\n</script>\n</head>\n<body>\n<div id=\"info\"><a href=\"http://threedlibrary.googlecode.com\" target=\"_blank\">tdl.js</a> - example</div>\n<div class=\"fpsContainer\">\n  <div class=\"fps\">fps: <span id=\"fps\"></span></div>\n</div>\n<div id=\"viewContainer\">\n<canvas id=\"canvas\" width=\"1024\" height=\"1024\" style=\"width: 100%; height: 100%;\"></canvas>\n</div>\n</body>\n<script id=\"lineVertexShader\" type=\"text/something-not-javascript\">\nuniform mat4 worldViewProjection;\nuniform vec3 lightWorldPos;\nuniform mat4 world;\nuniform mat4 viewInverse;\nuniform mat4 worldInverseTranspose;\nattribute vec4 position;\nattribute vec3 normal;\nvarying vec4 v_position;\nvarying vec3 v_normal;\nvarying vec3 v_surfaceToLight;\nvarying vec3 v_surfaceToView;\nvoid main() {\n  v_position = (worldViewProjection * position);\n  v_normal = (worldInverseTranspose * vec4(normal, 0)).xyz;\n  v_surfaceToLight = lightWorldPos - (world * position).xyz;\n  v_surfaceToView = (viewInverse[3] - (world * position)).xyz;\n  gl_Position = v_position;\n}\n\n</script>\n<script id=\"lineFragmentShader\" type=\"text/something-not-javascript\">\nprecision mediump float;\nuniform vec4 lightColor;\nvarying vec4 v_position;\nvarying vec3 v_normal;\nvarying vec3 v_surfaceToLight;\nvarying vec3 v_surfaceToView;\n\nuniform sampler2D diffuseSampler;\nuniform vec4 specular;\nuniform sampler2D bumpSampler;\nuniform float shininess;\nuniform float specularFactor;\n\nvec4 lit(float l ,float h, float m) {\n  return vec4(1.0,\n              max(l, 0.0),\n              (l > 0.0) ? pow(max(0.0, h), m) : 0.0,\n              1.0);\n}\nvoid main() {\n  vec3 normal = normalize(v_normal);\n  vec3 surfaceToLight = normalize(v_surfaceToLight);\n  vec3 surfaceToView = normalize(v_surfaceToView);\n  vec3 halfVector = normalize(surfaceToLight + surfaceToView);\n  vec4 litR = lit(dot(normal, surfaceToLight),\n                    dot(normal, halfVector), shininess);\n  gl_FragColor = vec4((\n  lightColor * (litR.y\n                        + specular * litR.z * specularFactor)).rgb,\n      1.0);\n}\n</script>\n</html>\n\n\n"
  },
  {
    "path": "example/picking.html",
    "content": "<!--\r\n * Copyright 2009, Google Inc.\r\n * All rights reserved.\r\n *\r\n * Redistribution and use in source and binary forms, with or without\r\n * modification, are permitted provided that the following conditions are\r\n * met:\r\n *\r\n *     * Redistributions of source code must retain the above copyright\r\n * notice, this list of conditions and the following disclaimer.\r\n *     * Redistributions in binary form must reproduce the above\r\n * copyright notice, this list of conditions and the following disclaimer\r\n * in the documentation and/or other materials provided with the\r\n * distribution.\r\n *     * Neither the name of Google Inc. nor the names of its\r\n * contributors may be used to endorse or promote products derived from\r\n * this software without specific prior written permission.\r\n *\r\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n-->\r\n<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<meta charset=\"utf-8\">\r\n<title>WebGL TDL Example</title>\r\n<style>\r\nhtml, body {\r\n  width: 100%;\r\n  height: 100%;\r\n  border: 0px;\r\n  padding: 0px;\r\n  margin: 0px;\r\n  background-color: red;\r\n  font-family: sans-serif;\r\n  overflow: hidden;\r\n  color: #fff;\r\n}\r\na {\r\n  color: #fff;\r\n}\r\n#info {\r\n    font-size: small;\r\n    position: absolute;\r\n\ttop: 0px; width: 100%;\r\n\tpadding: 5px;\r\n\ttext-align: center;\r\n\tz-index: 2;\r\n}\r\nCANVAS {\r\n  background-color: gray;\r\n}\r\n.fpsContainer {\r\n  position: absolute;\r\n  top: 10px;\r\n  left: 10px;\r\n  z-index: 2;\r\n  color: white;\r\n  font-family: sans-serif;\r\n  background-color: rgba(0,0,0,0.5);\r\n  border-radius: 10px;\r\n  padding: 10px;\r\n}\r\n#viewContainer {\r\n  width: 100%;\r\n  height: 100%;\r\n}\r\n</style>\r\n<script type=\"text/javascript\" src=\"../tdl/base.js\"></script>\r\n<script type=\"text/javascript\">\r\ntdl.require('tdl.buffers');\r\ntdl.require('tdl.fast');\r\ntdl.require('tdl.fps');\r\ntdl.require('tdl.framebuffers');\r\ntdl.require('tdl.log');\r\ntdl.require('tdl.math');\r\ntdl.require('tdl.models');\r\ntdl.require('tdl.primitives');\r\ntdl.require('tdl.programs');\r\ntdl.require('tdl.textures');\r\ntdl.require('tdl.webgl');\r\nwindow.onload = initialize;\r\n\r\n// globals\r\nvar gl;                   // the gl context.\r\nvar canvas;               // the canvas\r\nvar math;                 // the math lib.\r\nvar fast;                 // the fast math lib.\r\nvar g_fpsTimer;           // object to measure frames per second;\r\nvar g_logGLCalls = true;  // whether or not to log webgl calls\r\nvar g_debug = false;      // whether or not to debug.\r\nvar g_drawOnce = false;   // draw just one frame.\r\n\r\n//g_drawOnce = true;\r\n//g_debug = true;\r\n\r\nvar g_eyeSpeed          = 0.2;\r\nvar g_eyeHeight         = 2;\r\nvar g_eyeRadius         = 9;\r\n\r\nfunction ValidateNoneOfTheArgsAreUndefined(functionName, args) {\r\n  for (var ii = 0; ii < args.length; ++ii) {\r\n    if (args[ii] === undefined) {\r\n      tdl.error(\"undefined passed to gl.\" + functionName + \"(\" +\r\n                tdl.webgl.glFunctionArgsToString(functionName, args) + \")\");\r\n    }\r\n  }\r\n}\r\n\r\nfunction Log(msg) {\r\n  if (g_logGLCalls) {\r\n    tdl.log(msg);\r\n  }\r\n}\r\n\r\nfunction LogGLCall(functionName, args) {\r\n  if (g_logGLCalls) {\r\n    ValidateNoneOfTheArgsAreUndefined(functionName, args)\r\n    tdl.log(\"gl.\" + functionName + \"(\" +\r\n                tdl.webgl.glFunctionArgsToString(functionName, args) + \")\");\r\n  }\r\n}\r\n\r\nfunction createProgramFromTags(vertexTagId, fragmentTagId) {\r\n  return tdl.programs.loadProgram(\r\n      document.getElementById(vertexTagId).text,\r\n      document.getElementById(fragmentTagId).text);\r\n}\r\n\r\n/**\r\n * Sets up Planet.\r\n */\r\nfunction setupSphere() {\r\n  var textures = {\r\n    diffuseSampler: tdl.textures.loadTexture('assets/sometexture.png')};\r\n  var program = createProgramFromTags(\r\n      'sphereVertexShader',\r\n      'sphereFragmentShader');\r\n  var arrays = tdl.primitives.createSphere(0.4, 10, 12);\r\n\r\n  return new tdl.models.Model(program, arrays, textures);\r\n}\r\n\r\nfunction initialize() {\r\n  math = tdl.math;\r\n  fast = tdl.fast;\r\n  canvas = document.getElementById(\"canvas\");\r\n  g_fpsTimer = new tdl.fps.FPSTimer();\r\n\r\n  gl = tdl.webgl.setupWebGL(canvas);\r\n  if (!gl) {\r\n    return false;\r\n  }\r\n  if (g_debug) {\r\n    gl = tdl.webgl.makeDebugContext(gl, undefined, LogGLCall);\r\n  }\r\n\r\n  Log(\"--Setup Sphere---------------------------------------\");\r\n  var sphere = setupSphere();\r\n\r\n  var pickingProgram = createProgramFromTags(\r\n      'pickingVertexShader',\r\n      'pickingFragmentShader');\r\n\r\n  var pickingFramebuffer = new tdl.framebuffers.Framebuffer(gl.canvas.width, gl.canvas.height, true);\r\n  var backbuffer = tdl.framebuffers.getBackBuffer(gl.canvas);\r\n\r\n  var then = 0.0;\r\n  var clock = 0.0;\r\n  var fpsElem = document.getElementById(\"fps\");\r\n  var pickElem = document.getElementById(\"pick\");\r\n  var pickIndex = -1;\r\n\r\n  // pre-allocate a bunch of arrays\r\n  var projection = new Float32Array(16);\r\n  var view = new Float32Array(16);\r\n  var world = new Float32Array(16);\r\n  var worldInverse = new Float32Array(16);\r\n  var worldInverseTranspose = new Float32Array(16);\r\n  var viewProjection = new Float32Array(16);\r\n  var worldViewProjection = new Float32Array(16);\r\n  var viewInverse = new Float32Array(16);\r\n  var viewProjectionInverse = new Float32Array(16);\r\n  var eyePosition = new Float32Array(3);\r\n  var target = new Float32Array(3);\r\n  var up = new Float32Array([0,1,0]);\r\n  var lightWorldPos = new Float32Array(3);\r\n  var v3t0 = new Float32Array(3);\r\n  var v3t1 = new Float32Array(3);\r\n  var v3t2 = new Float32Array(3);\r\n  var v3t3 = new Float32Array(3);\r\n  var m4t0 = new Float32Array(16);\r\n  var m4t1 = new Float32Array(16);\r\n  var m4t2 = new Float32Array(16);\r\n  var m4t3 = new Float32Array(16);\r\n  var zero4 = new Float32Array(4);\r\n  var one4 = new Float32Array([1,1,1,1]);\r\n\r\n  // Sphere uniforms.\r\n  var sphereConst = {\r\n    viewInverse: viewInverse,\r\n    lightWorldPos: lightWorldPos,\r\n    specular: one4,\r\n    shininess: 50,\r\n    specularFactor: 0.2};\r\n  var spherePer = {\r\n    lightColor: new Float32Array([0,0,0,1]),\r\n    world: world,\r\n    worldViewProjection: worldViewProjection,\r\n    worldInverse: worldInverse,\r\n    worldInverseTranspose: worldInverseTranspose};\r\n  var pickingPer = {\r\n    u_color: new Float32Array(4),\r\n    worldViewProjection: worldViewProjection\r\n  };\r\n\r\n  var frameCount = 0;\r\n  function render() {\r\n    ++frameCount;\r\n    if (!g_drawOnce) {\r\n      tdl.webgl.requestAnimationFrame(render, canvas);\r\n    }\r\n    var now = (new Date()).getTime() * 0.001;\r\n    var elapsedTime;\r\n    if(then == 0.0) {\r\n      elapsedTime = 0.0;\r\n    } else {\r\n      elapsedTime = now - then;\r\n    }\r\n    then = now;\r\n\r\n    g_fpsTimer.update(elapsedTime);\r\n    fpsElem.innerHTML = g_fpsTimer.averageFPS;\r\n\r\n    clock += elapsedTime;\r\n    eyePosition[0] = Math.sin(clock * g_eyeSpeed) * g_eyeRadius;\r\n    eyePosition[1] = g_eyeHeight;\r\n    eyePosition[2] = Math.cos(clock * g_eyeSpeed) * g_eyeRadius;\r\n\r\n    gl.colorMask(true, true, true, true);\r\n    gl.depthMask(true);\r\n    gl.clearColor(0,0,0,0);\r\n    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);\r\n\r\n    gl.enable(gl.CULL_FACE);\r\n    gl.enable(gl.DEPTH_TEST);\r\n\r\n    drawScene(false);\r\n\r\n    // Set the alpha to 255.\r\n    gl.colorMask(false, false, false, true);\r\n    gl.clearColor(0,0,0,1);\r\n    gl.clear(gl.COLOR_BUFFER_BIT);\r\n\r\n    // turn off logging after 1 frame.\r\n    g_logGLCalls = false;\r\n  }\r\n\r\n  canvas.addEventListener('click', pick, false);\r\n\r\n  var pickColor = new Uint8Array(4);\r\n  function pick(event) {\r\n    var info = getEventInfo(event);\r\n    var m = getRelativeCoordinates(info);\r\n    // convert mouse coords to WebGL coords\r\n    var pickX = m.x * pickingFramebuffer.width / gl.canvas.clientWidth;\r\n    var pickY = pickingFramebuffer.height - (m.y * pickingFramebuffer.height / gl.canvas.clientHeight) - 1;\r\n\r\n    // Bind the picking framebuffer to draw offscreen\r\n    pickingFramebuffer.bind();\r\n\r\n    // clear to 1,1,1,1 (outside the range of objects)\r\n    gl.colorMask(true, true, true, true);\r\n    gl.depthMask(true);\r\n    gl.clearColor(1,1,1,1);\r\n    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);\r\n\r\n    gl.enable(gl.CULL_FACE);\r\n    gl.enable(gl.DEPTH_TEST);\r\n    gl.disable(gl.BLEND);\r\n\r\n    // Swap in the picking program\r\n    sphere.oldProgram = sphere.program;\r\n    sphere.program = pickingProgram;\r\n\r\n    // Draw the scene to the framebuffer\r\n    drawScene(true);\r\n\r\n    // Read the pixel under the mouse\r\n    gl.readPixels(pickX, pickY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pickColor);\r\n\r\n    // Compute which model by drawing order was picked.\r\n    pickIndex = pickColor[0] + pickColor[1] * 0x100 + pickColor[2] * 0x10000;\r\n    pickElem.innerHTML = pickIndex;\r\n\r\n    // Restore the old program\r\n    sphere.program = sphere.oldProgram;\r\n\r\n    // Restore the backbuffer so future drawing is visible again\r\n    backbuffer.bind();\r\n  }\r\n\r\n  function drawScene(picking) {\r\n    fast.matrix4.perspective(\r\n        projection,\r\n        math.degToRad(60),\r\n        canvas.clientWidth / canvas.clientHeight,\r\n        1,\r\n        5000);\r\n    fast.matrix4.lookAt(\r\n        view,\r\n        eyePosition,\r\n        target,\r\n        up);\r\n    fast.matrix4.mul(viewProjection, view, projection);\r\n    fast.matrix4.inverse(viewInverse, view);\r\n    fast.matrix4.inverse(viewProjectionInverse, viewProjection);\r\n\r\n    fast.matrix4.getAxis(v3t0, viewInverse, 0); // x\r\n    fast.matrix4.getAxis(v3t1, viewInverse, 1); // y;\r\n    fast.matrix4.getAxis(v3t2, viewInverse, 2); // z;\r\n    fast.mulScalarVector(v3t0, 10, v3t0);\r\n    fast.mulScalarVector(v3t1, 10, v3t1);\r\n    fast.mulScalarVector(v3t2, 10, v3t2);\r\n    fast.addVector(lightWorldPos, eyePosition, v3t0);\r\n    fast.addVector(lightWorldPos, lightWorldPos, v3t1);\r\n    fast.addVector(lightWorldPos, lightWorldPos, v3t2);\r\n\r\n  //      view: view,\r\n  //      projection: projection,\r\n  //      viewProjection: viewProjection,\r\n\r\n    Log(\"--Draw sphere---------------------------------------\");\r\n    sphere.drawPrep(sphereConst);\r\n    var modelCount = 0;\r\n    var across = 6;\r\n    var lightColor = spherePer.lightColor;\r\n    var half = (across - 1) * 0.5;\r\n    for (var xx = 0; xx < across; ++xx) {\r\n      for (var yy = 0; yy < across; ++yy) {\r\n        for (var zz = 0; zz < across; ++zz) {\r\n          lightColor[0] = xx / across;\r\n          lightColor[1] = yy / across;\r\n          lightColor[2] = zz / across;\r\n          var scale = (xx + yy + zz) % 4 / 4 + 0.5;\r\n          fast.matrix4.scaling(m4t0, [scale, scale, scale]);\r\n          fast.matrix4.translation(m4t1, [xx - half, yy - half, zz - half]);\r\n          fast.matrix4.mul(world, m4t0, m4t1);\r\n          fast.matrix4.mul(worldViewProjection, world, viewProjection);\r\n          if (picking) {\r\n            // convert model count to a color.\r\n            pickingPer.u_color[0] = ((modelCount >>  0) & 0xFF) / 255;\r\n            pickingPer.u_color[1] = ((modelCount >>  8) & 0xFF) / 255;\r\n            pickingPer.u_color[2] = ((modelCount >> 16) & 0xFF) / 255;\r\n            pickingPer.u_color[3] = ((modelCount >> 24) & 0xFF) / 255;\r\n            sphere.draw(pickingPer);\r\n          } else {\r\n            // highlight picked object.\r\n            if (pickIndex == modelCount && (Math.floor(clock * 4) & 1)) {\r\n              lightColor[0] = 255;\r\n              lightColor[1] = 255;\r\n              lightColor[2] = 255;\r\n            }\r\n            fast.matrix4.inverse(worldInverse, world);\r\n            fast.matrix4.transpose(worldInverseTranspose, worldInverse);\r\n            sphere.draw(spherePer);\r\n          }\r\n          ++modelCount;\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  render();\r\n  return true;\r\n}\r\n\r\n/**\r\n * Returns the absolute position of an element for certain browsers.\r\n * @param {HTML Element} element The element to get a position for.\r\n * @return {Object} An object containing x and y as the absolute position\r\n *     of the given element.\r\n */\r\nvar getAbsolutePosition = function(element) {\r\n  var r = { x: element.offsetLeft, y: element.offsetTop };\r\n  if (element.offsetParent) {\r\n    var tmp = getAbsolutePosition(element.offsetParent);\r\n    r.x += tmp.x;\r\n    r.y += tmp.y;\r\n  }\r\n  return r;\r\n};\r\n\r\n /**\r\n  * Retrieve the coordinates of the given event relative to the center\r\n  * of the widget.\r\n  *\r\n  * @param {eventInfo} eventInfo As returned from\r\n  *     CLIENT3DJS.util.getEventInfo.\r\n  * @param {HTML Element} opt_reference A DOM element whose position we want\r\n  *     to transform the mouse coordinates to. If it is not passed in the\r\n  *     element in the eventInfo will be used.\r\n  * @return {Object} An object containing keys 'x' and 'y'.\r\n  */\r\nvar getRelativeCoordinates = function(eventInfo, opt_reference) {\r\n  var x, y;\r\n  var event = eventInfo.event;\r\n  var element = eventInfo.element;\r\n  var reference = opt_reference || eventInfo.element;\r\n  if (!window.opera && typeof event.offsetX != 'undefined') {\r\n    // Use offset coordinates and find common offsetParent\r\n    var pos = { x: event.offsetX, y: event.offsetY };\r\n    // Send the coordinates upwards through the offsetParent chain.\r\n    var e = element;\r\n    while (e) {\r\n      e.mouseX = pos.x;\r\n      e.mouseY = pos.y;\r\n      pos.x += e.offsetLeft;\r\n      pos.y += e.offsetTop;\r\n      e = e.offsetParent;\r\n    }\r\n    // Look for the coordinates starting from the reference element.\r\n    var e = reference;\r\n    var offset = { x: 0, y: 0 }\r\n    while (e) {\r\n      if (typeof e.mouseX != 'undefined') {\r\n        x = e.mouseX - offset.x;\r\n        y = e.mouseY - offset.y;\r\n        break;\r\n      }\r\n      offset.x += e.offsetLeft;\r\n      offset.y += e.offsetTop;\r\n      e = e.offsetParent;\r\n    }\r\n    // Reset stored coordinates\r\n    e = element;\r\n    while (e) {\r\n      e.mouseX = undefined;\r\n      e.mouseY = undefined;\r\n      e = e.offsetParent;\r\n    }\r\n  } else {\r\n    // Use absolute coordinates\r\n    var pos = getAbsolutePosition(reference);\r\n    x = event.pageX - pos.x;\r\n    y = event.pageY - pos.y;\r\n  }\r\n  // Subtract distance to middle\r\n  return { x: x, y: y };\r\n};\r\n\r\nvar getEventInfo = function(event) {\r\n  event = event ? event : window.event;\r\n  var element = event.target ? event.target : event.srcElement;\r\n  return {\r\n    event: event,\r\n    element: element,\r\n    name: (element.id ? element.id : ('->' + element.toString())),\r\n    wheel: (event.detail ? event.detail : -event.wheelDelta),\r\n    shift: (event.modifiers ? (event.modifiers & Event.SHIFT_MASK) : event.shiftKey)\r\n  };\r\n};\r\n\r\n</script>\r\n</head>\r\n<body>\r\n<div id=\"info\"><a href=\"http://threedlibrary.googlecode.com\" target=\"_blank\">tdl.js</a> - example</div>\r\n<div class=\"fpsContainer\">\r\n  <div class=\"fps\">fps: <span id=\"fps\"></span></div>\r\n  <div>You picked: <span id=\"pick\"></span></div>\r\n</div>\r\n<div id=\"viewContainer\">\r\n<canvas id=\"canvas\" width=\"1024\" height=\"1024\" style=\"width: 100%; height: 100%;\"></canvas>\r\n</div>\r\n</body>\r\n<script id=\"sphereVertexShader\" type=\"text/something-not-javascript\">\r\nuniform mat4 worldViewProjection;\r\nuniform vec3 lightWorldPos;\r\nuniform mat4 world;\r\nuniform mat4 viewInverse;\r\nuniform mat4 worldInverseTranspose;\r\nattribute vec4 position;\r\nattribute vec3 normal;\r\nattribute vec2 texCoord;\r\nvarying vec4 v_position;\r\nvarying vec2 v_texCoord;\r\nvarying vec3 v_normal;\r\nvarying vec3 v_surfaceToLight;\r\nvarying vec3 v_surfaceToView;\r\nvoid main() {\r\n  v_texCoord = texCoord;\r\n  v_position = (worldViewProjection * position);\r\n  v_normal = (worldInverseTranspose * vec4(normal, 0)).xyz;\r\n  v_surfaceToLight = lightWorldPos - (world * position).xyz;\r\n  v_surfaceToView = (viewInverse[3] - (world * position)).xyz;\r\n  gl_Position = v_position;\r\n}\r\n\r\n</script>\r\n<script id=\"sphereFragmentShader\" type=\"text/something-not-javascript\">\r\nprecision mediump float;\r\nuniform vec4 lightColor;\r\nvarying vec4 v_position;\r\nvarying vec2 v_texCoord;\r\nvarying vec3 v_normal;\r\nvarying vec3 v_surfaceToLight;\r\nvarying vec3 v_surfaceToView;\r\n\r\nuniform sampler2D diffuseSampler;\r\nuniform vec4 specular;\r\nuniform sampler2D bumpSampler;\r\nuniform float shininess;\r\nuniform float specularFactor;\r\n\r\nvec4 lit(float l ,float h, float m) {\r\n  return vec4(1.0,\r\n              max(l, 0.0),\r\n              (l > 0.0) ? pow(max(0.0, h), m) : 0.0,\r\n              1.0);\r\n}\r\nvoid main() {\r\n  vec4 diffuse = texture2D(diffuseSampler, v_texCoord);\r\n  vec3 normal = normalize(v_normal);\r\n  vec3 surfaceToLight = normalize(v_surfaceToLight);\r\n  vec3 surfaceToView = normalize(v_surfaceToView);\r\n  vec3 halfVector = normalize(surfaceToLight + surfaceToView);\r\n  vec4 litR = lit(dot(normal, surfaceToLight),\r\n                    dot(normal, halfVector), shininess);\r\n  gl_FragColor = vec4((\r\n  lightColor * (diffuse * litR.y\r\n                        + specular * litR.z * specularFactor)).rgb,\r\n      diffuse.a);\r\n}\r\n</script>\r\n<script id=\"pickingVertexShader\" type=\"text/something-not-javascript\">\r\nuniform mat4 worldViewProjection;\r\nattribute vec4 position;\r\nvoid main() {\r\n  gl_Position = worldViewProjection * position;\r\n}\r\n\r\n</script>\r\n<script id=\"pickingFragmentShader\" type=\"text/something-not-javascript\">\r\nprecision mediump float;\r\n\r\nuniform vec4 u_color;\r\n\r\nvoid main() {\r\n  gl_FragColor = u_color;\r\n}\r\n</script>\r\n</html>\r\n\r\n\r\n"
  },
  {
    "path": "js/require.js",
    "content": "/*\n RequireJS 2.1.11 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.\n Available via the MIT or new BSD license.\n see: http://github.com/jrburke/requirejs for details\n*/\nvar requirejs,require,define;\n(function(ca){function G(b){return\"[object Function]\"===M.call(b)}function H(b){return\"[object Array]\"===M.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function U(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function s(b,c){return ga.call(b,c)}function j(b,c){return s(b,c)&&b[c]}function B(b,c){for(var d in b)if(s(b,d)&&c(b[d],d))break}function V(b,c,d,g){c&&B(c,function(c,h){if(d||!s(b,h))g&&\"object\"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof\nRegExp)?(b[h]||(b[h]={}),V(b[h],c,d,g)):b[h]=c});return b}function t(b,c){return function(){return c.apply(b,arguments)}}function da(b){throw b;}function ea(b){if(!b)return b;var c=ca;v(b.split(\".\"),function(b){c=c[b]});return c}function C(b,c,d,g){c=Error(c+\"\\nhttp://requirejs.org/docs/errors.html#\"+b);c.requireType=b;c.requireModules=g;d&&(c.originalError=d);return c}function ha(b){function c(a,e,b){var f,n,c,d,g,h,i,I=e&&e.split(\"/\");n=I;var m=l.map,k=m&&m[\"*\"];if(a&&\".\"===a.charAt(0))if(e){n=\nI.slice(0,I.length-1);a=a.split(\"/\");e=a.length-1;l.nodeIdCompat&&R.test(a[e])&&(a[e]=a[e].replace(R,\"\"));n=a=n.concat(a);d=n.length;for(e=0;e<d;e++)if(c=n[e],\".\"===c)n.splice(e,1),e-=1;else if(\"..\"===c)if(1===e&&(\"..\"===n[2]||\"..\"===n[0]))break;else 0<e&&(n.splice(e-1,2),e-=2);a=a.join(\"/\")}else 0===a.indexOf(\"./\")&&(a=a.substring(2));if(b&&m&&(I||k)){n=a.split(\"/\");e=n.length;a:for(;0<e;e-=1){d=n.slice(0,e).join(\"/\");if(I)for(c=I.length;0<c;c-=1)if(b=j(m,I.slice(0,c).join(\"/\")))if(b=j(b,d)){f=b;\ng=e;break a}!h&&(k&&j(k,d))&&(h=j(k,d),i=e)}!f&&h&&(f=h,g=i);f&&(n.splice(0,g,f),a=n.join(\"/\"))}return(f=j(l.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName(\"script\"),function(e){if(e.getAttribute(\"data-requiremodule\")===a&&e.getAttribute(\"data-requirecontext\")===i.contextName)return e.parentNode.removeChild(e),!0})}function g(a){var e=j(l.paths,a);if(e&&H(e)&&1<e.length)return e.shift(),i.require.undef(a),i.require([a]),!0}function u(a){var e,b=a?a.indexOf(\"!\"):-1;-1<b&&(e=a.substring(0,\nb),a=a.substring(b+1,a.length));return[e,a]}function m(a,e,b,f){var n,d,g=null,h=e?e.name:null,l=a,m=!0,k=\"\";a||(m=!1,a=\"_@r\"+(M+=1));a=u(a);g=a[0];a=a[1];g&&(g=c(g,h,f),d=j(p,g));a&&(g?k=d&&d.normalize?d.normalize(a,function(a){return c(a,h,f)}):c(a,h,f):(k=c(a,h,f),a=u(k),g=a[0],k=a[1],b=!0,n=i.nameToUrl(k)));b=g&&!d&&!b?\"_unnormalized\"+(Q+=1):\"\";return{prefix:g,name:k,parentMap:e,unnormalized:!!b,url:n,originalName:l,isDefine:m,id:(g?g+\"!\"+k:k)+b}}function q(a){var e=a.id,b=j(k,e);b||(b=k[e]=new i.Module(a));\nreturn b}function r(a,e,b){var f=a.id,n=j(k,f);if(s(p,f)&&(!n||n.defineEmitComplete))\"defined\"===e&&b(p[f]);else if(n=q(a),n.error&&\"error\"===e)b(n.error);else n.on(e,b)}function w(a,e){var b=a.requireModules,f=!1;if(e)e(a);else if(v(b,function(e){if(e=j(k,e))e.error=a,e.events.error&&(f=!0,e.emit(\"error\",a))}),!f)h.onError(a)}function x(){S.length&&(ia.apply(A,[A.length,0].concat(S)),S=[])}function y(a){delete k[a];delete W[a]}function F(a,e,b){var f=a.map.id;a.error?a.emit(\"error\",a.error):(e[f]=\n!0,v(a.depMaps,function(f,c){var d=f.id,g=j(k,d);g&&(!a.depMatched[c]&&!b[d])&&(j(e,d)?(a.defineDep(c,p[d]),a.check()):F(g,e,b))}),b[f]=!0)}function D(){var a,e,b=(a=1E3*l.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],c=[],h=!1,k=!0;if(!X){X=!0;B(W,function(a){var i=a.map,m=i.id;if(a.enabled&&(i.isDefine||c.push(a),!a.error))if(!a.inited&&b)g(m)?h=e=!0:(f.push(m),d(m));else if(!a.inited&&(a.fetched&&i.isDefine)&&(h=!0,!i.prefix))return k=!1});if(b&&f.length)return a=C(\"timeout\",\"Load timeout for modules: \"+\nf,null,f),a.contextName=i.contextName,w(a);k&&v(c,function(a){F(a,{},{})});if((!b||e)&&h)if((z||fa)&&!Y)Y=setTimeout(function(){Y=0;D()},50);X=!1}}function E(a){s(p,a[0])||q(m(a[0],null,!0)).init(a[1],a[2])}function K(a){var a=a.currentTarget||a.srcElement,e=i.onScriptLoad;a.detachEvent&&!Z?a.detachEvent(\"onreadystatechange\",e):a.removeEventListener(\"load\",e,!1);e=i.onScriptError;(!a.detachEvent||Z)&&a.removeEventListener(\"error\",e,!1);return{node:a,id:a&&a.getAttribute(\"data-requiremodule\")}}function L(){var a;\nfor(x();A.length;){a=A.shift();if(null===a[0])return w(C(\"mismatch\",\"Mismatched anonymous define() module: \"+a[a.length-1]));E(a)}}var X,$,i,N,Y,l={waitSeconds:7,baseUrl:\"./\",paths:{},bundles:{},pkgs:{},shim:{},config:{}},k={},W={},aa={},A=[],p={},T={},ba={},M=1,Q=1;N={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?p[a.map.id]=a.exports:a.exports=p[a.map.id]={}},module:function(a){return a.module?\na.module:a.module={id:a.map.id,uri:a.map.url,config:function(){return j(l.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};$=function(a){this.events=j(aa,a.id)||{};this.map=a;this.shim=j(l.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};$.prototype={init:function(a,e,b,f){f=f||{};if(!this.inited){this.factory=e;if(b)this.on(\"error\",b);else this.events.error&&(b=t(this,function(a){this.emit(\"error\",a)}));this.depMaps=a&&a.slice(0);this.errback=\nb;this.inited=!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,e){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=e)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],t(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=\nthis.map.url;T[a]||(T[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,e,b=this.map.id;e=this.depExports;var f=this.exports,c=this.factory;if(this.inited)if(this.error)this.emit(\"error\",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(c)){if(this.events.error&&this.map.isDefine||h.onError!==da)try{f=i.execCb(b,c,e,f)}catch(d){a=d}else f=i.execCb(b,c,e,f);this.map.isDefine&&void 0===f&&((e=this.module)?f=e.exports:this.usingExports&&\n(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?\"define\":\"require\",w(this.error=a)}else f=c;this.exports=f;if(this.map.isDefine&&!this.ignore&&(p[b]=f,h.onResourceLoad))h.onResourceLoad(i,this.map,this.depMaps);y(b);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit(\"defined\",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=\nthis.map,b=a.id,d=m(a.prefix);this.depMaps.push(d);r(d,\"defined\",t(this,function(f){var d,g;g=j(ba,this.map.id);var J=this.map.name,u=this.map.parentMap?this.map.parentMap.name:null,p=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(J=f.normalize(J,function(a){return c(a,u,!0)})||\"\"),f=m(a.prefix+\"!\"+J,this.map.parentMap),r(f,\"defined\",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),g=j(k,f.id)){this.depMaps.push(f);\nif(this.events.error)g.on(\"error\",t(this,function(a){this.emit(\"error\",a)}));g.enable()}}else g?(this.map.url=i.nameToUrl(g),this.load()):(d=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),d.error=t(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(k,function(a){0===a.map.id.indexOf(b+\"_unnormalized\")&&y(a.map.id)});w(a)}),d.fromText=t(this,function(f,c){var g=a.name,J=m(g),k=O;c&&(f=c);k&&(O=!1);q(J);s(l.config,b)&&(l.config[g]=l.config[b]);try{h.exec(f)}catch(j){return w(C(\"fromtexteval\",\n\"fromText eval for \"+b+\" failed: \"+j,j,[b]))}k&&(O=!0);this.depMaps.push(J);i.completeLoad(g);p([g],d)}),f.load(a.name,p,d,l))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){W[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,t(this,function(a,b){var c,f;if(\"string\"===typeof a){a=m(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=j(N,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;r(a,\"defined\",t(this,function(a){this.defineDep(b,\na);this.check()}));this.errback&&r(a,\"error\",t(this,this.errback))}c=a.id;f=k[c];!s(N,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,t(this,function(a){var b=j(k,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});\"error\"===a&&delete this.events[a]}};i={config:l,contextName:b,registry:k,defined:p,urlFetched:T,defQueue:A,Module:$,makeModuleMap:m,\nnextTick:h.nextTick,onError:w,configure:function(a){a.baseUrl&&\"/\"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+=\"/\");var b=l.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(l[b]||(l[b]={}),V(l[b],a,!0,!0)):l[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(ba[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),l.shim=b);a.packages&&v(a.packages,function(a){var b,\na=\"string\"===typeof a?{name:a}:a;b=a.name;a.location&&(l.paths[b]=a.location);l.pkgs[b]=a.name+\"/\"+(a.main||\"main\").replace(ja,\"\").replace(R,\"\")});B(k,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=m(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ca,arguments));return b||a.exports&&ea(a.exports)}},makeRequire:function(a,e){function g(f,c,d){var j,l;e.enableBuildCallback&&(c&&G(c))&&(c.__requireJsBuild=\n!0);if(\"string\"===typeof f){if(G(c))return w(C(\"requireargs\",\"Invalid require call\"),d);if(a&&s(N,f))return N[f](k[a.id]);if(h.get)return h.get(i,f,a,g);j=m(f,a,!1,!0);j=j.id;return!s(p,j)?w(C(\"notloaded\",'Module name \"'+j+'\" has not been loaded yet for context: '+b+(a?\"\":\". Use require([])\"))):p[j]}L();i.nextTick(function(){L();l=q(m(null,a));l.skipMap=e.skipMap;l.init(f,c,d,{enabled:!0});D()});return g}e=e||{};V(g,{isBrowser:z,toUrl:function(b){var e,d=b.lastIndexOf(\".\"),g=b.split(\"/\")[0];if(-1!==\nd&&(!(\".\"===g||\"..\"===g)||1<d))e=b.substring(d,b.length),b=b.substring(0,d);return i.nameToUrl(c(b,a&&a.id,!0),e,!0)},defined:function(b){return s(p,m(b,a,!1,!0).id)},specified:function(b){b=m(b,a,!1,!0).id;return s(p,b)||s(k,b)}});a||(g.undef=function(b){x();var c=m(b,a,!0),e=j(k,b);d(b);delete p[b];delete T[c.url];delete aa[b];U(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&(aa[b]=e.events),y(b))});return g},enable:function(a){j(k,a.id)&&q(a).enable()},completeLoad:function(a){var b,\nc,f=j(l.shim,a)||{},d=f.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=j(k,a);if(!b&&!s(p,a)&&c&&!c.inited){if(l.enforceDefine&&(!d||!ea(d)))return g(a)?void 0:w(C(\"nodefine\",\"No define call for \"+a,null,[a]));E([a,f.deps||[],f.exportsFn])}D()},nameToUrl:function(a,b,c){var f,d,g;(f=j(l.pkgs,a))&&(a=f);if(f=j(ba,a))return i.nameToUrl(f,b,c);if(h.jsExtRegExp.test(a))f=a+(b||\"\");else{f=l.paths;a=a.split(\"/\");for(d=a.length;0<d;d-=1)if(g=a.slice(0,\nd).join(\"/\"),g=j(f,g)){H(g)&&(g=g[0]);a.splice(0,d,g);break}f=a.join(\"/\");f+=b||(/^data\\:|\\?/.test(f)||c?\"\":\".js\");f=(\"/\"===f.charAt(0)||f.match(/^[\\w\\+\\.\\-]+:/)?\"\":l.baseUrl)+f}return l.urlArgs?f+((-1===f.indexOf(\"?\")?\"?\":\"&\")+l.urlArgs):f},load:function(a,b){h.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if(\"load\"===a.type||ka.test((a.currentTarget||a.srcElement).readyState))P=null,a=K(a),i.completeLoad(a.id)},onScriptError:function(a){var b=K(a);if(!g(b.id))return w(C(\"scripterror\",\n\"Script error for: \"+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var h,x,y,D,K,E,P,L,q,Q,la=/(\\/\\*([\\s\\S]*?)\\*\\/|([^:]|^)\\/\\/(.*)$)/mg,ma=/[^.]\\s*require\\s*\\(\\s*[\"']([^'\"\\s]+)[\"']\\s*\\)/g,R=/\\.js$/,ja=/^\\.\\//;x=Object.prototype;var M=x.toString,ga=x.hasOwnProperty,ia=Array.prototype.splice,z=!!(\"undefined\"!==typeof window&&\"undefined\"!==typeof navigator&&window.document),fa=!z&&\"undefined\"!==typeof importScripts,ka=z&&\"PLAYSTATION 3\"===navigator.platform?/^complete$/:/^(complete|loaded)$/,\nZ=\"undefined\"!==typeof opera&&\"[object Opera]\"===opera.toString(),F={},r={},S=[],O=!1;if(\"undefined\"===typeof define){if(\"undefined\"!==typeof requirejs){if(G(requirejs))return;r=requirejs;requirejs=void 0}\"undefined\"!==typeof require&&!G(require)&&(r=require,require=void 0);h=requirejs=function(b,c,d,g){var u,m=\"_\";!H(b)&&\"string\"!==typeof b&&(u=b,H(c)?(b=c,c=d,d=g):b=[]);u&&u.context&&(m=u.context);(g=j(F,m))||(g=F[m]=h.s.newContext(m));u&&g.configure(u);return g.require(b,c,d)};h.config=function(b){return h(b)};\nh.nextTick=\"undefined\"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=h);h.version=\"2.1.11\";h.jsExtRegExp=/^\\/|:|\\?|\\.js$/;h.isBrowser=z;x=h.s={contexts:F,newContext:ha};h({});v([\"toUrl\",\"undef\",\"defined\",\"specified\"],function(b){h[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName(\"head\")[0],D=document.getElementsByTagName(\"base\")[0]))y=x.head=D.parentNode;h.onError=da;h.createNode=function(b){var c=\nb.xhtml?document.createElementNS(\"http://www.w3.org/1999/xhtml\",\"html:script\"):document.createElement(\"script\");c.type=b.scriptType||\"text/javascript\";c.charset=\"utf-8\";c.async=!0;return c};h.load=function(b,c,d){var g=b&&b.config||{};if(z)return g=h.createNode(g,c,d),g.setAttribute(\"data-requirecontext\",b.contextName),g.setAttribute(\"data-requiremodule\",c),g.attachEvent&&!(g.attachEvent.toString&&0>g.attachEvent.toString().indexOf(\"[native code\"))&&!Z?(O=!0,g.attachEvent(\"onreadystatechange\",b.onScriptLoad)):\n(g.addEventListener(\"load\",b.onScriptLoad,!1),g.addEventListener(\"error\",b.onScriptError,!1)),g.src=d,L=g,D?y.insertBefore(g,D):y.appendChild(g),L=null,g;if(fa)try{importScripts(d),b.completeLoad(c)}catch(j){b.onError(C(\"importscripts\",\"importScripts failed for \"+c+\" at \"+d,j,[c]))}};z&&!r.skipDataMain&&U(document.getElementsByTagName(\"script\"),function(b){y||(y=b.parentNode);if(K=b.getAttribute(\"data-main\"))return q=K,r.baseUrl||(E=q.split(\"/\"),q=E.pop(),Q=E.length?E.join(\"/\")+\"/\":\"./\",r.baseUrl=\nQ),q=q.replace(R,\"\"),h.jsExtRegExp.test(q)&&(q=K),r.deps=r.deps?r.deps.concat(q):[q],!0});define=function(b,c,d){var g,h;\"string\"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(la,\"\").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?[\"require\"]:[\"require\",\"exports\",\"module\"]).concat(c)));if(O){if(!(g=L))P&&\"interactive\"===P.readyState||U(document.getElementsByTagName(\"script\"),function(b){if(\"interactive\"===b.readyState)return P=b}),g=P;g&&(b||\n(b=g.getAttribute(\"data-requiremodule\")),h=F[g.getAttribute(\"data-requirecontext\")])}(h?h.defQueue:S).push([b,c,d])};define.amd={jQuery:!0};h.exec=function(b){return eval(b)};h(r)}})(this);\n"
  },
  {
    "path": "jsdoc.conf.json",
    "content": "{\r\n\t\"tags\"      : {\r\n\t\t\"allowUnknownTags\" : false\r\n\t},\r\n\t\"plugins\"   : [\"plugins/markdown\"],\r\n\t\"templates\" : {\r\n\t\t\"cleverLinks\"           : false,\r\n\t\t\"monospaceLinks\"        : false,\r\n\t\t\"dateFormat\"            : \"ddd MMM Do YYYY\",\r\n\t\t\"outputSourceFiles\"     : false,\r\n\t\t\"outputSourcePath\"      : false,\r\n\t\t\"systemName\"            : \"ThreeDLibrary\",\r\n\t\t\"footer\"                : \"\",\r\n\t\t\"copyright\"             : \"copyright Google, Greggman\",\r\n\t\t\"navType\"               : \"vertical\",\r\n\t\t\"theme\"                 : \"cerulean\",\r\n\t\t\"linenums\"              : true,\r\n\t\t\"collapseSymbols\"       : false,\r\n\t\t\"inverseNav\"            : true,\r\n\t\t\"highlightTutorialCode\" : true\r\n\t},\r\n\t\"markdown\"  : {\r\n\t\t\"parser\"   : \"gfm\",\r\n\t\t\"hardwrap\" : true\r\n\t}\r\n}\r\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"tdl\",\n  \"version\": \"0.0.8\",\n  \"description\": \"Some WebGL Library\",\n  \"main\": \"tdl/base.js\",\n  \"directories\": {\n    \"doc\": \"docs\",\n    \"example\": \"example\"\n  },\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/greggman/tdl.git\"\n  },\n  \"keywords\": [\n    \"WebGL\"\n  ],\n  \"author\": \"Greggman\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/greggman/tdl/issues\"\n  },\n  \"homepage\": \"https://github.com/greggman/tdl\",\n  \"devDependencies\": {\n    \"grunt\": \"^0.4.5\",\n    \"grunt-contrib-clean\": \"^0.6.0\",\n    \"grunt-contrib-uglify\": \"^0.7.0\",\n    \"grunt-jsdoc\": \"^0.5.7\"\n  }\n}\n"
  },
  {
    "path": "tdl/base-rs.js",
    "content": "/*\n * Copyright 2014, Gregg Tavares.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Gregg Tavares. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n// emulate tdl/base.js for require.js\n\ndefine(function() {\n\n// Was base.js already included?\nvar haveBaseJS = (this.tdl !== undefined);\nif (haveBaseJS) {\n  tdl.provide('tdl.base-rs');\n  return;\n}\n\nthis.tdl = {base:{}};\nthis.goog = {};\n\nvar noop = function() {};\n\n// Let's assume if the user is using require JS they don't need tdl.require\n// If that's not the case we'd need provide a version of tdl.require that\n// ignores the tdl files but not the user's files. Probably hooked into requirejs\ntdl.require = noop;\ntdl.provide = noop;\n\n\n/**\n * Determine whether a value is an array. Do not use instanceof because that\n * will not work for V8 arrays (the browser thinks they are Objects).\n * @param {*} value A value.\n * @return {boolean} Whether the value is an array.\n */\ntdl.base.isArray = function(value) {\n  var valueAsObject = /** @type {!Object} */ (value);\n  return typeof(value) === 'object' && value !== null &&\n      'length' in valueAsObject && 'splice' in valueAsObject;\n};\n\n/**\n * A stub for later optionally converting obfuscated names\n * @private\n * @param {string} name Name to un-obfuscate.\n * @return {string} un-obfuscated name.\n */\ntdl.base.maybeDeobfuscateFunctionName_ = function(name) {\n  return name;\n};\n\n/**\n * Makes one class inherit from another.\n * @param {!Object} subClass Class that wants to inherit.\n * @param {!Object} superClass Class to inherit from.\n */\ntdl.base.inherit = function(subClass, superClass) {\n  /**\n   * TmpClass.\n   * @ignore\n   * @constructor\n   */\n  var TmpClass = function() { };\n  TmpClass.prototype = superClass.prototype;\n  subClass.prototype = new TmpClass();\n};\n\n/**\n * Parses an error stack from an exception\n * @param {!Exception} excp The exception to get a stack trace from.\n * @return {!Array.<string>} An array of strings of the stack trace.\n */\ntdl.base.parseErrorStack = function(excp) {\n  var stack = [];\n  var name;\n  var line;\n\n  if (!excp || !excp.stack) {\n    return stack;\n  }\n\n  var stacklist = excp.stack.split('\\n');\n\n  for (var i = 0; i < stacklist.length - 1; i++) {\n    var framedata = stacklist[i];\n\n    name = framedata.match(/^([a-zA-Z0-9_$]*)/)[1];\n    if (name) {\n      name = tdl.base.maybeDeobfuscateFunctionName_(name);\n    } else {\n      name = 'anonymous';\n    }\n\n    var result = framedata.match(/(.*:[0-9]+)$/);\n    line = result && result[1];\n\n    if (!line) {\n      line = '(unknown)';\n    }\n\n    stack[stack.length] = name + ' : ' + line\n  }\n\n  // remove top level anonymous functions to match IE\n  var omitRegexp = /^anonymous :/;\n  while (stack.length && omitRegexp.exec(stack[stack.length - 1])) {\n    stack.length = stack.length - 1;\n  }\n\n  return stack;\n};\n\n/**\n * Gets a function name from a function object.\n * @param {!function(...): *} aFunction The function object to try to get a\n *      name from.\n * @return {string} function name or 'anonymous' if not found.\n */\ntdl.base.getFunctionName = function(aFunction) {\n  var regexpResult = aFunction.toString().match(/function(\\s*)(\\w*)/);\n  if (regexpResult && regexpResult.length >= 2 && regexpResult[2]) {\n    return tdl.base.maybeDeobfuscateFunctionName_(regexpResult[2]);\n  }\n  return 'anonymous';\n};\n\n/**\n * Pretty prints an exception's stack, if it has one.\n * @param {Array.<string>} stack An array of errors.\n * @return {string} The pretty stack.\n */\ntdl.base.formatErrorStack = function(stack) {\n  var result = '';\n  for (var i = 0; i < stack.length; i++) {\n    result += '> ' + stack[i] + '\\n';\n  }\n  return result;\n};\n\n/**\n * Gets a stack trace as a string.\n * @param {number} stripCount The number of entries to strip from the top of the\n *     stack. Example: Pass in 1 to remove yourself from the stack trace.\n * @return {string} The stack trace.\n */\ntdl.base.getStackTrace = function(stripCount) {\n  var result = '';\n\n  if (typeof(arguments.caller) != 'undefined') { // IE, not ECMA\n    for (var a = arguments.caller; a != null; a = a.caller) {\n      result += '> ' + tdl.base.getFunctionName(a.callee) + '\\n';\n      if (a.caller == a) {\n        result += '*';\n        break;\n      }\n    }\n  } else { // Mozilla, not ECMA\n    // fake an exception so we can get Mozilla's error stack\n    var testExcp;\n    try {\n      eval('var var;');\n    } catch (testExcp) {\n      var stack = tdl.base.parseErrorStack(testExcp);\n      result += tdl.base.formatErrorStack(stack.slice(3 + stripCount,\n                                                        stack.length));\n    }\n  }\n\n  return result;\n};\n\n/**\n * Returns true if the user's browser is Microsoft IE.\n * @return {boolean} true if the user's browser is Microsoft IE.\n */\ntdl.base.IsMSIE = function() {\n  var ua = navigator.userAgent.toLowerCase();\n  var msie = /msie/.test(ua) && !/opera/.test(ua);\n  return msie;\n};\n\nreturn {};\n});\n"
  },
  {
    "path": "tdl/base.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview Base for all tdl sample utilties.\n *\n * The main point of this module is to provide a central place to\n * have an init function to register an tdl namespace object because many other\n * modules need access to it.\n */\n\n/**\n * A namespace for all the tdl utility libraries.\n * @namespace\n */\nvar tdl = tdl || {};\n\n/**\n * Define this because the Google internal JSCompiler needs goog.typedef below.\n */\nvar goog = goog || {};\n\n\nif (!window.Int32Array) {\n  window.Int32Array = function() { };\n  window.Float32Array = function() { };\n  window.Uint16Array = function() { };\n}\n\n/**\n * A macro for defining composite types.\n *\n * By assigning goog.typedef to a name, this tells Google internal JSCompiler\n * that this is not the name of a class, but rather it's the name of a composite\n * type.\n *\n * For example,\n * /** @type {Array|NodeList} / goog.ArrayLike = goog.typedef;\n * will tell JSCompiler to replace all appearances of goog.ArrayLike in type\n * definitions with the union of Array and NodeList.\n *\n * Does nothing in uncompiled code.\n */\ngoog.typedef = true;\n\n/**\n * Reference to the global context.  In most cases this will be 'window'.\n */\ntdl.global = this;\n\n/**\n * Some javascripts don't support __defineGetter__ or __defineSetter__\n * so we define some here so at least we don't get compile errors.\n * We expect the initialzation code will check and complain. This stubs\n * are just here to make sure we can actually get to the initialization code.\n */\n//if (!Object.prototype.__defineSetter__) {\n//  Object.prototype.__defineSetter__ = function() {}\n//  Object.prototype.__defineGetter__ = function() {}\n//}\n//\n/**\n * Flag used to force a function to run in the browser when it is called\n * from V8.\n * @type {boolean}\n */\ntdl.BROWSER_ONLY = true;\n\n/**\n * Array of namespaces that have been provided.\n * @private\n * @type {!Array.<string>}\n */\ntdl.provided_ = [];\n\n/**\n * Creates object stubs for a namespace. When present in a file,\n * tdl.provide also indicates that the file defines the indicated\n * object.\n * @param {string} name name of the object that this file defines.\n */\ntdl.provide = function(name) {\n  // Ensure that the same namespace isn't provided twice.\n  if (tdl.getObjectByName(name) &&\n      !tdl.implicitNamespaces_[name]) {\n    throw 'Namespace \"' + name + '\" already declared.';\n  }\n\n  var namespace = name;\n  while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {\n    tdl.implicitNamespaces_[namespace] = true;\n  }\n\n  tdl.exportPath_(name);\n  tdl.provided_.push(name);\n};\n\n\n/**\n * Namespaces implicitly defined by tdl.provide. For example,\n * tdl.provide('tdl.events.Event') implicitly declares\n * that 'tdl' and 'tdl.events' must be namespaces.\n *\n * @type {Object}\n * @private\n */\ntdl.implicitNamespaces_ = {};\n\n/**\n * Builds an object structure for the provided namespace path,\n * ensuring that names that already exist are not overwritten. For\n * example:\n * \"a.b.c\" -> a = {};a.b={};a.b.c={};\n * Used by tdl.provide and tdl.exportSymbol.\n * @param {string} name name of the object that this file defines.\n * @param {Object} opt_object the object to expose at the end of the path.\n * @param {Object} opt_objectToExportTo The object to add the path to; default\n *     is |tdl.global|.\n * @private\n */\ntdl.exportPath_ = function(name, opt_object, opt_objectToExportTo) {\n  var parts = name.split('.');\n  var cur = opt_objectToExportTo || tdl.global;\n  var part;\n\n  // Internet Explorer exhibits strange behavior when throwing errors from\n  // methods externed in this manner.  See the testExportSymbolExceptions in\n  // base_test.html for an example.\n  if (!(parts[0] in cur) && cur.execScript) {\n    cur.execScript('var ' + parts[0]);\n  }\n\n  // Parentheses added to eliminate strict JS warning in Firefox.\n  while (parts.length && (part = parts.shift())) {\n    if (!parts.length && tdl.isDef(opt_object)) {\n      // last part and we have an object; use it.\n      cur[part] = opt_object;\n    } else if (cur[part]) {\n      cur = cur[part];\n    } else {\n      cur = cur[part] = {};\n    }\n  }\n};\n\n\n/**\n * Returns an object based on its fully qualified external name.  If you are\n * using a compilation pass that renames property names beware that using this\n * function will not find renamed properties.\n *\n * @param {string} name The fully qualified name.\n * @param {Object} opt_obj The object within which to look; default is\n *     |tdl.global|.\n * @return {Object} The object or, if not found, null.\n */\ntdl.getObjectByName = function(name, opt_obj) {\n  var parts = name.split('.');\n  var cur = opt_obj || tdl.global;\n  for (var pp = 0; pp < parts.length; ++pp) {\n    var part = parts[pp];\n    if (cur[part]) {\n      cur = cur[part];\n    } else {\n      return null;\n    }\n  }\n  return cur;\n};\n\n\n/**\n * Implements a system for the dynamic resolution of dependencies.\n * @param {string} rule Rule to include, in the form tdl.package.part.\n */\ntdl.require = function(rule) {\n  // TODO(gman): For some unknown reason, when we call\n  // tdl.util.getScriptTagText_ it calls\n  // document.getElementsByTagName('script') and for some reason the scripts do\n  // not always show up. Calling it here seems to fix that as long as we\n  // actually ask for the length, at least in FF 3.5.1 It would be nice to\n  // figure out why.\n  var dummy = document.getElementsByTagName('script').length;\n  // if the object already exists we do not need do do anything\n  if (tdl.getObjectByName(rule)) {\n    return;\n  }\n  var path = tdl.getPathFromRule_(rule);\n  if (path) {\n    tdl.included_[path] = true;\n    tdl.writeScripts_();\n  } else {\n    throw new Error('tdl.require could not find: ' + rule);\n  }\n};\n\n\n/**\n * Path for included scripts.\n * @type {string}\n */\ntdl.basePath = '';\n\n\n/**\n * Object used to keep track of urls that have already been added. This\n * record allows the prevention of circular dependencies.\n * @type {Object}\n * @private\n */\ntdl.included_ = {};\n\n\n/**\n * This object is used to keep track of dependencies and other data that is\n * used for loading scripts.\n * @private\n * @type {Object}\n */\ntdl.dependencies_ = {\n  visited: {},  // used when resolving dependencies to prevent us from\n                // visiting the file twice.\n  written: {}  // used to keep track of script files we have written.\n};\n\n\n/**\n * Tries to detect the base path of the tdl-base.js script that\n * bootstraps the tdl libraries.\n * @private\n */\ntdl.findBasePath_ = function() {\n  var doc = tdl.global.document;\n  if (typeof doc == 'undefined') {\n    return;\n  }\n  if (tdl.global.BASE_PATH) {\n    tdl.basePath = tdl.global.BASE_PATH;\n    return;\n  } else {\n    // HACKHACK to hide compiler warnings :(\n    tdl.global.BASE_PATH = null;\n  }\n  var expectedBase = 'tdl/base.js';\n  var scripts = doc.getElementsByTagName('script');\n  for (var script, i = 0; script = scripts[i]; i++) {\n    var src = script.src;\n    var l = src.length;\n    if (src.substr(l - expectedBase.length) == expectedBase) {\n      tdl.basePath = src.substr(0, l - expectedBase.length);\n      return;\n    }\n  }\n};\n\n\n/**\n * Writes a script tag if, and only if, that script hasn't already been added\n * to the document.  (Must be called at execution time.)\n * @param {string} src Script source.\n * @private\n */\ntdl.writeScriptTag_ = function(src) {\n  var doc = tdl.global.document;\n  if (typeof doc != 'undefined' &&\n      !tdl.dependencies_.written[src]) {\n    tdl.dependencies_.written[src] = true;\n    var html = '<script type=\"text/javascript\" src=\"' +\n               src + '\"></' + 'script>'\n    doc.write(html);\n  }\n};\n\n\n/**\n * Resolves dependencies based on the dependencies added using addDependency\n * and calls writeScriptTag_ in the correct order.\n * @private\n */\ntdl.writeScripts_ = function() {\n  // the scripts we need to write this time.\n  var scripts = [];\n  var seenScript = {};\n  var deps = tdl.dependencies_;\n\n  function visitNode(path) {\n    if (path in deps.written) {\n      return;\n    }\n\n    // we have already visited this one. We can get here if we have cyclic\n    // dependencies.\n    if (path in deps.visited) {\n      if (!(path in seenScript)) {\n        seenScript[path] = true;\n        scripts.push(path);\n      }\n      return;\n    }\n\n    deps.visited[path] = true;\n\n    if (!(path in seenScript)) {\n      seenScript[path] = true;\n      scripts.push(path);\n    }\n  }\n\n  for (var path in tdl.included_) {\n    if (!deps.written[path]) {\n      visitNode(path);\n    }\n  }\n\n  for (var i = 0; i < scripts.length; i++) {\n    if (scripts[i]) {\n      tdl.writeScriptTag_(tdl.basePath + scripts[i]);\n    } else {\n      throw Error('Undefined script input');\n    }\n  }\n};\n\n\n/**\n * Looks at the dependency rules and tries to determine the script file that\n * fulfills a particular rule.\n * @param {string} rule In the form tdl.namespace.Class or\n *     project.script.\n * @return {string?} Url corresponding to the rule, or null.\n * @private\n */\ntdl.getPathFromRule_ = function(rule) {\n  var parts = rule.split('.');\n  return parts.join('/') + '.js';\n};\n\ntdl.findBasePath_();\n\n/**\n * Returns true if the specified value is not |undefined|.\n * WARNING: Do not use this to test if an object has a property. Use the in\n * operator instead.\n * @param {*} val Variable to test.\n * @return {boolean} Whether variable is defined.\n */\ntdl.isDef = function(val) {\n  return typeof val != 'undefined';\n};\n\n\n/**\n * Exposes an unobfuscated global namespace path for the given object.\n * Note that fields of the exported object *will* be obfuscated,\n * unless they are exported in turn via this function or\n * tdl.exportProperty.\n *\n * <p>Also handy for making public items that are defined in anonymous\n * closures.\n *\n * ex. tdl.exportSymbol('Foo', Foo);\n *\n * ex. tdl.exportSymbol('public.path.Foo.staticFunction',\n *                        Foo.staticFunction);\n *     public.path.Foo.staticFunction();\n *\n * ex. tdl.exportSymbol('public.path.Foo.prototype.myMethod',\n *                        Foo.prototype.myMethod);\n *     new public.path.Foo().myMethod();\n *\n * @param {string} publicPath Unobfuscated name to export.\n * @param {Object} object Object the name should point to.\n * @param {Object} opt_objectToExportTo The object to add the path to; default\n *     is |tdl.global|.\n */\ntdl.exportSymbol = function(publicPath, object, opt_objectToExportTo) {\n  tdl.exportPath_(publicPath, object, opt_objectToExportTo);\n};\n\ntdl.provide('tdl.base');\n\n/**\n * The base module for tdl.\n * @namespace\n */\ntdl.base = tdl.base || {};\n\n/**\n * Determine whether a value is an array. Do not use instanceof because that\n * will not work for V8 arrays (the browser thinks they are Objects).\n * @param {*} value A value.\n * @return {boolean} Whether the value is an array.\n */\ntdl.base.isArray = function(value) {\n  var valueAsObject = /** @type {!Object} */ (value);\n  return typeof(value) === 'object' && value !== null &&\n      'length' in valueAsObject && 'splice' in valueAsObject;\n};\n\n/**\n * A stub for later optionally converting obfuscated names\n * @private\n * @param {string} name Name to un-obfuscate.\n * @return {string} un-obfuscated name.\n */\ntdl.base.maybeDeobfuscateFunctionName_ = function(name) {\n  return name;\n};\n\n/**\n * Makes one class inherit from another.\n * @param {!Object} subClass Class that wants to inherit.\n * @param {!Object} superClass Class to inherit from.\n */\ntdl.base.inherit = function(subClass, superClass) {\n  /**\n   * TmpClass.\n   * @ignore\n   * @constructor\n   */\n  var TmpClass = function() { };\n  TmpClass.prototype = superClass.prototype;\n  subClass.prototype = new TmpClass();\n};\n\n/**\n * Parses an error stack from an exception\n * @param {!Exception} excp The exception to get a stack trace from.\n * @return {!Array.<string>} An array of strings of the stack trace.\n */\ntdl.base.parseErrorStack = function(excp) {\n  var stack = [];\n  var name;\n  var line;\n\n  if (!excp || !excp.stack) {\n    return stack;\n  }\n\n  var stacklist = excp.stack.split('\\n');\n\n  for (var i = 0; i < stacklist.length - 1; i++) {\n    var framedata = stacklist[i];\n\n    name = framedata.match(/^([a-zA-Z0-9_$]*)/)[1];\n    if (name) {\n      name = tdl.base.maybeDeobfuscateFunctionName_(name);\n    } else {\n      name = 'anonymous';\n    }\n\n    var result = framedata.match(/(.*:[0-9]+)$/);\n    line = result && result[1];\n\n    if (!line) {\n      line = '(unknown)';\n    }\n\n    stack[stack.length] = name + ' : ' + line\n  }\n\n  // remove top level anonymous functions to match IE\n  var omitRegexp = /^anonymous :/;\n  while (stack.length && omitRegexp.exec(stack[stack.length - 1])) {\n    stack.length = stack.length - 1;\n  }\n\n  return stack;\n};\n\n/**\n * Gets a function name from a function object.\n * @param {!function(...): *} aFunction The function object to try to get a\n *      name from.\n * @return {string} function name or 'anonymous' if not found.\n */\ntdl.base.getFunctionName = function(aFunction) {\n  var regexpResult = aFunction.toString().match(/function(\\s*)(\\w*)/);\n  if (regexpResult && regexpResult.length >= 2 && regexpResult[2]) {\n    return tdl.base.maybeDeobfuscateFunctionName_(regexpResult[2]);\n  }\n  return 'anonymous';\n};\n\n/**\n * Pretty prints an exception's stack, if it has one.\n * @param {Array.<string>} stack An array of errors.\n * @return {string} The pretty stack.\n */\ntdl.base.formatErrorStack = function(stack) {\n  var result = '';\n  for (var i = 0; i < stack.length; i++) {\n    result += '> ' + stack[i] + '\\n';\n  }\n  return result;\n};\n\n/**\n * Gets a stack trace as a string.\n * @param {number} stripCount The number of entries to strip from the top of the\n *     stack. Example: Pass in 1 to remove yourself from the stack trace.\n * @return {string} The stack trace.\n */\ntdl.base.getStackTrace = function(stripCount) {\n  var result = '';\n\n  if (typeof(arguments.caller) != 'undefined') { // IE, not ECMA\n    for (var a = arguments.caller; a != null; a = a.caller) {\n      result += '> ' + tdl.base.getFunctionName(a.callee) + '\\n';\n      if (a.caller == a) {\n        result += '*';\n        break;\n      }\n    }\n  } else { // Mozilla, not ECMA\n    // fake an exception so we can get Mozilla's error stack\n    var testExcp;\n    try {\n      eval('var var;');\n    } catch (testExcp) {\n      var stack = tdl.base.parseErrorStack(testExcp);\n      result += tdl.base.formatErrorStack(stack.slice(3 + stripCount,\n                                                        stack.length));\n    }\n  }\n\n  return result;\n};\n\n/**\n * Returns true if the user's browser is Microsoft IE.\n * @return {boolean} true if the user's browser is Microsoft IE.\n */\ntdl.base.IsMSIE = function() {\n  var ua = navigator.userAgent.toLowerCase();\n  var msie = /msie/.test(ua) && !/opera/.test(ua);\n  return msie;\n};\n\n// Handle case where we are NOT using require.js\nfunction define() {\n  var args = [];\n  for (var ii = 0; ii < arguments.length; ++ii) {\n    args.push(arguments[ii]);\n  }\n  if (args.length == 1) {\n    args.unshift([]);\n  }\n  if (args.length == 2) {\n    args.unshift(undefined);\n  }\n\n  var id = args[0];\n  var deps = args[1];\n  var obj = args[2];\n\n  for (var ii = 0; ii < deps.length; ++ii) {\n    // assume format is './name'\n    var name = deps[ii].replace('./', 'tdl.');\n    tdl.require(name);\n  }\n  if (typeof obj == \"function\") {\n    obj();\n  }\n}\n"
  },
  {
    "path": "tdl/buffers.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains objects to deal with WebGL\n *               buffers.\n */\ndefine(['./base-rs'], function(BaseRS) {\n\ntdl.provide('tdl.buffers');\n/**\n * A module for buffers.\n * @namespace\n */\ntdl.buffers = tdl.buffers || {};\n\n/**\n * A Buffer represnets a WebGL buffer.\n * @constructor\n * @param {tdl.primitives.AttribBuffer} array AttribBuffer with\n *        data.\n * @param {number?} opt_target Assumes gl.ARRAY_BUFFER\n */\ntdl.buffers.Buffer = function(array, opt_target) {\n  var target = opt_target || gl.ARRAY_BUFFER;\n  var buf = gl.createBuffer();\n  this.target = target;\n  this.buf = buf;\n  this.set(array);\n};\n\n/**\n * Sets the contents of this buffer from the contents of the\n * given AttribBuffer.\n * @param {tdl.primitives.AttribBuffer} array AttribBuffer with\n *        data.\n * @param {number?} opt_usage GL buffer usage. Defaults to\n *        gl.STATIC_DRAW.\n */\ntdl.buffers.Buffer.prototype.set = function(array, opt_usage) {\n  this.numComponents_ = array.numComponents;\n  this.numElements_ = array.numElements;\n  this.totalComponents_ = this.numComponents_ * this.numElements_;\n  if (array.buffer instanceof Float32Array) {\n    this.type_ = gl.FLOAT;\n    this.normalize_ = false;\n  } else if (array.buffer instanceof Uint8Array) {\n    this.type_ = gl.UNSIGNED_BYTE;\n    this.normalize_ = true;\n  } else if (array.buffer instanceof Int8Array) {\n    this.type_ = gl.BYTE;\n    this.normalize_ = true;\n  } else if (array.buffer instanceof Uint16Array) {\n    this.type_ = gl.UNSIGNED_SHORT;\n    this.normalize_ = true;\n  } else if (array.buffer instanceof Int16Array) {\n    this.type_ = gl.SHORT;\n    this.normalize_ = true;\n  } else {\n    throw(\"unhandled type:\" + (typeof array.buffer));\n  }\n  gl.bindBuffer(this.target, this.buf);\n  gl.bufferData(this.target, array.buffer, opt_usage || gl.STATIC_DRAW);\n}\n\n/**\n * Sets part of a buffer.\n * @param {ArrayBufferView} array some typed array buffer view\n * @param {number} the offset in bytes into the buffer to copy\n *        the array.\n */\ntdl.buffers.Buffer.prototype.setRange = function(array, offset) {\n  gl.bindBuffer(this.target, this.buf);\n  gl.bufferSubData(this.target, offset, array);\n};\n\n/**\n * Gets the type of the buffer. Eg. `gl.FLOAT`\n * @return {number}\n */\ntdl.buffers.Buffer.prototype.type = function() {\n  return this.type_;\n};\n\n/**\n * Gets the number of components per element of buffer.\n * @return {number} num components per element in buffer\n */\ntdl.buffers.Buffer.prototype.numComponents = function() {\n  return this.numComponents_;\n};\n\n/**\n * Gets the number of elements in the buffer\n * @return {number} num elements in buffer\n */\ntdl.buffers.Buffer.prototype.numElements = function() {\n  return this.numElements_;\n};\n\n/**\n * Gets the total components in the buffer.\n * @return {number} Basically this is numComponents *\n *         numElements\n */\ntdl.buffers.Buffer.prototype.totalComponents = function() {\n  return this.totalComponents_;\n};\n\n/**\n * Get the WebGLBuffer for this buffer.\n * @return {WebGLBuffer} the WebGLBuffer for this buffer.\n */\ntdl.buffers.Buffer.prototype.buffer = function() {\n  return this.buf;\n};\n\n/**\n * Get the stride?\n * @return {number} stride.\n */\ntdl.buffers.Buffer.prototype.stride = function() {\n  return 0;\n};\n\n/**\n * Gets whether the data in this buffer should be normalized.\n * @return {boolean} normalizaiton.\n */\ntdl.buffers.Buffer.prototype.normalize = function() {\n  return this.normalize_;\n}\n\n/**\n * Get the offset?\n * @return {number} offset.\n */\ntdl.buffers.Buffer.prototype.offset = function() {\n  return 0;\n};\n\nreturn tdl.buffers;\n});\n"
  },
  {
    "path": "tdl/clock.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains various functions for managing a clock\n */\ndefine(['./base-rs', './io', './log'], function(BaseRS, IO, Log) {\n\ntdl.provide('tdl.clock');\n/**\n * Clock related stuff\n * @namespace\n */\ntdl.clock = tdl.clock || {};\n\n/**\n * Creates a clock. Optionally synced to a server\n * @param {number} opt_syncRate. If passed, this is the number of seconds\n *        between syncing to the server. If not passed the local clock is used.\n *        Note: If the client is faster than the server this means it's possible\n *        the clock will report a certain time and then later a previous time.\n */\ntdl.clock.createClock = function(opt_syncRate, opt_url) {\n  if (opt_syncRate) {\n    return new tdl.clock.SyncedClock(opt_syncRate, opt_url);\n  } else {\n    return new tdl.clock.LocalClock();\n  }\n};\n\n/**\n * A clock that gets the local current time in seconds.\n * @constructor\n * @private\n */\ntdl.clock.LocalClock = function() {\n}\n\n/**\n * Gets the current time in seconds.\n * @return {number} current time in seconds\n */\ntdl.clock.LocalClock.prototype.getTime = function() {\n  return (new Date()).getTime() * 0.001;\n}\n\n/**\n * A clock that gets the current time in seconds attempting to eep the clock\n * synced to the server.\n * @constructor\n * @private\n */\ntdl.clock.SyncedClock = function(opt_syncRate, opt_url) {\n  this.url = opt_url || window.location.href;\n  this.syncRate = opt_syncRate || 10;\n  this.timeOffset = 0;\n  this.syncToServer();\n}\n\ntdl.clock.SyncedClock.prototype.getLocalTime_ = function() {\n  return (new Date()).getTime() * 0.001;\n}\n\ntdl.clock.SyncedClock.prototype.syncToServer = function() {\n  var that = this;\n  var sendTime = this.getLocalTime_();\n  tdl.io.sendJSON(this.url, {cmd: 'time'}, function(obj, exception) {\n    if (exception) {\n      tdl.log(\"error: syncToServer: \" + exception);\n    } else {\n      var receiveTime = that.getLocalTime_();\n      var duration = receiveTime - sendTime;\n      var serverTime = obj.time + duration * 0.5;\n      that.timeOffset = serverTime - receiveTime;\n      tdl.log(\"new timeoffset: \" + that.timeOffset);\n    }\n    setTimeout(function() {\n        that.syncToServer();\n      }, that.syncRate * 1000);\n  });\n};\n\n/**\n * Gets the current time in seconds.\n * @return {number} current time in seconds\n */\ntdl.clock.SyncedClock.prototype.getTime = function() {\n  return (new Date()).getTime() * 0.001 + this.timeOffset;\n}\n\nreturn tdl.clock;\n});\n"
  },
  {
    "path": "tdl/fast.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains matrix/vector math functions.\n */\ndefine(['./base-rs'], function(BaseRS) {\n\ntdl.provide('tdl.fast');\n\n/**\n * A module for math for tdl.fast.\n * @namespace\n */\ntdl.fast = tdl.fast || {};\n\nif (!window.Float32Array) {\n  // This just makes some errors go away when there is no WebGL.\n  window.Float32Array = function() { };\n}\n\ntdl.fast.temp0v3_ = new Float32Array(3);\ntdl.fast.temp1v3_ = new Float32Array(3);\ntdl.fast.temp2v3_ = new Float32Array(3);\n\ntdl.fast.temp0v4_ = new Float32Array(4);\ntdl.fast.temp1v4_ = new Float32Array(4);\ntdl.fast.temp2v4_ = new Float32Array(4);\n\ntdl.fast.temp0m4_ = new Float32Array(16);\ntdl.fast.temp1m4_ = new Float32Array(16);\ntdl.fast.temp2m4_ = new Float32Array(16);\n\n/**\n * Functions which deal with 4-by-4 transformation matrices are kept in their\n * own namespsace.\n * @namespace\n */\ntdl.fast.matrix4 = tdl.fast.matrix4 || {};\n\n/**\n * Functions that are specifically row major are kept in their own namespace.\n * @namespace\n */\ntdl.fast.rowMajor = tdl.fast.rowMajor || {};\n\n/**\n * Functions that are specifically column major are kept in their own namespace.\n * @namespace\n */\ntdl.fast.columnMajor = tdl.fast.columnMajor || {};\n\n/**\n * An Array of 2 floats\n * @typedef {Float32Array} tdl.fast.Vector2\n */\n\n/**\n * An Array of 3 floats\n * @typedef {Float32Array} tdl.fast.Vector3\n */\n\n/**\n * An Array of 4 floats\n * @typedef {Float32Array} tdl.fast.Vector4\n */\n\n/**\n * An Array of floats.\n * @typedef {Float32Array} tdl.fast.Vector\n */\n\n/**\n * A 2x2 Matrix of floats\n * @typedef {Float32Array} tdl.fast.Matrix2\n */\n\n/**\n * A 3x3 Matrix of floats\n * @typedef {Float32Array} tdl.fast.Matrix3\n */\n\n/**\n * A 4x4 Matrix of floats\n * @typedef {Float32Array} tdl.fast.Matrix4\n */\n\n/**\n * A arbitrary size Matrix of floats\n * @typedef {Array<Number[]>} tdl.fast.Matrix\n */\n\n/**\n * Adds two vectors; assumes a and b have the same dimension.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Vector} a Operand vector.\n * @param {tdl.fast.Vector} b Operand vector.\n */\ntdl.fast.addVector = function(dst, a, b) {\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    dst[i] = a[i] + b[i];\n  return dst;\n};\n\n/**\n * Subtracts two vectors.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Vector} a Operand vector.\n * @param {tdl.fast.Vector} b Operand vector.\n */\ntdl.fast.subVector = function(dst, a, b) {\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    dst[i] = a[i] - b[i];\n  return dst;\n};\n\n/**\n * Performs linear interpolation on two vectors.\n * Given vectors a and b and interpolation coefficient t, returns\n * (1 - t) * a + t * b.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Vector} a Operand vector.\n * @param {tdl.fast.Vector} b Operand vector.\n * @param {number} t Interpolation coefficient.\n */\ntdl.fast.lerpVector = function(dst, a, b, t) {\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    dst[i] = (1 - t) * a[i] + t * b[i];\n  return dst;\n};\n\n/**\n * Divides a vector by a scalar.\n * @param {tdl.fast.Vector} dst The vector.\n * @param {tdl.fast.Vector} v The vector.\n * @param {number} k The scalar.\n * @return {tdl.fast.Vector} dst.\n */\ntdl.fast.divVectorScalar = function(dst, v, k) {\n  var vLength = v.length;\n  for (var i = 0; i < vLength; ++i)\n    dst[i] = v[i] / k;\n  return dst;\n};\n\n/**\n * Computes the cross product of two vectors; assumes both vectors have\n * three entries.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Vector} a Operand vector.\n * @param {tdl.fast.Vector} b Operand vector.\n * @return {tdl.fast.Vector} The vector a cross b.\n */\ntdl.fast.cross = function(dst, a, b) {\n  dst[0] = a[1] * b[2] - a[2] * b[1];\n  dst[1] = a[2] * b[0] - a[0] * b[2];\n  dst[2] = a[0] * b[1] - a[1] * b[0];\n  return dst;\n};\n\n/**\n * Computes the dot product of two vectors; assumes both vectors have\n * three entries.\n * @param {tdl.fast.Vector} a Operand vector.\n * @param {tdl.fast.Vector} b Operand vector.\n * @return {number} dot product\n */\ntdl.fast.dot = function(a, b) {\n  return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]);\n};\n\n/**\n * Divides a vector by its Euclidean length and returns the quotient.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Vector} a The vector.\n * @return {tdl.fast.Vector} The normalized vector.\n */\ntdl.fast.normalize = function(dst, a) {\n  var n = 0.0;\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    n += a[i] * a[i];\n  n = Math.sqrt(n);\n  if (n > 0.00001) {\n    for (var i = 0; i < aLength; ++i)\n      dst[i] = a[i] / n;\n  } else {\n    for (var i = 0; i < aLength; ++i)\n      dst[i] = 0;\n  }\n  return dst;\n};\n\n/**\n * Negates a vector.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Vector} v The vector.\n * @return {tdl.fast.Vector} -v.\n */\ntdl.fast.negativeVector = function(dst, v) {\n var vLength = v.length;\n for (var i = 0; i < vLength; ++i) {\n   dst[i] = -v[i];\n }\n return dst;\n};\n\n/**\n * Negates a matrix.\n * @param {tdl.fast.Matrix} dst matrix.\n * @param {tdl.fast.Matrix} v The matrix.\n * @return {tdl.fast.Matrix} -v.\n */\ntdl.fast.negativeMatrix = function(dst, v) {\n  var vLength = v.length;\n  for (var i = 0; i < vLength; ++i) {\n    dst[i] = -v[i];\n  }\n  return dst;\n};\n\n/**\n * Copies a vector.\n * @param {tdl.fast.Vector} v The vector.\n * @return {tdl.fast.Vector} A copy of v.\n */\ntdl.fast.copyVector = function(dst, v) {\n  dst.set(v);\n  return dst;\n};\n\n/**\n * Copies a matrix.\n * @param {tdl.fast.Matrix} m The matrix.\n * @return {tdl.fast.Matrix} A copy of m.\n */\ntdl.fast.copyMatrix = function(dst, m) {\n  dst.set(m);\n  return dst;\n};\n\n/**\n * Multiplies a scalar by a vector.\n * @param {tdl.fast.Vector} dst vector.\n * @param {number} k The scalar.\n * @param {tdl.fast.Vector} v The vector.\n * @return {tdl.fast.Vector} The product of k and v.\n */\ntdl.fast.mulScalarVector = function(dst, k, v) {\n  var vLength = v.length;\n  for (var i = 0; i < vLength; ++i) {\n    dst[i] = k * v[i];\n  }\n  return dst;\n};\n\n/**\n * Multiplies a vector by a scalar.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Vector} v The vector.\n * @param {number} k The scalar.\n * @return {tdl.fast.Vector} The product of k and v.\n */\ntdl.fast.mulVectorScalar = function(dst, v, k) {\n  return tdl.fast.mulScalarVector(dst, k, v);\n};\n\n/**\n * Multiplies a scalar by a matrix.\n * @param {tdl.fast.Matrix} dst matrix.\n * @param {number} k The scalar.\n * @param {tdl.fast.Matrix} m The matrix.\n * @return {tdl.fast.Matrix} The product of m and k.\n */\ntdl.fast.mulScalarMatrix = function(dst, k, m) {\n  var mLength = m.length;\n  for (var i = 0; i < mLength; ++i) {\n    dst[i] = k * m[i];\n  }\n  return dst;\n};\n\n/**\n * Multiplies a matrix by a scalar.\n * @param {tdl.fast.Matrix} dst matrix.\n * @param {tdl.fast.Matrix} m The matrix.\n * @param {number} k The scalar.\n * @return {tdl.fast.Matrix} The product of m and k.\n */\ntdl.fast.mulMatrixScalar = function(dst, m, k) {\n  return tdl.fast.mulScalarMatrix(dst, k, m);\n};\n\n/**\n * Multiplies a vector by another vector (component-wise); assumes a and\n * b have the same length.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Vector} a Operand vector.\n * @param {tdl.fast.Vector} b Operand vector.\n * @return {tdl.fast.Vector} The vector of products of entries of a and\n *     b.\n */\ntdl.fast.mulVectorVector = function(dst, a, b) {\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    dst[i] = a[i] * b[i];\n  return dst;\n};\n\n/**\n * Divides a vector by another vector (component-wise); assumes a and\n * b have the same length.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Vector} a Operand vector.\n * @param {tdl.fast.Vector} b Operand vector.\n * @return {tdl.fast.Vector} The vector of quotients of entries of a and\n *     b.\n */\ntdl.fast.divVectorVector = function(dst, a, b) {\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    dst[i] = a[i] / b[i];\n  return dst;\n};\n\n/**\n * Multiplies a vector by a matrix; treats the vector as a row vector; assumes\n * matrix entries are accessed in [row][column] fashion.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Vector} v The vector.\n * @param {tdl.fast.Matrix} m The matrix.\n * @return {tdl.fast.Vector} The product of v and m as a row vector.\n */\ntdl.fast.rowMajor.mulVectorMatrix4 = function(dst, v, m) {\n  for (var i = 0; i < 4; ++i) {\n    dst[i] = 0.0;\n    for (var j = 0; j < 4; ++j)\n      dst[i] += v[j] * m[j * 4 + i];\n  }\n  return dst;\n};\n\n/**\n * Multiplies a vector by a matrix; treats the vector as a row vector; assumes\n * matrix entries are accessed in [column][row] fashion.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Vector} v The vector.\n * @param {tdl.fast.Matrix} m The matrix.\n * @return {tdl.fast.Vector} The product of v and m as a row vector.\n */\ntdl.fast.columnMajor.mulVectorMatrix4 = function(dst, v, m) {\n  var mLength = m.length;\n  var vLength = v.length;\n  for (var i = 0; i < 4; ++i) {\n    dst[i] = 0.0;\n    var col = i * 4;\n    for (var j = 0; j < 4; ++j)\n      dst[i] += v[j] * m[col + j];\n  }\n  return dst;\n};\n\n/**\n * Multiplies a vector by a matrix; treats the vector as a row vector.\n * @param {tdl.fast.Matrix} m The matrix.\n * @param {tdl.fast.Vector} v The vector.\n * @return {tdl.fast.Vector} The product of m and v as a row vector.\n */\ntdl.fast.mulVectorMatrix4 = null;\n\n/**\n * Multiplies a matrix by a vector; treats the vector as a column vector.\n * assumes matrix entries are accessed in [row][column] fashion.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Matrix} m The matrix.\n * @param {tdl.fast.Vector} v The vector.\n * @return {tdl.fast.Vector} The product of m and v as a column vector.\n */\ntdl.fast.rowMajor.mulMatrix4Vector = function(dst, m, v) {\n  for (var i = 0; i < 4; ++i) {\n    dst[i] = 0.0;\n    var row = i * 4;\n    for (var j = 0; j < 4; ++j)\n      dst[i] += m[row + j] * v[j];\n  }\n  return dst;\n};\n\n/**\n * Multiplies a matrix by a vector; treats the vector as a column vector;\n * assumes matrix entries are accessed in [column][row] fashion.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Matrix} m The matrix.\n * @param {tdl.fast.Vector} v The vector.\n * @return {tdl.fast.Vector} The product of m and v as a column vector.\n */\ntdl.fast.columnMajor.mulMatrix4Vector = function(dst, m, v) {\n  for (var i = 0; i < 4; ++i) {\n    dst[i] = 0.0;\n    for (var j = 0; j < 4; ++j)\n      dst[i] += v[j] * m[j * 4 + i];\n  }\n  return dst;\n};\n\n/**\n * Multiplies a matrix by a vector; treats the vector as a column vector.\n * @param {tdl.fast.Matrix} m The matrix.\n * @param {tdl.fast.Vector} v The vector.\n * @return {tdl.fast.Vector} The product of m and v as a column vector.\n */\ntdl.fast.mulMatrix4Vector = null;\n\n/**\n * Multiplies two 3-by-3 matrices; assumes that the given matrices are 3-by-3;\n * assumes matrix entries are accessed in [row][column] fashion.\n * @param {tdl.fast.Matrix3} dst matrix.\n * @param {tdl.fast.Matrix3} a The matrix on the left.\n * @param {tdl.fast.Matrix3} b The matrix on the right.\n * @return {tdl.fast.Matrix3} The matrix product of a and b.\n */\ntdl.fast.rowMajor.mulMatrixMatrix3 = function(dst, a, b) {\n  var a00 = a[0];\n  var a01 = a[1];\n  var a02 = a[2];\n  var a10 = a[3 + 0];\n  var a11 = a[3 + 1];\n  var a12 = a[3 + 2];\n  var a20 = a[6 + 0];\n  var a21 = a[6 + 1];\n  var a22 = a[6 + 2];\n  var b00 = b[0];\n  var b01 = b[1];\n  var b02 = b[2];\n  var b10 = b[3 + 0];\n  var b11 = b[3 + 1];\n  var b12 = b[3 + 2];\n  var b20 = b[6 + 0];\n  var b21 = b[6 + 1];\n  var b22 = b[6 + 2];\n  dst[0] = a00 * b00 + a01 * b10 + a02 * b20;\n  dst[1] = a00 * b01 + a01 * b11 + a02 * b21;\n  dst[2] = a00 * b02 + a01 * b12 + a02 * b22;\n  dst[3] = a10 * b00 + a11 * b10 + a12 * b20;\n  dst[4] = a10 * b01 + a11 * b11 + a12 * b21;\n  dst[5] = a10 * b02 + a11 * b12 + a12 * b22;\n  dst[6] = a20 * b00 + a21 * b10 + a22 * b20;\n  dst[7] = a20 * b01 + a21 * b11 + a22 * b21;\n  dst[8] = a20 * b02 + a21 * b12 + a22 * b22;\n  return dst;\n};\n\n/**\n * Multiplies two 3-by-3 matrices; assumes that the given matrices are 3-by-3;\n * assumes matrix entries are accessed in [column][row] fashion.\n * @param {tdl.fast.Matrix3} dst matrix.\n * @param {tdl.fast.Matrix3} a The matrix on the left.\n * @param {tdl.fast.Matrix3} b The matrix on the right.\n * @return {tdl.fast.Matrix3} The matrix product of a and b.\n */\ntdl.fast.columnMajor.mulMatrixMatrix3 = function(dst, a, b) {\n  var a00 = a[0];\n  var a01 = a[1];\n  var a02 = a[2];\n  var a10 = a[3 + 0];\n  var a11 = a[3 + 1];\n  var a12 = a[3 + 2];\n  var a20 = a[6 + 0];\n  var a21 = a[6 + 1];\n  var a22 = a[6 + 2];\n  var b00 = b[0];\n  var b01 = b[1];\n  var b02 = b[2];\n  var b10 = b[3 + 0];\n  var b11 = b[3 + 1];\n  var b12 = b[3 + 2];\n  var b20 = b[6 + 0];\n  var b21 = b[6 + 1];\n  var b22 = b[6 + 2];\n  dst[0] = a00 * b00 + a10 * b01 + a20 * b02;\n  dst[1] = a01 * b00 + a11 * b01 + a21 * b02;\n  dst[2] = a02 * b00 + a12 * b01 + a22 * b02;\n  dst[3] = a00 * b10 + a10 * b11 + a20 * b12;\n  dst[4] = a01 * b10 + a11 * b11 + a21 * b12;\n  dst[5] = a02 * b10 + a12 * b11 + a22 * b12;\n  dst[6] = a00 * b20 + a10 * b21 + a20 * b22;\n  dst[7] = a01 * b20 + a11 * b21 + a21 * b22;\n  dst[8] = a02 * b20 + a12 * b21 + a22 * b22;\n  return dst;\n};\n\n/**\n * Multiplies two 3-by-3 matrices; assumes that the given matrices are 3-by-3.\n * @param {tdl.fast.Matrix3} a The matrix on the left.\n * @param {tdl.fast.Matrix3} b The matrix on the right.\n * @return {tdl.fast.Matrix3} The matrix product of a and b.\n */\ntdl.fast.mulMatrixMatrix3 = null;\n\n/**\n * Multiplies two 4-by-4 matrices; assumes that the given matrices are 4-by-4;\n * assumes matrix entries are accessed in [row][column] fashion.\n * @param {tdl.fast.Matrix4} dst matrix.\n * @param {tdl.fast.Matrix4} a The matrix on the left.\n * @param {tdl.fast.Matrix4} b The matrix on the right.\n * @return {tdl.fast.Matrix4} The matrix product of a and b.\n */\ntdl.fast.rowMajor.mulMatrixMatrix4 = function(dst, a, b) {\n  var a00 = a[0];\n  var a01 = a[1];\n  var a02 = a[2];\n  var a03 = a[3];\n  var a10 = a[ 4 + 0];\n  var a11 = a[ 4 + 1];\n  var a12 = a[ 4 + 2];\n  var a13 = a[ 4 + 3];\n  var a20 = a[ 8 + 0];\n  var a21 = a[ 8 + 1];\n  var a22 = a[ 8 + 2];\n  var a23 = a[ 8 + 3];\n  var a30 = a[12 + 0];\n  var a31 = a[12 + 1];\n  var a32 = a[12 + 2];\n  var a33 = a[12 + 3];\n  var b00 = b[0];\n  var b01 = b[1];\n  var b02 = b[2];\n  var b03 = b[3];\n  var b10 = b[ 4 + 0];\n  var b11 = b[ 4 + 1];\n  var b12 = b[ 4 + 2];\n  var b13 = b[ 4 + 3];\n  var b20 = b[ 8 + 0];\n  var b21 = b[ 8 + 1];\n  var b22 = b[ 8 + 2];\n  var b23 = b[ 8 + 3];\n  var b30 = b[12 + 0];\n  var b31 = b[12 + 1];\n  var b32 = b[12 + 2];\n  var b33 = b[12 + 3];\n  dst[ 0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30;\n  dst[ 1] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31;\n  dst[ 2] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32;\n  dst[ 3] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33;\n  dst[ 4] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30;\n  dst[ 5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31;\n  dst[ 6] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32;\n  dst[ 7] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33;\n  dst[ 8] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30;\n  dst[ 9] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31;\n  dst[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32;\n  dst[11] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33;\n  dst[12] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30;\n  dst[13] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31;\n  dst[14] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32;\n  dst[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33;\n  return dst;\n};\n\n/**\n * Multiplies two 4-by-4 matrices; assumes that the given matrices are 4-by-4;\n * assumes matrix entries are accessed in [column][row] fashion.\n * @param {tdl.fast.Matrix4} dst matrix.\n * @param {tdl.fast.Matrix4} a The matrix on the left.\n * @param {tdl.fast.Matrix4} b The matrix on the right.\n * @return {tdl.fast.Matrix4} The matrix product of a and b.\n */\ntdl.fast.columnMajor.mulMatrixMatrix4 = function(dst, a, b) {\n  var a00 = a[0];\n  var a01 = a[1];\n  var a02 = a[2];\n  var a03 = a[3];\n  var a10 = a[ 4 + 0];\n  var a11 = a[ 4 + 1];\n  var a12 = a[ 4 + 2];\n  var a13 = a[ 4 + 3];\n  var a20 = a[ 8 + 0];\n  var a21 = a[ 8 + 1];\n  var a22 = a[ 8 + 2];\n  var a23 = a[ 8 + 3];\n  var a30 = a[12 + 0];\n  var a31 = a[12 + 1];\n  var a32 = a[12 + 2];\n  var a33 = a[12 + 3];\n  var b00 = b[0];\n  var b01 = b[1];\n  var b02 = b[2];\n  var b03 = b[3];\n  var b10 = b[ 4 + 0];\n  var b11 = b[ 4 + 1];\n  var b12 = b[ 4 + 2];\n  var b13 = b[ 4 + 3];\n  var b20 = b[ 8 + 0];\n  var b21 = b[ 8 + 1];\n  var b22 = b[ 8 + 2];\n  var b23 = b[ 8 + 3];\n  var b30 = b[12 + 0];\n  var b31 = b[12 + 1];\n  var b32 = b[12 + 2];\n  var b33 = b[12 + 3];\n  dst[ 0] = a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03;\n  dst[ 1] = a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03;\n  dst[ 2] = a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03;\n  dst[ 3] = a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03;\n  dst[ 4] = a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13;\n  dst[ 5] = a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13;\n  dst[ 6] = a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13;\n  dst[ 7] = a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13;\n  dst[ 8] = a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23;\n  dst[ 9] = a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23;\n  dst[10] = a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23;\n  dst[11] = a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23;\n  dst[12] = a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33;\n  dst[13] = a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33;\n  dst[14] = a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33;\n  dst[15] = a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33;\n  return dst;\n};\n\n/**\n * Multiplies two 4-by-4 matrices; assumes that the given matrices are 4-by-4.\n * @param {tdl.fast.Matrix4} a The matrix on the left.\n * @param {tdl.fast.Matrix4} b The matrix on the right.\n * @return {tdl.fast.Matrix4} The matrix product of a and b.\n */\ntdl.fast.mulMatrixMatrix4 = null;\n\n/**\n * Gets the jth column of the given matrix m; assumes matrix entries are\n * accessed in [row][column] fashion.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Matrix} m The matrix.\n * @param {number} j The index of the desired column.\n * @return {tdl.fast.Vector} The jth column of m as a vector.\n */\ntdl.fast.rowMajor.column4 = function(dst, m, j) {\n  for (var i = 0; i < 4; ++i) {\n    dst[i] = m[i * 4 + j];\n  }\n  return dst;\n};\n\n/**\n * Gets the jth column of the given matrix m; assumes matrix entries are\n * accessed in [column][row] fashion.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Matrix} m The matrix.\n * @param {number} j The index of the desired column.\n * @return {tdl.fast.Vector} The jth column of m as a vector.\n */\ntdl.fast.columnMajor.column4 = function(dst, m, j) {\n  var off = j * 4;\n  dst[0] = m[off + 0];\n  dst[1] = m[off + 1];\n  dst[2] = m[off + 2];\n  dst[3] = m[off + 3];\n  return dst;\n};\n\n/**\n * Gets the jth column of the given matrix m.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Matrix} m The matrix.\n * @param {number} j The index of the desired column.\n * @return {tdl.fast.Vector} The jth column of m as a vector.\n */\ntdl.fast.column4 = null;\n\n/**\n * Gets the ith row of the given matrix m; assumes matrix entries are\n * accessed in [row][column] fashion.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Matrix} m The matrix.\n * @param {number} i The index of the desired row.\n * @return {tdl.fast.Vector} The ith row of m.\n */\ntdl.fast.rowMajor.row4 = function(dst, m, i) {\n  var off = i * 4;\n  dst[0] = m[off + 0];\n  dst[1] = m[off + 1];\n  dst[2] = m[off + 2];\n  dst[3] = m[off + 3];\n  return dst;\n};\n\n/**\n * Gets the ith row of the given matrix m; assumes matrix entries are\n * accessed in [column][row] fashion.\n * @param {tdl.fast.Vector} dst vector.\n * @param {tdl.fast.Matrix} m The matrix.\n * @param {number} i The index of the desired row.\n * @return {tdl.fast.Vector} The ith row of m.\n */\ntdl.fast.columnMajor.row4 = function(dst, m, i) {\n  for (var j = 0; j < 4; ++j) {\n    dst[j] = m[j * 4 + i];\n  }\n  return dst;\n};\n\n/**\n * Gets the ith row of the given matrix m.\n * @param {tdl.fast.Matrix} m The matrix.\n * @param {number} i The index of the desired row.\n * @return {tdl.fast.Vector} The ith row of m.\n */\ntdl.fast.row4 = null;\n\n/**\n * Creates an n-by-n identity matrix.\n *\n * @param {tdl.fast.Matrix} dst matrix.\n * @return {tdl.fast.Matrix} An n-by-n identity matrix.\n */\ntdl.fast.identity4 = function(dst) {\n  dst[ 0] = 1;\n  dst[ 1] = 0;\n  dst[ 2] = 0;\n  dst[ 3] = 0;\n  dst[ 4] = 0;\n  dst[ 5] = 1;\n  dst[ 6] = 0;\n  dst[ 7] = 0;\n  dst[ 8] = 0;\n  dst[ 9] = 0;\n  dst[10] = 1;\n  dst[11] = 0;\n  dst[12] = 0;\n  dst[13] = 0;\n  dst[14] = 0;\n  dst[15] = 1;\n  return dst;\n};\n\n/**\n * Takes the transpose of a matrix.\n * @param {tdl.fast.Matrix} dst matrix.\n * @param {tdl.fast.Matrix} m The matrix.\n * @return {tdl.fast.Matrix} The transpose of m.\n */\ntdl.fast.transpose4 = function(dst, m) {\n  if (dst === m) {\n    var t;\n\n    t = m[1];\n    m[1] = m[4];\n    m[4] = t;\n\n    t = m[2];\n    m[2] = m[8];\n    m[8] = t;\n\n    t = m[3];\n    m[3] = m[12];\n    m[12] = t;\n\n    t = m[6];\n    m[6] = m[9];\n    m[9] = t;\n\n    t = m[7];\n    m[7] = m[13];\n    m[13] = t;\n\n    t = m[11];\n    m[11] = m[14];\n    m[14] = t;\n    return dst;\n  }\n\n  var m00 = m[0 * 4 + 0];\n  var m01 = m[0 * 4 + 1];\n  var m02 = m[0 * 4 + 2];\n  var m03 = m[0 * 4 + 3];\n  var m10 = m[1 * 4 + 0];\n  var m11 = m[1 * 4 + 1];\n  var m12 = m[1 * 4 + 2];\n  var m13 = m[1 * 4 + 3];\n  var m20 = m[2 * 4 + 0];\n  var m21 = m[2 * 4 + 1];\n  var m22 = m[2 * 4 + 2];\n  var m23 = m[2 * 4 + 3];\n  var m30 = m[3 * 4 + 0];\n  var m31 = m[3 * 4 + 1];\n  var m32 = m[3 * 4 + 2];\n  var m33 = m[3 * 4 + 3];\n\n  dst[ 0] = m00;\n  dst[ 1] = m10;\n  dst[ 2] = m20;\n  dst[ 3] = m30;\n  dst[ 4] = m01;\n  dst[ 5] = m11;\n  dst[ 6] = m21;\n  dst[ 7] = m31;\n  dst[ 8] = m02;\n  dst[ 9] = m12;\n  dst[10] = m22;\n  dst[11] = m32;\n  dst[12] = m03;\n  dst[13] = m13;\n  dst[14] = m23;\n  dst[15] = m33;\n  return dst;\n};\n\n/**\n * Computes the inverse of a 4-by-4 matrix.\n * @param {tdl.fast.Matrix4} dst matrix.\n * @param {tdl.fast.Matrix4} m The matrix.\n * @return {tdl.fast.Matrix4} The inverse of m.\n */\ntdl.fast.inverse4 = function(dst, m) {\n  var m00 = m[0 * 4 + 0];\n  var m01 = m[0 * 4 + 1];\n  var m02 = m[0 * 4 + 2];\n  var m03 = m[0 * 4 + 3];\n  var m10 = m[1 * 4 + 0];\n  var m11 = m[1 * 4 + 1];\n  var m12 = m[1 * 4 + 2];\n  var m13 = m[1 * 4 + 3];\n  var m20 = m[2 * 4 + 0];\n  var m21 = m[2 * 4 + 1];\n  var m22 = m[2 * 4 + 2];\n  var m23 = m[2 * 4 + 3];\n  var m30 = m[3 * 4 + 0];\n  var m31 = m[3 * 4 + 1];\n  var m32 = m[3 * 4 + 2];\n  var m33 = m[3 * 4 + 3];\n  var tmp_0  = m22 * m33;\n  var tmp_1  = m32 * m23;\n  var tmp_2  = m12 * m33;\n  var tmp_3  = m32 * m13;\n  var tmp_4  = m12 * m23;\n  var tmp_5  = m22 * m13;\n  var tmp_6  = m02 * m33;\n  var tmp_7  = m32 * m03;\n  var tmp_8  = m02 * m23;\n  var tmp_9  = m22 * m03;\n  var tmp_10 = m02 * m13;\n  var tmp_11 = m12 * m03;\n  var tmp_12 = m20 * m31;\n  var tmp_13 = m30 * m21;\n  var tmp_14 = m10 * m31;\n  var tmp_15 = m30 * m11;\n  var tmp_16 = m10 * m21;\n  var tmp_17 = m20 * m11;\n  var tmp_18 = m00 * m31;\n  var tmp_19 = m30 * m01;\n  var tmp_20 = m00 * m21;\n  var tmp_21 = m20 * m01;\n  var tmp_22 = m00 * m11;\n  var tmp_23 = m10 * m01;\n\n  var t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) -\n      (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31);\n  var t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) -\n      (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31);\n  var t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) -\n      (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31);\n  var t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) -\n      (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21);\n\n  var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);\n\n  dst[ 0] = d * t0;\n  dst[ 1] = d * t1;\n  dst[ 2] = d * t2;\n  dst[ 3] = d * t3;\n  dst[ 4] = d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) -\n          (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30));\n  dst[ 5] = d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) -\n          (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30));\n  dst[ 6] = d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) -\n          (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30));\n  dst[ 7] = d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) -\n          (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20));\n  dst[ 8] = d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) -\n          (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33));\n  dst[ 9] = d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) -\n          (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33));\n  dst[10] = d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) -\n          (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33));\n  dst[11] = d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) -\n          (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23));\n  dst[12] = d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) -\n          (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22));\n  dst[13] = d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) -\n          (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02));\n  dst[14] = d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) -\n          (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12));\n  dst[15] = d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) -\n          (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02));\n  return dst;\n};\n\n/**\n * Computes the inverse of a 4-by-4 matrix.\n * Note: It is faster to call this than tdl.fast.inverse.\n * @param {tdl.fast.Matrix4} m The matrix.\n * @return {tdl.fast.Matrix4} The inverse of m.\n */\ntdl.fast.matrix4.inverse = function(dst,m) {\n  return tdl.fast.inverse4(dst,m);\n};\n\n/**\n * Multiplies two 4-by-4 matrices; assumes that the given matrices are 4-by-4.\n * Note: It is faster to call this than tdl.fast.mul.\n * @param {tdl.fast.Matrix4} a The matrix on the left.\n * @param {tdl.fast.Matrix4} b The matrix on the right.\n * @return {tdl.fast.Matrix4} The matrix product of a and b.\n */\ntdl.fast.matrix4.mul = function(dst, a, b) {\n  return tdl.fast.mulMatrixMatrix4(dst, a, b);\n};\n\n/**\n * Copies a Matrix4.\n * Note: It is faster to call this than tdl.fast.copy.\n * @param {tdl.fast.Matrix4} m The matrix.\n * @return {tdl.fast.Matrix4} A copy of m.\n */\ntdl.fast.matrix4.copy = function(dst, m) {\n  return tdl.fast.copyMatrix(dst, m);\n};\n\n/**\n * Sets the translation component of a 4-by-4 matrix to the given\n * vector.\n * @param {tdl.fast.Matrix4} a The matrix.\n * @param {(tdl.fast.Vector3|tdl.fast.Vector4)} v The vector.\n * @return {tdl.fast.Matrix4} a once modified.\n */\ntdl.fast.matrix4.setTranslation = function(a, v) {\n  a[12] = v[0];\n  a[13] = v[1];\n  a[14] = v[2];\n  a[15] = 1;\n  return a;\n};\n\n/**\n * Returns the translation component of a 4-by-4 matrix as a vector with 3\n * entries.\n * @return {tdl.fast.Vector3} dst vector..\n * @param {tdl.fast.Matrix4} m The matrix.\n * @return {tdl.fast.Vector3} The translation component of m.\n */\ntdl.fast.matrix4.getTranslation = function(dst, m) {\n  dst[0] = m[12];\n  dst[1] = m[13];\n  dst[2] = m[14];\n  return dst;\n};\n\n/**\n * Creates a 4-by-4 identity matrix.\n * @param {tdl.fast.Matrix4} dst matrix.\n * @return {tdl.fast.Matrix4} The 4-by-4 identity.\n */\ntdl.fast.matrix4.identity = function(dst) {\n  return tdl.fast.identity4(dst);\n};\n\ntdl.fast.matrix4.getAxis = function(dst, m, axis) {\n  var off = axis * 4;\n  dst[0] = m[off + 0];\n  dst[1] = m[off + 1];\n  dst[2] = m[off + 2];\n  return dst;\n};\n\n/**\n * Computes a 4-by-4 perspective transformation matrix given the angular height\n * of the frustum, the aspect ratio, and the near and far clipping planes.  The\n * arguments define a frustum extending in the negative z direction.  The given\n * angle is the vertical angle of the frustum, and the horizontal angle is\n * determined to produce the given aspect ratio.  The arguments near and far are\n * the distances to the near and far clipping planes.  Note that near and far\n * are not z coordinates, but rather they are distances along the negative\n * z-axis.  The matrix generated sends the viewing frustum to the unit box.\n * We assume a unit box extending from -1 to 1 in the x and y dimensions and\n * from 0 to 1 in the z dimension.\n * @param {tdl.fast.Matrix4} dst matrix.\n * @param {number} angle The camera angle from top to bottom (in radians).\n * @param {number} aspect The aspect ratio width / height.\n * @param {number} zNear The depth (negative z coordinate)\n *     of the near clipping plane.\n * @param {number} zFar The depth (negative z coordinate)\n *     of the far clipping plane.\n * @return {tdl.fast.Matrix4} The perspective matrix.\n */\ntdl.fast.matrix4.perspective = function(dst, angle, aspect, zNear, zFar) {\n  var f = Math.tan(Math.PI * 0.5 - 0.5 * angle);\n  var rangeInv = 1.0 / (zNear - zFar);\n\n  dst[0]  = f / aspect;\n  dst[1]  = 0;\n  dst[2]  = 0;\n  dst[3]  = 0;\n\n  dst[4]  = 0;\n  dst[5]  = f;\n  dst[6]  = 0;\n  dst[7]  = 0;\n\n  dst[8]  = 0;\n  dst[9]  = 0;\n  dst[10] = (zNear + zFar) * rangeInv;\n  dst[11] = -1;\n\n  dst[12] = 0;\n  dst[13] = 0;\n  dst[14] = zNear * zFar * rangeInv * 2;\n  dst[15] = 0;\n\n  return dst;\n};\n\n\n/**\n * Computes a 4-by-4 othogonal transformation matrix given the left, right,\n * bottom, and top dimensions of the near clipping plane as well as the\n * near and far clipping plane distances.\n * @param {tdl.fast.Matrix4} dst Output matrix.\n * @param {number} left Left side of the near clipping plane viewport.\n * @param {number} right Right side of the near clipping plane viewport.\n * @param {number} top Top of the near clipping plane viewport.\n * @param {number} bottom Bottom of the near clipping plane viewport.\n * @param {number} near The depth (negative z coordinate)\n *     of the near clipping plane.\n * @param {number} far The depth (negative z coordinate)\n *     of the far clipping plane.\n * @return {tdl.fast.Matrix4} The perspective matrix.\n */\ntdl.fast.matrix4.ortho = function(dst, left, right, bottom, top, near, far) {\n\n\n  dst[0]  = 2 / (right - left);\n  dst[1]  = 0;\n  dst[2]  = 0;\n  dst[3]  = 0;\n\n  dst[4]  = 0;\n  dst[5]  = 2 / (top - bottom);\n  dst[6]  = 0;\n  dst[7]  = 0;\n\n  dst[8]  = 0;\n  dst[9]  = 0;\n  dst[10] = -1 / (far - near);\n  dst[11] = 0;\n\n  dst[12] = (right + left) / (left - right);\n  dst[13] = (top + bottom) / (bottom - top);\n  dst[14] = -near / (near - far);\n  dst[15] = 1;\n\n  return dst;\n}\n\n/**\n * Computes a 4-by-4 perspective transformation matrix given the left, right,\n * top, bottom, near and far clipping planes. The arguments define a frustum\n * extending in the negative z direction. The arguments near and far are the\n * distances to the near and far clipping planes. Note that near and far are not\n * z coordinates, but rather they are distances along the negative z-axis. The\n * matrix generated sends the viewing frustum to the unit box. We assume a unit\n * box extending from -1 to 1 in the x and y dimensions and from 0 to 1 in the z\n * dimension.\n * @param {number} left The x coordinate of the left plane of the box.\n * @param {number} right The x coordinate of the right plane of the box.\n * @param {number} bottom The y coordinate of the bottom plane of the box.\n * @param {number} top The y coordinate of the right plane of the box.\n * @param {number} near The negative z coordinate of the near plane of the box.\n * @param {number} far The negative z coordinate of the far plane of the box.\n * @return {tdl.fast.Matrix4} The perspective projection matrix.\n */\ntdl.fast.matrix4.frustum = function(dst, left, right, bottom, top, near, far) {\n  var dx = (right - left);\n  var dy = (top - bottom);\n  var dz = (near - far);\n\n  dst[ 0] = 2 * near / dx;\n  dst[ 1] = 0;\n  dst[ 2] = 0;\n  dst[ 3] = 0;\n  dst[ 4] = 0;\n  dst[ 5] = 2 * near / dy;\n  dst[ 6] = 0;\n  dst[ 7] = 0;\n  dst[ 8] = (left + right) / dx;\n  dst[ 9] = (top + bottom) / dy;\n  dst[10] = far / dz;\n  dst[11] = -1;\n  dst[12] = 0;\n  dst[13] = 0;\n  dst[14] = near * far / dz;\n  dst[15] = 0;\n\n  return dst;\n};\n\n/**\n * Computes a 4-by-4 look-at transformation.  The transformation generated is\n * an orthogonal rotation matrix with translation component.  The translation\n * component sends the eye to the origin.  The rotation component sends the\n * vector pointing from the eye to the target to a vector pointing in the\n * negative z direction, and also sends the up vector into the upper half of\n * the yz plane.\n * @param {tdl.fast.Matrix4} dst matrix.\n * @param {tdl.fast.Vector3} eye The\n *     position of the eye.\n * @param {tdl.fast.Vector3} target The\n *     position meant to be viewed.\n * @param {tdl.fast.Vector3} up A vector\n *     pointing up.\n * @return {tdl.fast.Matrix4} The look-at matrix.\n */\ntdl.fast.matrix4.lookAt = function(dst, eye, target, up) {\n  var t0 = tdl.fast.temp0v3_;\n  var t1 = tdl.fast.temp1v3_;\n  var t2 = tdl.fast.temp2v3_;\n\n  var vz = tdl.fast.normalize(t0, tdl.fast.subVector(t0, eye, target));\n  var vx = tdl.fast.normalize(t1, tdl.fast.cross(t1, up, vz));\n  var vy = tdl.fast.cross(t2, vz, vx);\n\n  dst[ 0] = vx[0];\n  dst[ 1] = vy[0];\n  dst[ 2] = vz[0];\n  dst[ 3] = 0;\n  dst[ 4] = vx[1];\n  dst[ 5] = vy[1];\n  dst[ 6] = vz[1];\n  dst[ 7] = 0;\n  dst[ 8] = vx[2];\n  dst[ 9] = vy[2];\n  dst[10] = vz[2];\n  dst[11] = 0;\n  dst[12] = -tdl.fast.dot(vx, eye);\n  dst[13] = -tdl.fast.dot(vy, eye);\n  dst[14] = -tdl.fast.dot(vz, eye);\n  dst[15] = 1;\n\n  return dst;\n};\n\n/**\n * Computes a 4-by-4 camera look-at transformation. This is the\n * inverse of lookAt The transformation generated is an\n * orthogonal rotation matrix with translation component.\n * @param {(tdl.fast.Vector3|tdl.fast.Vector4)} eye The position\n *     of the eye.\n * @param {(tdl.fast.Vector3|tdl.fast.Vector4)} target The\n *     position meant to be viewed.\n * @param {(tdl.fast.Vector3|tdl.fast.Vector4)} up A vector\n *     pointing up.\n * @return {tdl.fast.Matrix4} The camera look-at matrix.\n */\ntdl.fast.matrix4.cameraLookAt = function(dst, eye, target, up) {\n  var t0 = tdl.fast.temp0v3_;\n  var t1 = tdl.fast.temp1v3_;\n  var t2 = tdl.fast.temp2v3_;\n\n  var vz = tdl.fast.normalize(t0, tdl.fast.subVector(t0, eye, target));\n  var vx = tdl.fast.normalize(t1, tdl.fast.cross(t1, up, vz));\n  var vy = tdl.fast.cross(t2, vz, vx);\n\n  dst[ 0] = vx[0];\n  dst[ 1] = vx[1];\n  dst[ 2] = vx[2];\n  dst[ 3] = 0;\n  dst[ 4] = vy[0];\n  dst[ 5] = vy[1];\n  dst[ 6] = vy[2];\n  dst[ 7] = 0;\n  dst[ 8] = vz[0];\n  dst[ 9] = vz[1];\n  dst[10] = vz[2];\n  dst[11] = 0;\n  dst[12] = eye[0];\n  dst[13] = eye[1];\n  dst[14] = eye[2];\n  dst[15] = 1;\n\n  return dst;\n};\n\n/**\n * Creates a 4-by-4 matrix which translates by the given vector v.\n * @param {(tdl.fast.Vector3|tdl.fast.Vector4)} v The vector by\n *     which to translate.\n * @return {tdl.fast.Matrix4} The translation matrix.\n */\ntdl.fast.matrix4.translation = function(dst, v) {\n  dst[ 0] = 1;\n  dst[ 1] = 0;\n  dst[ 2] = 0;\n  dst[ 3] = 0;\n  dst[ 4] = 0;\n  dst[ 5] = 1;\n  dst[ 6] = 0;\n  dst[ 7] = 0;\n  dst[ 8] = 0;\n  dst[ 9] = 0;\n  dst[10] = 1;\n  dst[11] = 0;\n  dst[12] = v[0];\n  dst[13] = v[1];\n  dst[14] = v[2];\n  dst[15] = 1;\n  return dst;\n};\n\n/**\n * Modifies the given 4-by-4 matrix by translation by the given vector v.\n * @param {tdl.fast.Matrix4} m The matrix.\n * @param {(tdl.fast.Vector3|tdl.fast.Vector4)} v The vector by\n *     which to translate.\n * @return {tdl.fast.Matrix4} m once modified.\n */\ntdl.fast.matrix4.translate = function(m, v) {\n  var v0 = v[0];\n  var v1 = v[1];\n  var v2 = v[2];\n  var m00 = m[0];\n  var m01 = m[1];\n  var m02 = m[2];\n  var m03 = m[3];\n  var m10 = m[1 * 4 + 0];\n  var m11 = m[1 * 4 + 1];\n  var m12 = m[1 * 4 + 2];\n  var m13 = m[1 * 4 + 3];\n  var m20 = m[2 * 4 + 0];\n  var m21 = m[2 * 4 + 1];\n  var m22 = m[2 * 4 + 2];\n  var m23 = m[2 * 4 + 3];\n  var m30 = m[3 * 4 + 0];\n  var m31 = m[3 * 4 + 1];\n  var m32 = m[3 * 4 + 2];\n  var m33 = m[3 * 4 + 3];\n\n  m[12] = m00 * v0 + m10 * v1 + m20 * v2 + m30;\n  m[13] = m01 * v0 + m11 * v1 + m21 * v2 + m31;\n  m[14] = m02 * v0 + m12 * v1 + m22 * v2 + m32;\n  m[15] = m03 * v0 + m13 * v1 + m23 * v2 + m33;\n\n  return m;\n};\n\ntdl.fast.matrix4.transpose = tdl.fast.transpose4;\n\n/**\n * Creates a 4-by-4 matrix which rotates around the x-axis by the given angle.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.fast.Matrix4} The rotation matrix.\n */\ntdl.fast.matrix4.rotationX = function(dst, angle) {\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n\n  dst[ 0] = 1;\n  dst[ 1] = 0;\n  dst[ 2] = 0;\n  dst[ 3] = 0;\n  dst[ 4] = 0;\n  dst[ 5] = c;\n  dst[ 6] = s;\n  dst[ 7] = 0;\n  dst[ 8] = 0;\n  dst[ 9] = -s;\n  dst[10] = c;\n  dst[11] = 0;\n  dst[12] = 0;\n  dst[13] = 0;\n  dst[14] = 0;\n  dst[15] = 1;\n\n  return dst;\n};\n\n/**\n * Modifies the given 4-by-4 matrix by a rotation around the x-axis by the given\n * angle.\n * @param {tdl.fast.Matrix4} m The matrix.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.fast.Matrix4} m once modified.\n */\ntdl.fast.matrix4.rotateX = function(m, angle) {\n  var m10 = m[4];\n  var m11 = m[5];\n  var m12 = m[6];\n  var m13 = m[7];\n  var m20 = m[8];\n  var m21 = m[9];\n  var m22 = m[10];\n  var m23 = m[11];\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n\n  m[4]  = c * m10 + s * m20;\n  m[5]  = c * m11 + s * m21;\n  m[6]  = c * m12 + s * m22;\n  m[7]  = c * m13 + s * m23;\n  m[8]  = c * m20 - s * m10;\n  m[9]  = c * m21 - s * m11;\n  m[10] = c * m22 - s * m12;\n  m[11] = c * m23 - s * m13;\n\n  return m;\n};\n\n/**\n * Creates a 4-by-4 matrix which rotates around the y-axis by the given angle.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.fast.Matrix4} The rotation matrix.\n */\ntdl.fast.matrix4.rotationY = function(dst, angle) {\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n\n  dst[ 0] = c;\n  dst[ 1] = 0;\n  dst[ 2] = -s;\n  dst[ 3] = 0;\n  dst[ 4] = 0;\n  dst[ 5] = 1;\n  dst[ 6] = 0;\n  dst[ 7] = 0;\n  dst[ 8] = s;\n  dst[ 9] = 0;\n  dst[10] = c;\n  dst[11] = 0;\n  dst[12] = 0;\n  dst[13] = 0;\n  dst[14] = 0;\n  dst[15] = 1;\n\n  return dst;\n};\n\n/**\n * Modifies the given 4-by-4 matrix by a rotation around the y-axis by the given\n * angle.\n * @param {tdl.fast.Matrix4} m The matrix.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.fast.Matrix4} m once modified.\n */\ntdl.fast.matrix4.rotateY = function(m, angle) {\n  var m00 = m[0*4+0];\n  var m01 = m[0*4+1];\n  var m02 = m[0*4+2];\n  var m03 = m[0*4+3];\n  var m20 = m[2*4+0];\n  var m21 = m[2*4+1];\n  var m22 = m[2*4+2];\n  var m23 = m[2*4+3];\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n\n  m[ 0] = c * m00 - s * m20;\n  m[ 1] = c * m01 - s * m21;\n  m[ 2] = c * m02 - s * m22;\n  m[ 3] = c * m03 - s * m23;\n  m[ 8] = c * m20 + s * m00;\n  m[ 9] = c * m21 + s * m01;\n  m[10] = c * m22 + s * m02;\n  m[11] = c * m23 + s * m03;\n\n  return m;\n};\n\n/**\n * Creates a 4-by-4 matrix which rotates around the z-axis by the given angle.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.fast.Matrix4} The rotation matrix.\n */\ntdl.fast.matrix4.rotationZ = function(dst, angle) {\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n\n  dst[ 0] = c;\n  dst[ 1] = s;\n  dst[ 2] = 0;\n  dst[ 3] = 0;\n  dst[ 4] = -s;\n  dst[ 5] = c;\n  dst[ 6] = 0;\n  dst[ 7] = 0;\n  dst[ 8] = 0;\n  dst[ 9] = 0;\n  dst[10] = 1;\n  dst[11] = 0;\n  dst[12] = 0;\n  dst[13] = 0;\n  dst[14] = 0;\n  dst[15] = 1;\n\n  return dst;\n};\n\n/**\n * Modifies the given 4-by-4 matrix by a rotation around the z-axis by the given\n * angle.\n * @param {tdl.fast.Matrix4} m The matrix.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.fast.Matrix4} m once modified.\n */\ntdl.fast.matrix4.rotateZ = function(m, angle) {\n  var m00 = m[0*4+0];\n  var m01 = m[0*4+1];\n  var m02 = m[0*4+2];\n  var m03 = m[0*4+3];\n  var m10 = m[1*4+0];\n  var m11 = m[1*4+1];\n  var m12 = m[1*4+2];\n  var m13 = m[1*4+3];\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n\n  m[ 0] = c * m00 + s * m10;\n  m[ 1] = c * m01 + s * m11;\n  m[ 2] = c * m02 + s * m12;\n  m[ 3] = c * m03 + s * m13;\n  m[ 4] = c * m10 - s * m00;\n  m[ 5] = c * m11 - s * m01;\n  m[ 6] = c * m12 - s * m02;\n  m[ 7] = c * m13 - s * m03;\n\n  return m;\n};\n\n/**\n * Creates a 4-by-4 matrix which rotates around the given axis by the given\n * angle.\n * @param {(tdl.fast.Vector3|tdl.fast.Vector4)} axis The axis\n *     about which to rotate.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.fast.Matrix4} A matrix which rotates angle radians\n *     around the axis.\n */\ntdl.fast.matrix4.axisRotation = function(dst, axis, angle) {\n  var x = axis[0];\n  var y = axis[1];\n  var z = axis[2];\n  var n = Math.sqrt(x * x + y * y + z * z);\n  x /= n;\n  y /= n;\n  z /= n;\n  var xx = x * x;\n  var yy = y * y;\n  var zz = z * z;\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n  var oneMinusCosine = 1 - c;\n\n  dst[ 0] = xx + (1 - xx) * c;\n  dst[ 1] = x * y * oneMinusCosine + z * s;\n  dst[ 2] = x * z * oneMinusCosine - y * s;\n  dst[ 3] = 0;\n  dst[ 4] = x * y * oneMinusCosine - z * s;\n  dst[ 5] = yy + (1 - yy) * c;\n  dst[ 6] = y * z * oneMinusCosine + x * s;\n  dst[ 7] = 0;\n  dst[ 8] = x * z * oneMinusCosine + y * s;\n  dst[ 9] = y * z * oneMinusCosine - x * s;\n  dst[10] = zz + (1 - zz) * c;\n  dst[11] = 0;\n  dst[12] = 0;\n  dst[13] = 0;\n  dst[14] = 0;\n  dst[15] = 1;\n\n  return dst;\n};\n\n/**\n * Modifies the given 4-by-4 matrix by rotation around the given axis by the\n * given angle.\n * @param {tdl.fast.Matrix4} m The matrix.\n * @param {(tdl.fast.Vector3|tdl.fast.Vector4)} axis The axis\n *     about which to rotate.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.fast.Matrix4} m once modified.\n */\ntdl.fast.matrix4.axisRotate = function(m, axis, angle) {\n  var x = axis[0];\n  var y = axis[1];\n  var z = axis[2];\n  var n = Math.sqrt(x * x + y * y + z * z);\n  x /= n;\n  y /= n;\n  z /= n;\n  var xx = x * x;\n  var yy = y * y;\n  var zz = z * z;\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n  var oneMinusCosine = 1 - c;\n\n  var r00 = xx + (1 - xx) * c;\n  var r01 = x * y * oneMinusCosine + z * s;\n  var r02 = x * z * oneMinusCosine - y * s;\n  var r10 = x * y * oneMinusCosine - z * s;\n  var r11 = yy + (1 - yy) * c;\n  var r12 = y * z * oneMinusCosine + x * s;\n  var r20 = x * z * oneMinusCosine + y * s;\n  var r21 = y * z * oneMinusCosine - x * s;\n  var r22 = zz + (1 - zz) * c;\n\n  var m00 = m[0];\n  var m01 = m[1];\n  var m02 = m[2];\n  var m03 = m[3];\n  var m10 = m[4];\n  var m11 = m[5];\n  var m12 = m[6];\n  var m13 = m[7];\n  var m20 = m[8];\n  var m21 = m[9];\n  var m22 = m[10];\n  var m23 = m[11];\n  var m30 = m[12];\n  var m31 = m[13];\n  var m32 = m[14];\n  var m33 = m[15];\n\n  m[ 0] = r00 * m00 + r01 * m10 + r02 * m20;\n  m[ 1] = r00 * m01 + r01 * m11 + r02 * m21;\n  m[ 2] = r00 * m02 + r01 * m12 + r02 * m22;\n  m[ 3] = r00 * m03 + r01 * m13 + r02 * m23;\n  m[ 4] = r10 * m00 + r11 * m10 + r12 * m20;\n  m[ 5] = r10 * m01 + r11 * m11 + r12 * m21;\n  m[ 6] = r10 * m02 + r11 * m12 + r12 * m22;\n  m[ 7] = r10 * m03 + r11 * m13 + r12 * m23;\n  m[ 8] = r20 * m00 + r21 * m10 + r22 * m20;\n  m[ 9] = r20 * m01 + r21 * m11 + r22 * m21;\n  m[10] = r20 * m02 + r21 * m12 + r22 * m22;\n  m[11] = r20 * m03 + r21 * m13 + r22 * m23;\n\n  return m;\n};\n\n/**\n * Creates a 4-by-4 matrix which scales in each dimension by an amount given by\n * the corresponding entry in the given vector; assumes the vector has three\n * entries.\n * @param {tdl.fast.Vector3} v A vector of\n *     three entries specifying the factor by which to scale in each dimension.\n * @return {tdl.fast.Matrix4} The scaling matrix.\n */\ntdl.fast.matrix4.scaling = function(dst, v) {\n  dst[ 0] = v[0];\n  dst[ 1] = 0;\n  dst[ 2] = 0;\n  dst[ 3] = 0;\n  dst[ 4] = 0;\n  dst[ 5] = v[1];\n  dst[ 6] = 0;\n  dst[ 7] = 0;\n  dst[ 8] = 0;\n  dst[ 9] = 0;\n  dst[10] = v[2];\n  dst[11] = 0;\n  dst[12] = 0;\n  dst[13] = 0;\n  dst[14] = 0;\n  dst[15] = 1;\n  return dst;\n};\n\n/**\n * Modifies the given 4-by-4 matrix, scaling in each dimension by an amount\n * given by the corresponding entry in the given vector; assumes the vector has\n * three entries.\n * @param {tdl.fast.Matrix4} m The matrix to be modified.\n * @param {tdl.fast.Vector3} v A vector of three entries specifying the\n *     factor by which to scale in each dimension.\n * @return {tdl.fast.Matrix4} m once modified.\n */\ntdl.fast.matrix4.scale = function(m, v) {\n  var v0 = v[0];\n  var v1 = v[1];\n  var v2 = v[2];\n\n  m[0] = v0 * m[0*4+0];\n  m[1] = v0 * m[0*4+1];\n  m[2] = v0 * m[0*4+2];\n  m[3] = v0 * m[0*4+3];\n  m[4] = v1 * m[1*4+0];\n  m[5] = v1 * m[1*4+1];\n  m[6] = v1 * m[1*4+2];\n  m[7] = v1 * m[1*4+3];\n  m[8] = v2 * m[2*4+0];\n  m[9] = v2 * m[2*4+1];\n  m[10] = v2 * m[2*4+2];\n  m[11] = v2 * m[2*4+3];\n\n  return m;\n};\n\n/**\n * Sets each function in the namespace tdl.fast to the row major\n * version in tdl.fast.rowMajor (provided such a function exists in\n * tdl.fast.rowMajor).  Call this function to establish the row major\n * convention.\n */\ntdl.fast.installRowMajorFunctions = function() {\n  for (var f in tdl.fast.rowMajor) {\n    tdl.fast[f] = tdl.fast.rowMajor[f];\n  }\n};\n\n/**\n * Sets each function in the namespace tdl.fast to the column major\n * version in tdl.fast.columnMajor (provided such a function exists in\n * tdl.fast.columnMajor).  Call this function to establish the column\n * major convention.\n */\ntdl.fast.installColumnMajorFunctions = function() {\n  for (var f in tdl.fast.columnMajor) {\n    tdl.fast[f] = tdl.fast.columnMajor[f];\n  }\n};\n\n// By default, install the row-major functions.\ntdl.fast.installRowMajorFunctions();\n\nreturn tdl.fast;\n});\n"
  },
  {
    "path": "tdl/fps.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains objects to measure frames\n *               per second.\n */\ndefine(['./base-rs'], function(BaseRS) {\n\ntdl.provide('tdl.fps');\n/**\n * A module for fps.\n * @namespace\n */\ntdl.fps = tdl.fps || {};\n\n/**\n * Number of frames to average over for computing FPS.\n * @type {number}\n */\ntdl.fps.NUM_FRAMES_TO_AVERAGE = 16;\n\n/**\n * Measures frames per second.\n * @constructor\n */\ntdl.fps.FPSTimer = function() {\n  // total time spent for last N frames.\n  this.totalTime_ = tdl.fps.NUM_FRAMES_TO_AVERAGE;\n\n  // elapsed time for last N frames.\n  this.timeTable_ = [];\n\n  // where to record next elapsed time.\n  this.timeTableCursor_ = 0;\n\n  // Initialize the FPS elapsed time history table.\n  for (var tt = 0; tt < tdl.fps.NUM_FRAMES_TO_AVERAGE; ++tt) {\n    this.timeTable_[tt] = 1.0;\n  }\n\n  /**\n   * The instantaneous FPS\n   * @type {number}\n   */\n  this.instantaneousFPS = 0;\n  /**\n   * The average FPS\n   * @type {number}\n   */\n  this.averageFPS = 0;\n};\n\n/**\n * Updates the fps measurement. You must call this in your\n * render loop.\n *\n * @param {number} elapsedTime The elasped time in seconds\n *     since the last frame.\n */\ntdl.fps.FPSTimer.prototype.update = function(elapsedTime) {\n  // Keep the total time and total active time for the last N frames.\n  this.totalTime_ += elapsedTime - this.timeTable_[this.timeTableCursor_];\n\n  // Save off the elapsed time for this frame so we can subtract it later.\n  this.timeTable_[this.timeTableCursor_] = elapsedTime;\n\n  // Wrap the place to store the next time sample.\n  ++this.timeTableCursor_;\n  if (this.timeTableCursor_ == tdl.fps.NUM_FRAMES_TO_AVERAGE) {\n    this.timeTableCursor_ = 0;\n  }\n\n  this.instantaneousFPS = Math.floor(1.0 / elapsedTime + 0.5);\n  this.averageFPS = Math.floor(\n      (1.0 / (this.totalTime_ / tdl.fps.NUM_FRAMES_TO_AVERAGE)) + 0.5);\n};\n\nreturn tdl.fps;\n});\n\n"
  },
  {
    "path": "tdl/framebuffers.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains objects to manage\n *               framebuffers.\n */\ndefine(['./base-rs', './textures'], function(BaseRS, Textures) {\n\ntdl.provide('tdl.framebuffers');\n/**\n * A module for textures.\n * @namespace\n */\ntdl.framebuffers = tdl.framebuffers || {};\n\n/**\n * Creates a framebuffer\n * @param {number} width width of framebuffer.\n * @param {number} height height of framebuffer.\n * @param {boolean?} opt_depth true = make a depth attachment\n * @return {tdl.Framebuffer} the created framebuffer.\n */\ntdl.framebuffers.createFramebuffer = function(width, height, opt_depth) {\n  return new tdl.framebuffers.Framebuffer(width, height, opt_depth);\n};\n\n/**\n * Creates a cubemap framebuffer\n * @param {number} size size of edge of cube.\n * @param {boolean?} opt_depth true = make a depth attachment\n * @return {tdl.CubeFramebuffer} the created framebuffer.\n */\ntdl.framebuffers.createCubeFramebuffer = function(size, opt_depth) {\n  return new tdl.framebuffers.CubeFramebuffer(size, opt_depth);\n};\n\n/**\n * A class to represent the backbuffer (the canvas)\n * @constructor\n */\ntdl.framebuffers.BackBuffer = function() {\n  this.depth = true;\n  this.buffer = null;\n};\n\n/**\n * Binds the backbuffer as the current render target.\n */\ntdl.framebuffers.BackBuffer.prototype.bind = function() {\n    gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n    gl.viewport(0, 0, this.width, this.height);\n};\n\nif (Object.prototype.__defineSetter__) {\ntdl.framebuffers.BackBuffer.prototype.__defineGetter__(\n    'width',\n    function () {\n      return gl.drawingBufferWidth || gl.canvas.width;\n    }\n);\n\ntdl.framebuffers.BackBuffer.prototype.__defineGetter__(\n    'height',\n    function () {\n      return gl.drawingBufferHeight || gl.canvas.height;\n    }\n);\n}\n\n/**\n * Get a FrameBuffer for the backbuffer.\n * Use this where you need to pass in a framebuffer, but you really\n * mean the backbuffer, so that binding it works as expected.\n * @return {tdl.BackBuffer} the created BackBuffer.\n */\ntdl.framebuffers.getBackBuffer = function() {\n  return new tdl.framebuffers.BackBuffer();\n};\n\n/**\n * Represnets a WebGLFramebuffer\n * @constructor\n * @param {number} width width of framebuffer.\n * @param {number} height height of framebuffer.\n * @param {boolean?} opt_depth true = create a depth attachment\n */\ntdl.framebuffers.Framebuffer = function(width, height, opt_depth) {\n  this.width = width;\n  this.height = height;\n  this.depth = opt_depth;\n  var tex = new tdl.textures.SolidTexture([0,0,0,0]);\n  this.initializeTexture(tex);\n\n  var fb = gl.createFramebuffer();\n  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);\n  gl.framebufferTexture2D(\n      gl.FRAMEBUFFER,\n      gl.COLOR_ATTACHMENT0,\n      gl.TEXTURE_2D,\n      tex.texture,\n      0);\n\n  if (this.depth) {\n    if (gl.tdl.depthTexture) {\n      var dt = new tdl.textures.DepthTexture(this.width, this.height);\n      gl.framebufferTexture2D(\n          gl.FRAMEBUFFER,\n          gl.DEPTH_ATTACHMENT,\n          gl.TEXTURE_2D,\n          dt.texture,\n          0);\n      this.depthTexture = dt;\n    } else {\n      var db = gl.createRenderbuffer();\n      gl.bindRenderbuffer(gl.RENDERBUFFER, db);\n      gl.renderbufferStorage(\n          gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);\n      gl.framebufferRenderbuffer(\n          gl.FRAMEBUFFER,\n          gl.DEPTH_ATTACHMENT,\n          gl.RENDERBUFFER,\n          db);\n      gl.bindRenderbuffer(gl.RENDERBUFFER, null);\n      this.depthRenderbuffer = db;\n    }\n  }\n\n  var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);\n  if (status != gl.FRAMEBUFFER_COMPLETE && !gl.isContextLost()) {\n    throw(\"gl.checkFramebufferStatus() returned \" +\n          tdl.webgl.glEnumToString(status));\n  }\n  this.framebuffer = fb;\n  this.texture = tex;\n  gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n};\n\n/**\n * Bind this framebuffer as the current render target.\n */\ntdl.framebuffers.Framebuffer.prototype.bind = function() {\n  gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n  gl.viewport(0, 0, this.width, this.height);\n};\n\n/**\n * Unbinds this framebuffer as the current render target\n */\ntdl.framebuffers.Framebuffer.prototype.unbind = function() {\n  gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n  gl.viewport(\n      0, 0,\n      gl.drawingBufferWidth || gl.canvas.width,\n      gl.drawingBufferHeight || gl.canvas.height);\n};\n\ntdl.framebuffers.Framebuffer.prototype.initializeTexture = function(tex) {\n  gl.bindTexture(gl.TEXTURE_2D, tex.texture);\n  tex.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n  tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n  tex.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n  tex.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n  gl.texImage2D(gl.TEXTURE_2D,\n                0,                 // level\n                gl.RGBA,           // internalFormat\n                this.width,        // width\n                this.height,       // height\n                0,                 // border\n                gl.RGBA,           // format\n                gl.UNSIGNED_BYTE,  // type\n                null);             // data\n};\n\n/**\n * Represnents a Cube Map framebuffer\n * @constructor\n * @param {number} size size of edge of cube.\n * @param {boolean?} opt_depth true = make a depth attachment\n */\ntdl.framebuffers.CubeFramebuffer = function(size, opt_depth) {\n  this.size = size;\n  this.depth = opt_depth;\n  var tex = new tdl.textures.CubeMap(this.size);\n  gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex.texture);\n  tex.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n  tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n  tex.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n  tex.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n  for (var ff = 0; ff < 6; ++ff) {\n    gl.texImage2D(tdl.textures.CubeMap.faceTargets[ff],\n                  0,                 // level\n                  gl.RGBA,           // internalFormat\n                  this.size,         // width\n                  this.size,         // height\n                  0,                 // border\n                  gl.RGBA,           // format\n                  gl.UNSIGNED_BYTE,  // type\n                  null);             // data\n  }\n  if (this.depth) {\n    var db = gl.createRenderbuffer();\n    gl.bindRenderbuffer(gl.RENDERBUFFER, db);\n    gl.renderbufferStorage(\n        gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.size, this.size);\n  }\n  this.framebuffers = [];\n  for (var ff = 0; ff < 6; ++ff) {\n    var fb = gl.createFramebuffer();\n    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);\n    gl.framebufferTexture2D(\n        gl.FRAMEBUFFER,\n        gl.COLOR_ATTACHMENT0,\n        tdl.textures.CubeMap.faceTargets[ff],\n        tex.texture,\n        0);\n    if (this.depth) {\n      gl.framebufferRenderbuffer(\n          gl.FRAMEBUFFER,\n          gl.DEPTH_ATTACHMENT,\n          gl.RENDERBUFFER,\n          db);\n    }\n    var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);\n    if (status != gl.FRAMEBUFFER_COMPLETE) {\n      throw(\"gl.checkFramebufferStatus() returned \" + WebGLDebugUtils.glEnumToString(status));\n    }\n    this.framebuffers.push(fb);\n  }\n  gl.bindRenderbuffer(gl.RENDERBUFFER, null);\n  this.texture = tex;\n};\n\n/**\n * Binds a face as the current render target.\n * @param {number} face The face to use as the render target.\n */\ntdl.framebuffers.CubeFramebuffer.prototype.bind = function(face) {\n  gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[face]);\n  gl.viewport(0, 0, this.size, this.size);\n};\n\n/**\n * Unbinds this framebuffer as the current render target.\n */\ntdl.framebuffers.CubeFramebuffer.prototype.unbind = function() {\n  gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n  gl.viewport(\n      0, 0,\n      gl.drawingBufferWidth || gl.canvas.width,\n      gl.drawingBufferHeight || gl.canvas.height);\n};\n\n/**\n * A framebuffer with a Float32RGBA texture.\n * @param {number} width width of framebuffer.\n * @param {number} height height of framebuffer.\n * @param {boolean?} opt_depth true = create a depth attachment\n */\ntdl.framebuffers.Float32Framebuffer = function(width, height, opt_depth) {\n  if (!gl.getExtension(\"OES_texture_float\")) {\n    throw(\"Requires OES_texture_float extension\");\n  }\n  tdl.framebuffers.Framebuffer.call(this, width, height, opt_depth);\n};\n\ntdl.base.inherit(tdl.framebuffers.Float32Framebuffer, tdl.framebuffers.Framebuffer);\n\ntdl.framebuffers.Float32Framebuffer.prototype.initializeTexture = function(tex) {\n  gl.bindTexture(gl.TEXTURE_2D, tex.texture);\n  tex.setParameter(gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n  tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n  tex.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n  tex.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n  gl.texImage2D(gl.TEXTURE_2D,\n                0,                 // level\n                gl.RGBA,           // internalFormat\n                this.width,        // width\n                this.height,       // height\n                0,                 // border\n                gl.RGBA,           // format\n                gl.FLOAT,          // type\n                null);             // data\n};\n\nreturn tdl.framebuffers;\n});\n"
  },
  {
    "path": "tdl/fullscreen.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains misc functions to deal with\n *               fullscreen.\n */\ndefine(['./base-rs'], function(BaseRS) {\n\n/**\n * A module for misc.\n * @namespace\n */\ntdl.provide('tdl.fullscreen');\ntdl.fullscreen = tdl.fullscreen || {};\n\ntdl.fullscreen.requestFullScreen = function(element) {\n  if (element.requestFullscreen) {\n    element.requestFullscreen();\n  } else if (element.msRequestFullscreen) {\n    element.msRequestFullscreen();\n  } else if (element.webkitRequestFullScreen) {\n    element.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);\n  } else if (element.mozRequestFullScreen) {\n    element.mozRequestFullScreen();\n  }\n};\n\ntdl.fullscreen.cancelFullScreen = function(element) {\n  if (document.exitFullscreen) {\n    document.exitFullscreen();\n  } else if (document.msExitFullscreen) {\n    document.msExitFullscreen();\n  } else if (document.webkitCancelFullScreen) {\n    document.webkitCancelFullScreen();\n  } else if (document.mozCancelFullScreen) {\n    document.mozCancelFullScreen();\n  }\n};\n\ntdl.fullscreen.onFullScreenChange = function(element, callback) {\n  var isFullScreen = function() {\n    return document.fullscreenElement || document.mozFullScreenElement ||\n           document.webkitFullscreenElement || document.msFullscreenElement ||\n           document.mozFullScreen || document.webkitIsFullScreen;\n  };\n  document.addEventListener('fullscreenchange', function(event) {\n      callback(isFullScreen());\n    });\n  element.addEventListener('webkitfullscreenchange', function(event) {\n      callback(isFullScreen());\n    });\n  document.addEventListener('mozfullscreenchange', function(event) {\n      callback(isFullScreen());\n    });\n};\n\nreturn tdl.fullscreen;\n});\n"
  },
  {
    "path": "tdl/io.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains various functions and class for io.\n */\ndefine(['./base-rs'], function(BaseRS) {\n\ntdl.provide('tdl.io');\n/**\n * A Module with various io functions and classes.\n * @namespace\n */\ntdl.io = tdl.io || {};\n\n/**\n * Creates a LoadInfo object.\n * @param {XMLHttpRequest} opt_request\n *     The request to watch.\n * @return {!tdl.io.LoadInfo} The new LoadInfo.\n * @see tdl.io.LoadInfo\n */\ntdl.io.createLoadInfo = function(opt_request) {\n  return new tdl.io.LoadInfo(opt_request);\n};\n\n/**\n * A class to help with progress reporting for most loading utilities.\n *\n * @example\n * var g_loadInfo = null;\n * g_id = window.setInterval(statusUpdate, 500);\n * g_loadInfo = tdl.scene.loadScene('http://google.com/somescene.js',\n *                                  callback);\n *\n * function callback(exception) {\n *   g_loadInfo = null;\n *   window.clearInterval(g_id);\n *   if (!exception) {\n *     // do something with scene just loaded\n *   }\n * }\n *\n * function statusUpdate() {\n *   if (g_loadInfo) {\n *     var progress = g_loadInfo.getKnownProgressInfoSoFar();\n *     document.getElementById('loadstatus').innerHTML = progress.percent;\n *   }\n * }\n *\n * @constructor\n * @param {XMLHttpRequest?} opt_request\n *     The request to watch.\n * @see tdl.loader.Loader\n */\ntdl.io.LoadInfo = function(opt_request) {\n  this.request_ = opt_request;\n  this.streamLength_ = 0;  // because the request may have been freed.\n  this.children_ = [];\n};\n\n/**\n * Adds another LoadInfo as a child of this LoadInfo so they can be\n * managed as a group.\n * @param {!tdl.io.LoadInfo} loadInfo The child LoadInfo.\n */\ntdl.io.LoadInfo.prototype.addChild = function(loadInfo) {\n  this.children_.push(loadInfo);\n};\n\n/**\n * Marks this LoadInfo as finished.\n */\ntdl.io.LoadInfo.prototype.finish = function() {\n  if (this.request_) {\n    if (this.hasStatus_) {\n      this.streamLength_ = this.request_.streamLength;\n    }\n    this.request_ = null;\n  }\n};\n\n/**\n * Gets the total bytes that will be streamed known so far.\n *\n * If you are only streaming 1 file then this will be the info for that file but\n * if you have queued up many files using a `tdl.loader.Loader`\n * only a couple of files are streamed at a time meaning that\n * the size is not known for files that have yet started to\n * download.\n *\n * If you are downloading many files for your application and you want to\n * provide a progress status you have about 4 options\n *\n * 1.  Use `LoadInfo.getTotalBytesDownloaded()` /\n *     `LoadInfo.getTotalKnownBytesToStreamSoFar()` and just be\n *     aware the bar will grown and then shrink as new files\n *     start to download and their lengths become known.\n *\n * 2.  Use `LoadInfo.getTotalRequestsDownloaded()` /\n *     `LoadInfo.getTotalKnownRequestsToStreamSoFar()` and be\n *     aware the granularity is not all that great since it only\n *     reports fully downloaded files. If you are downloading a\n *     bunch of small files this might be ok.\n *\n * 3.  Put all your files in one archive. Then there will be\n *     only one file and method 1 will work well.\n *\n * 4.  Figure out the total size in bytes of the files you will\n *     download and put that number in your application, then use\n *     `LoadInfo.getTotalBytesDownloaded()` /\n *     `MY_APPS_TOTAL_BYTES_TO_DOWNLOAD`.\n *\n * @return {number} The total number of currently known bytes to be streamed.\n */\ntdl.io.LoadInfo.prototype.getTotalKnownBytesToStreamSoFar = function() {\n  //if (!this.streamLength_ && this.request_ && this.hasStatus_) {\n  //  //\n  //  //this.streamLength_ = this.request_.streamLength;\n  //}\n  var total = this.streamLength_;\n  for (var cc = 0; cc < this.children_.length; ++cc) {\n    total += this.children_[cc].getTotalKnownBytesToStreamSoFar();\n  }\n  return total;\n};\n\n/**\n * Gets the total bytes downloaded so far.\n * @return {number} The total number of currently known bytes to be streamed.\n */\ntdl.io.LoadInfo.prototype.getTotalBytesDownloaded = function() {\n  var total = (this.request_ && this.hasStatus_) ?\n              this.request_.bytesReceived : this.streamLength_;\n  for (var cc = 0; cc < this.children_.length; ++cc) {\n    total += this.children_[cc].getTotalBytesDownloaded();\n  }\n  return total;\n};\n\n/**\n * Gets the total streams that will be download known so far.\n * We can't know all the streams since you could use an tdl.loader.Loader\n * object, request some streams, then call this function, then request some\n * more.\n *\n * See LoadInfo.getTotalKnownBytesToStreamSoFar for details.\n * @return {number} The total number of requests currently known to be streamed.\n * @see tdl.io.LoadInfo.getTotalKnownBytesToStreamSoFar\n */\ntdl.io.LoadInfo.prototype.getTotalKnownRequestsToStreamSoFar = function() {\n  var total = 1;\n  for (var cc = 0; cc < this.children_.length; ++cc) {\n    total += this.children_[cc].getTotalKnownRequestToStreamSoFar();\n  }\n  return total;\n};\n\n/**\n * Gets the total requests downloaded so far.\n * @return {number} The total requests downloaded so far.\n */\ntdl.io.LoadInfo.prototype.getTotalRequestsDownloaded = function() {\n  var total = this.request_ ? 0 : 1;\n  for (var cc = 0; cc < this.children_.length; ++cc) {\n    total += this.children_[cc].getTotalRequestsDownloaded();\n  }\n  return total;\n};\n\n/**\n * Gets progress info.\n * This is commonly formatted version of the information available from a\n * LoadInfo.\n *\n * See LoadInfo.getTotalKnownBytesToStreamSoFar for details.\n * @return {{percent: number, downloaded: string, totalBytes: string,\n *     base: number, suffix: string}} progress info.\n * @see tdl.io.LoadInfo.getTotalKnownBytesToStreamSoFar\n */\ntdl.io.LoadInfo.prototype.getKnownProgressInfoSoFar = function() {\n  var percent = 0;\n  var bytesToDownload = this.getTotalKnownBytesToStreamSoFar();\n  var bytesDownloaded = this.getTotalBytesDownloaded();\n  if (bytesToDownload > 0) {\n    percent = Math.floor(bytesDownloaded / bytesToDownload * 100);\n  }\n\n  var base = (bytesToDownload < 1024 * 1024) ? 1024 : (1024 * 1024);\n\n  return {\n    percent: percent,\n    downloaded: (bytesDownloaded / base).toFixed(2),\n    totalBytes: (bytesToDownload / base).toFixed(2),\n    base: base,\n    suffix: (base == 1024 ? 'kb' : 'mb')}\n\n};\n\n/**\n * Loads text from an external file. This function is synchronous.\n * @param {string} url The url of the external file.\n * @return {string} the loaded text if the request is synchronous.\n */\ntdl.io.loadTextFileSynchronous = function(url) {\n  var error = 'loadTextFileSynchronous failed to load url \"' + url + '\"';\n  var request;\n  if (window.XMLHttpRequest) {\n    request = new XMLHttpRequest();\n    if (request.overrideMimeType) {\n      request.overrideMimeType('text/plain');\n    }\n  } else if (window.ActiveXObject) {\n    request = new ActiveXObject('MSXML2.XMLHTTP.3.0');\n  } else {\n    throw 'XMLHttpRequest is disabled';\n  }\n  request.open('GET', url, false);\n  request.send(null);\n  if (request.readyState != 4) {\n    throw error;\n  }\n  return request.responseText;\n};\n\n/**\n * Loads text from an external file. This function is asynchronous.\n * @param {string} url The url of the external file.\n * @param {function(string, *): void} callback A callback passed the loaded\n *     string and an exception which will be null on success.\n * @return {tdl.io.LoadInfo} A LoadInfo to track progress.\n */\ntdl.io.loadTextFile = function(url, callback) {\n  var error = 'loadTextFile failed to load url \"' + url + '\"';\n  var request;\n  if (window.XMLHttpRequest) {\n    request = new XMLHttpRequest();\n    if (request.overrideMimeType) {\n      request.overrideMimeType('text/plain; charset=utf-8');\n    }\n  } else if (window.ActiveXObject) {\n    request = new ActiveXObject('MSXML2.XMLHTTP.3.0');\n  } else {\n    throw 'XMLHttpRequest is disabled';\n  }\n  var loadInfo = tdl.io.createLoadInfo(request, false);\n  request.open('GET', url, true);\n  var finish = function() {\n    if (request.readyState == 4) {\n      var text = '';\n      // HTTP reports success with a 200 status. The file protocol reports\n      // success with zero. HTTP does not use zero as a status code (they\n      // start at 100).\n      // https://developer.mozilla.org/En/Using_XMLHttpRequest\n      var success = request.status == 200 || request.status == 0;\n      if (success) {\n        text = request.responseText;\n      }\n      loadInfo.finish();\n      callback(text, success ? null : 'could not load: ' + url);\n    }\n  };\n  request.onreadystatechange = finish;\n  request.send(null);\n  return loadInfo;\n};\n\n/**\n * Loads a file from an external file. This function is\n * asynchronous.\n * @param {string} url The url of the external file.\n * @param {function(string, *): void} callback A callback passed the loaded\n *     ArrayBuffer and an exception which will be null on\n *     success.\n * @return {tdl.io.LoadInfo} A LoadInfo to track progress.\n */\ntdl.io.loadArrayBuffer = function(url, callback) {\n  var error = 'loadArrayBuffer failed to load url \"' + url + '\"';\n  var request;\n  if (window.XMLHttpRequest) {\n    request = new XMLHttpRequest();\n  } else {\n    throw 'XMLHttpRequest is disabled';\n  }\n  var loadInfo = tdl.io.createLoadInfo(request, false);\n  request.open('GET', url, true);\n  var finish = function() {\n    if (request.readyState == 4) {\n      var text = '';\n      // HTTP reports success with a 200 status. The file protocol reports\n      // success with zero. HTTP does not use zero as a status code (they\n      // start at 100).\n      // https://developer.mozilla.org/En/Using_XMLHttpRequest\n      var success = request.status == 200 || request.status == 0;\n      if (success) {\n        arrayBuffer = request.response;\n      }\n      loadInfo.finish();\n      callback(arrayBuffer, success ? null : 'could not load: ' + url);\n    }\n  };\n  request.onreadystatechange = finish;\n  if (request.responseType === undefined) {\n    throw 'no support for binary files';\n  }\n  request.responseType = \"arraybuffer\";\n  request.send(null);\n  return loadInfo;\n};\n\n/**\n * Loads JSON from an external file. This function is asynchronous.\n * @param {string} url The url of the external file.\n * @param {function(jsonObject, *): void} callback A callback passed the loaded\n *     json and an exception which will be null on success.\n * @return {tdl.io.LoadInfo} A LoadInfo to track progress.\n */\ntdl.io.loadJSON = function(url, callback) {\n  var error = 'loadJSON failed to load url \"' + url + '\"';\n  var request;\n  if (window.XMLHttpRequest) {\n    request = new XMLHttpRequest();\n    if (request.overrideMimeType) {\n      request.overrideMimeType('text/plain');\n    }\n  } else if (window.ActiveXObject) {\n    request = new ActiveXObject('MSXML2.XMLHTTP.3.0');\n  } else {\n    throw 'XMLHttpRequest is disabled';\n  }\n  var loadInfo = tdl.io.createLoadInfo(request, false);\n  request.open('GET', url, true);\n  var finish = function() {\n    if (request.readyState == 4) {\n      var json = undefined;\n      // HTTP reports success with a 200 status. The file protocol reports\n      // success with zero. HTTP does not use zero as a status code (they\n      // start at 100).\n      // https://developer.mozilla.org/En/Using_XMLHttpRequest\n      var success = request.status == 200 || request.status == 0;\n      if (success) {\n        try {\n          json = JSON.parse(request.responseText);\n        } catch (e) {\n          success = false;\n        }\n      }\n      loadInfo.finish();\n      callback(json, success ? null : 'could not load: ' + url);\n    }\n  };\n  try {\n    request.onreadystatechange = finish;\n    request.send(null);\n  } catch (e) {\n    callback(null, 'could not load: ' + url);\n  }\n  return loadInfo;\n};\n\n/**\n * Sends an object. This function is asynchronous.\n * @param {string} url The url of the external file.\n * @param {function(jsonObject, *): void} callback A callback passed the loaded\n *     json and an exception which will be null on success.\n * @return {tdl.io.LoadInfo} A LoadInfo to track progress.\n */\ntdl.io.sendJSON = function(url, jsonObject, callback) {\n  var error = 'sendJSON failed to load url \"' + url + '\"';\n  var request;\n  if (window.XMLHttpRequest) {\n    request = new XMLHttpRequest();\n    if (request.overrideMimeType) {\n      request.overrideMimeType('text/plain');\n    }\n  } else if (window.ActiveXObject) {\n    request = new ActiveXObject('MSXML2.XMLHTTP.3.0');\n  } else {\n    throw 'XMLHttpRequest is disabled';\n  }\n  var loadInfo = tdl.io.createLoadInfo(request, false);\n  request.open('POST', url, true);\n  var js = JSON.stringify(jsonObject);\n  var finish = function() {\n    if (request.readyState == 4) {\n      var json = undefined;\n      // HTTP reports success with a 200 status. The file protocol reports\n      // success with zero. HTTP does not use zero as a status code (they\n      // start at 100).\n      // https://developer.mozilla.org/En/Using_XMLHttpRequest\n      var success = request.status == 200 || request.status == 0;\n      if (success) {\n        try {\n          json = JSON.parse(request.responseText);\n        } catch (e) {\n          success = false;\n        }\n      }\n      loadInfo.finish();\n      callback(json, success ? null : 'could not load: ' + url);\n    }\n  };\n  try {\n    request.onreadystatechange = finish;\n    request.setRequestHeader(\"Content-type\", \"application/json\");\n    request.send(js);\n  } catch (e) {\n    callback(null, 'could not load: ' + url);\n  }\n  return loadInfo;\n};\n\nreturn tdl.io;\n});\n"
  },
  {
    "path": "tdl/loader.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains a loader class for helping to load\n *     muliple assets in an asynchronous manner.\n */\ndefine(['./base-rs', './io'], function(BaseRS, IO) {\n\ntdl.provide('tdl.loader');\n/**\n * A Module with a loader class for helping to load muliple assets in an\n * asynchronous manner.\n * @namespace\n */\ntdl.loader = tdl.loader || {};\n\n/**\n * @callback Loader~Finished\n * @memberOf tdl.loader\n */\n/**\n * A simple Loader class to call some callback when everything has loaded.\n * @constructor\n * @param {tdl.loader.Loader~Finished} onFinished Function to call when\n *        final item has loaded.\n */\ntdl.loader.Loader = function(onFinished)  {\n  this.count_ = 1;\n  this.onFinished_ = onFinished;\n\n  /**\n   * The LoadInfo for this loader you can use to track progress.\n   * @type {!tdl.io.LoadInfo}\n   */\n  this.loadInfo = tdl.io.createLoadInfo();\n};\n\n/**\n * Creates a Loader for helping to load a bunch of items asychronously.\n *\n * The way you use this is as follows.\n *\n * @example\n * var loader = tdl.loader.createLoader(myFinishedCallback);\n * loader.loadTextFile(text1Url, callbackForText);\n * loader.loadTextFile(text2Url, callbackForText);\n * loader.loadTextFile(text3Url, callbackForText);\n * loader.finish();\n *\n * The loader guarantees that myFinishedCallback will be called after\n * all the items have been loaded.\n *\n* @param {tdl.loader.Loader~Finished} onFinished Function to call when\n*        final item has loaded.\n* @return {tdl.loader.Loader} A Loader Object.\n */\ntdl.loader.createLoader = function(onFinished) {\n  return new tdl.loader.Loader(onFinished);\n};\n\n/**\n * @callback Loader~Text\n * @param {string?} str contents of file\n * @param {error?} error or null of no error.\n */\n\n/**\n * Loads a text file.\n * @param {string} url URL of scene to load.\n * @param {Loader~Text} onTextLoaded\n *     Function to call when the file is loaded. It will be\n *     passed the contents of the file as a string and an\n *     exception which is null on success.\n */\ntdl.loader.Loader.prototype.loadTextFile = function(url, onTextLoaded) {\n  var that = this;  // so the function below can see \"this\".\n  ++this.count_;\n  var loadInfo = tdl.io.loadTextFile(url, function(string, exception) {\n    onTextLoaded(string, exception);\n    that.countDown_();\n  });\n  this.loadInfo.addChild(loadInfo);\n};\n\n/**\n * Creates a loader that is tracked by this loader so that when the new loader\n * is finished it will be reported to this loader.\n * @param {tdl.loader.Loader~Finished} onFinished Function\n *      to be called when everything loaded with this loader has\n *      finished.\n * @return {tdl.loader.Loader} The new Loader.\n */\ntdl.loader.Loader.prototype.createLoader = function(onFinished) {\n  var that = this;\n  ++this.count_;\n  var loader = tdl.loader.createLoader(function() {\n      onFinished();\n      that.countDown_();\n  });\n  this.loadInfo.addChild(loader.loadInfo);\n  return loader;\n};\n\n/**\n * Counts down the internal count and if it gets to zero calls the callback.\n * @private\n */\ntdl.loader.Loader.prototype.countDown_ = function() {\n  --this.count_;\n  if (this.count_ === 0) {\n    this.onFinished_();\n  }\n};\n\n/**\n * Finishes the loading process.\n * Actually this just calls countDown_ to account for the count starting at 1.\n * @private\n */\ntdl.loader.Loader.prototype.finish = function() {\n  this.countDown_();\n};\n\nreturn tdl.loader;\n});\n"
  },
  {
    "path": "tdl/log.js",
    "content": "/*\r\n * Copyright 2009, Google Inc.\r\n * All rights reserved.\r\n *\r\n * Redistribution and use in source and binary forms, with or without\r\n * modification, are permitted provided that the following conditions are\r\n * met:\r\n *\r\n *     * Redistributions of source code must retain the above copyright\r\n * notice, this list of conditions and the following disclaimer.\r\n *     * Redistributions in binary form must reproduce the above\r\n * copyright notice, this list of conditions and the following disclaimer\r\n * in the documentation and/or other materials provided with the\r\n * distribution.\r\n *     * Neither the name of Google Inc. nor the names of its\r\n * contributors may be used to endorse or promote products derived from\r\n * this software without specific prior written permission.\r\n *\r\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n */\r\n\r\n\r\n/**\r\n * @fileoverview This file contains objects to deal with logging.\r\n */\r\ndefine(['./base-rs', './string'], function(BaseRS, Strings) {\r\n\r\ntdl.provide('tdl.log');\r\n/**\r\n * A module for log.\r\n * @namespace\r\n */\r\n\r\n/**\r\n * Wrapped logging function.\r\n * @param {*} msg The message to log.\r\n */\r\ntdl.log = function() {\r\n  var str = tdl.string.argsToString(arguments);\r\n  if (window.console && window.console.log) {\r\n    window.console.log(str);\r\n  } else if (window.dump) {\r\n    window.dump(str + \"\\n\");\r\n  }\r\n};\r\n\r\n/**\r\n * Wrapped logging function.\r\n * @param {*} msg The message to log.\r\n */\r\ntdl.error = function() {\r\n  var str = tdl.string.argsToString(arguments);\r\n  if (window.console) {\r\n    if (window.console.error) {\r\n      window.console.error(str);\r\n    } else if (window.console.log) {\r\n      window.console.log(str);\r\n    }\r\n  } else if (window.dump) {\r\n    window.dump(str + \"\\n\");\r\n  }\r\n};\r\n\r\n/**\r\n * Dumps an object to the console.\r\n *\r\n * @param {!Object} obj Object to dump.\r\n * @param {string} opt_prefix string to prefix each value with.\r\n */\r\ntdl.dumpObj = function(obj, opt_prefix) {\r\n  tdl.log(tdl.string.objToString(obj, opt_prefix));\r\n};\r\n\r\nreturn tdl;\r\n});\r\n"
  },
  {
    "path": "tdl/math.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains matrix/vector math functions.\n * It adds them to the \"math\" module on the tdl object.\n *\n * tdl.math supports a row-major and a column-major mode.  In both\n * modes, vectors are stored as arrays of numbers, and matrices are stored as\n * arrays of arrays of numbers.\n *\n * In row-major mode:\n *\n * - Rows of a matrix are sub-arrays.\n * - Individual entries of a matrix M get accessed in M[row][column] fashion.\n * - Tuples of coordinates are interpreted as row-vectors.\n * - A vector v gets transformed by a matrix M by multiplying in the order v*M.\n *\n * In column-major mode:\n *\n * - Columns of a matrix are sub-arrays.\n * - Individual entries of a matrix M get accessed in M[column][row] fashion.\n * - Tuples of coordinates are interpreted as column-vectors.\n * - A matrix M transforms a vector v by multiplying in the order M*v.\n *\n * When a function in tdl.math requires separate row-major and\n * column-major versions, a function with the same name gets added to each of\n * the namespaces tdl.math.rowMajor and tdl.math.columnMajor. The\n * function installRowMajorFunctions() or the function\n * installColumnMajorFunctions() should get called during initialization to\n * establish the mode.  installRowMajorFunctions() works by iterating through\n * the tdl.math.rowMajor namespace and for each function foo, setting\n * tdl.math.foo equal to tdl.math.rowMajor.foo.\n * installRowMajorFunctions() works the same way, iterating over the columnMajor\n * namespace.  At the end of this file, we call installRowMajorFunctions().\n *\n * Switching modes changes two things.  It changes how a matrix is encoded as an\n * array, and it changes how the entries of a matrix get interpreted.  Because\n * those two things change together, the matrix representing a given\n * transformation of space is the same JavaScript object in either mode.\n * One consequence of this is that very few functions require separate row-major\n * and column-major versions.  Typically, a function requires separate versions\n * only if it makes matrix multiplication order explicit, like\n * mulMatrixMatrix(), mulMatrixVector(), or mulVectorMatrix().  Functions which\n * create a new matrix, like scaling(), rotationZYX(), and translation() return\n * the same JavaScript object in either mode, and functions which implicitly\n * multiply like scale(), rotateZYX() and translate() modify the matrix in the\n * same way in either mode.\n *\n * The convention choice made for math functions in this library is independent\n * of the convention choice for how matrices get loaded into shaders.  That\n * convention is determined on a per-shader basis.\n *\n * Other utilities in tdl should avoid making calls to functions that make\n * multiplication order explicit.  Instead they should appeal to functions like:\n *\n * tdl.math.matrix4.transformPoint\n * tdl.math.matrix4.transformDirection\n * tdl.math.matrix4.transformNormal\n * tdl.math.matrix4.transformVector4\n * tdl.math.matrix4.composition\n * tdl.math.matrix4.compose\n *\n * These functions multiply matrices implicitly and internally choose the\n * multiplication order to get the right result.  That way, utilities which use\n * tdl.math work in either major mode.  Note that this does not necessarily\n * mean all sample code will work even if a line is added which switches major\n * modes, but it does mean that calls to tdl still do what they are supposed\n * to.\n *\n */\n\ndefine(['./base-rs'], function(BaseRS) {\n\ntdl.provide('tdl.math');\n\n/**\n * A module for math for tdl.math.\n * @namespace\n */\ntdl.math = tdl.math || {};\n\n/**\n * A random seed for the pseudoRandom function.\n * @private\n * @type {number}\n */\ntdl.math.randomSeed_ = 0;\n\n/**\n * A constant for the pseudoRandom function\n * @private\n * @type {number}\n */\ntdl.math.RANDOM_RANGE_ = Math.pow(2, 32);\n\n/**\n * Functions which deal with 4-by-4 transformation matrices are kept in their\n * own namespsace.\n * @namespace\n */\ntdl.math.matrix4 = tdl.math.matrix4 || {};\n\n/**\n * Functions that are specifically row major are kept in their own namespace.\n * @namespace\n */\ntdl.math.rowMajor = tdl.math.rowMajor || {};\n\n/**\n * Functions that are specifically column major are kept in their own namespace.\n * @namespace\n */\ntdl.math.columnMajor = tdl.math.columnMajor || {};\n\n/**\n * An Array of 2 floats\n * @typedef {number[]} tdl.math.Vector2\n */\n\n/**\n * An Array of 3 floats\n * @typedef {number[]} tdl.math.Vector3\n */\n\n/**\n * An Array of 4 floats\n * @typedef {number[]} tdl.math.Vector4\n */\n\n/**\n * An Array of floats.\n * @typedef {number[]} tdl.math.Vector\n */\n\n/**\n * A 1x1 Matrix of floats\n * @typedef {number[]} tdl.math.Matrix1\n */\n\n/**\n * A 2x2 Matrix of floats\n * @typedef {number[]} tdl.math.Matrix2\n */\n\n/**\n * A 3x3 Matrix of floats\n * @typedef {number[]} tdl.math.Matrix3\n */\n\n/**\n * A 4x4 Matrix of floats\n * @typedef {number[]} tdl.math.Matrix4\n */\n\n/**\n * A arbitrary size Matrix of floats\n * @typedef {Array.<number[]>} tdl.math.Matrix;\n */\n\n/**\n * Returns a deterministic pseudorandom number between 0 and 1\n * @return {number} a random number between 0 and 1\n */\ntdl.math.pseudoRandom = function() {\n  var math = tdl.math;\n  return (math.randomSeed_ =\n          (134775813 * math.randomSeed_ + 1) %\n          math.RANDOM_RANGE_) / math.RANDOM_RANGE_;\n};\n\n/**\n * Resets the pseudoRandom function sequence.\n */\ntdl.math.resetPseudoRandom = function() {\n  tdl.math.randomSeed_ = 0;\n};\n\n/**\n * Return a random integer between 0 and n-1\n * @param {number} n\n */\ntdl.math.randomInt = function(n) {\n  return Math.floor(Math.random() * n);\n}\n\n/**\n * Converts degrees to radians.\n * @param {number} degrees A value in degrees.\n * @return {number} the value in radians.\n */\ntdl.math.degToRad = function(degrees) {\n  return degrees * Math.PI / 180;\n};\n\n/**\n * Converts radians to degrees.\n * @param {number} radians A value in radians.\n * @return {number} the value in degrees.\n */\ntdl.math.radToDeg = function(radians) {\n  return radians * 180 / Math.PI;\n};\n\n/**\n * Performs linear interpolation on two scalars.\n * Given scalars a and b and interpolation coefficient t, returns\n * (1 - t) * a + t * b.\n * @param {number} a Operand scalar.\n * @param {number} b Operand scalar.\n * @param {number} t Interpolation coefficient.\n * @return {number} The weighted sum of a and b.\n */\ntdl.math.lerpScalar = function(a, b, t) {\n  return (1 - t) * a + t * b;\n};\n\n/**\n * Adds two vectors; assumes a and b have the same dimension.\n * @param {tdl.math.Vector} a Operand vector.\n * @param {tdl.math.Vector} b Operand vector.\n * @return {tdl.math.Vector} The sum of a and b.\n */\ntdl.math.addVector = function(a, b) {\n  var r = [];\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    r[i] = a[i] + b[i];\n  return r;\n};\n\n/**\n * Subtracts two vectors.\n * @param {tdl.math.Vector} a Operand vector.\n * @param {tdl.math.Vector} b Operand vector.\n * @return {tdl.math.Vector} The difference of a and b.\n */\ntdl.math.subVector = function(a, b) {\n  var r = [];\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    r[i] = a[i] - b[i];\n  return r;\n};\n\n/**\n * Performs linear interpolation on two vectors.\n * Given vectors a and b and interpolation coefficient t, returns\n * (1 - t) * a + t * b.\n * @param {tdl.math.Vector} a Operand vector.\n * @param {tdl.math.Vector} b Operand vector.\n * @param {number} t Interpolation coefficient.\n * @return {tdl.math.Vector} The weighted sum of a and b.\n */\ntdl.math.lerpVector = function(a, b, t) {\n  var r = [];\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    r[i] = (1 - t) * a[i] + t * b[i];\n  return r;\n};\n\n/**\n * Clamps a value between 0 and range using a modulo.\n * @param {number} v Value to clamp mod.\n * @param {number} range Range to clamp to.\n * @param {number} opt_rangeStart start of range. Default = 0.\n * @return {number} Clamp modded value.\n */\ntdl.math.modClamp = function(v, range, opt_rangeStart) {\n  var start = opt_rangeStart || 0;\n  if (range < 0.00001) {\n    return start;\n  }\n  v -= start;\n  if (v < 0) {\n    v -= Math.floor(v / range) * range;\n  } else {\n    v = v % range;\n  }\n  return v + start;\n};\n\n/**\n * Lerps in a circle.\n * Does a lerp between a and b but inside range so for example if\n * range is 100, a is 95 and b is 5 lerping will go in the positive direction.\n * @param {number} a Start value.\n * @param {number} b Target value.\n * @param {number} t Amount to lerp (0 to 1).\n * @param {number} range Range of circle.\n * @return {number} lerped result.\n */\ntdl.math.lerpCircular = function(a, b, t, range) {\n  a = tdl.math.modClamp(a, range);\n  b = tdl.math.modClamp(b, range);\n  var delta = b - a;\n  if (Math.abs(delta) > range * 0.5) {\n    if (delta > 0) {\n      b -= range;\n    } else {\n      b += range;\n    }\n  }\n  return tdl.math.modClamp(tdl.math.lerpScalar(a, b, t), range);\n};\n\n/**\n * Lerps radians.\n * @param {number} a Start value.\n * @param {number} b Target value.\n * @param {number} t Amount to lerp (0 to 1).\n * @return {number} lerped result.\n */\ntdl.math.lerpRadian = function(a, b, t) {\n  return tdl.math.lerpCircular(a, b, t, Math.PI * 2);\n};\n\n/**\n * Divides a vector by a scalar.\n * @param {tdl.math.Vector} v The vector.\n * @param {number} k The scalar.\n * @return {tdl.math.Vector} v The vector v divided by k.\n */\ntdl.math.divVectorScalar = function(v, k) {\n  var r = [];\n  var vLength = v.length;\n  for (var i = 0; i < vLength; ++i)\n    r[i] = v[i] / k;\n  return r;\n};\n\n/**\n * Computes the dot product of two vectors; assumes that a and b have\n * the same dimension.\n * @param {tdl.math.Vector} a Operand vector.\n * @param {tdl.math.Vector} b Operand vector.\n * @return {number} The dot product of a and b.\n */\ntdl.math.dot = function(a, b) {\n  var r = 0.0;\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    r += a[i] * b[i];\n  return r;\n};\n\n/**\n * Computes the cross product of two vectors; assumes both vectors have\n * three entries.\n * @param {tdl.math.Vector} a Operand vector.\n * @param {tdl.math.Vector} b Operand vector.\n * @return {tdl.math.Vector} The vector a cross b.\n */\ntdl.math.cross = function(a, b) {\n  return [a[1] * b[2] - a[2] * b[1],\n          a[2] * b[0] - a[0] * b[2],\n          a[0] * b[1] - a[1] * b[0]];\n};\n\n/**\n * Computes the Euclidean length of a vector, i.e. the square root of the\n * sum of the squares of the entries.\n * @param {tdl.math.Vector} a The vector.\n * @return {number} The length of a.\n */\ntdl.math.length = function(a) {\n  var r = 0.0;\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    r += a[i] * a[i];\n  return Math.sqrt(r);\n};\n\n/**\n * Computes the square of the Euclidean length of a vector, i.e. the sum\n * of the squares of the entries.\n * @param {tdl.math.Vector} a The vector.\n * @return {number} The square of the length of a.\n */\ntdl.math.lengthSquared = function(a) {\n  var r = 0.0;\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    r += a[i] * a[i];\n  return r;\n};\n\n/**\n * Computes the Euclidean distance between two vectors.\n * @param {tdl.math.Vector} a A vector.\n * @param {tdl.math.Vector} b A vector.\n * @return {number} The distance between a and b.\n */\ntdl.math.distance = function(a, b) {\n  var r = 0.0;\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i) {\n    var t = a[i] - b[i];\n    r += t * t;\n  }\n  return Math.sqrt(r);\n};\n\n/**\n * Computes the square of the Euclidean distance between two vectors.\n * @param {tdl.math.Vector} a A vector.\n * @param {tdl.math.Vector} b A vector.\n * @return {number} The distance between a and b.\n */\ntdl.math.distanceSquared = function(a, b) {\n  var r = 0.0;\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i) {\n    var t = a[i] - b[i];\n    r += t * t;\n  }\n  return r;\n};\n\n/**\n * Divides a vector by its Euclidean length and returns the quotient.\n * @param {tdl.math.Vector} a The vector.\n * @return {tdl.math.Vector} The normalized vector.\n */\ntdl.math.normalize = function(a) {\n  var r = [];\n  var n = 0.0;\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    n += a[i] * a[i];\n  n = Math.sqrt(n);\n  if (n > 0.00001) {\n    for (var i = 0; i < aLength; ++i)\n      r[i] = a[i] / n;\n  } else {\n    r = [0,0,0];\n  }\n  return r;\n};\n\n/**\n * Adds two matrices; assumes a and b are the same size.\n * @param {tdl.math.Matrix} a Operand matrix.\n * @param {tdl.math.Matrix} b Operand matrix.\n * @return {tdl.math.Matrix} The sum of a and b.\n */\ntdl.math.addMatrix = function(a, b) {\n  var r = [];\n  var aLength = a.length;\n  var a0Length = a[0].length;\n  for (var i = 0; i < aLength; ++i) {\n    var row = [];\n    var ai = a[i];\n    var bi = b[i];\n    for (var j = 0; j < a0Length; ++j)\n      row[j] = ai[j] + bi[j];\n    r[i] = row;\n  }\n  return r;\n};\n\n/**\n * Subtracts two matrices; assumes a and b are the same size.\n * @param {tdl.math.Matrix} a Operand matrix.\n * @param {tdl.math.Matrix} b Operand matrix.\n * @return {tdl.math.Matrix} The sum of a and b.\n */\ntdl.math.subMatrix = function(a, b) {\n  var r = [];\n  var aLength = a.length;\n  var a0Length = a[0].length;\n  for (var i = 0; i < aLength; ++i) {\n    var row = [];\n    var ai = a[i];\n    var bi = b[i];\n    for (var j = 0; j < a0Length; ++j)\n      row[j] = ai[j] - bi[j];\n    r[i] = row;\n  }\n  return r;\n};\n\n/**\n * Performs linear interpolation on two matrices.\n * Given matrices a and b and interpolation coefficient t, returns\n * (1 - t) * a + t * b.\n * @param {tdl.math.Matrix} a Operand matrix.\n * @param {tdl.math.Matrix} b Operand matrix.\n * @param {number} t Interpolation coefficient.\n * @return {tdl.math.Matrix} The weighted of a and b.\n */\ntdl.math.lerpMatrix = function(a, b, t) {\n  var r = [];\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i) {\n    r[i] = (1 - t) * a[i] + t * b[i];\n  }\n  return r;\n};\n\n/**\n * Divides a matrix by a scalar.\n * @param {tdl.math.Matrix} m The matrix.\n * @param {number} k The scalar.\n * @return {tdl.math.Matrix} The matrix m divided by k.\n */\ntdl.math.divMatrixScalar = function(m, k) {\n  var r = [];\n  var mLength = m.length;\n  for (var i = 0; i < mLength; ++i) {\n    r[i] = m[i] / k;\n  }\n  return r;\n};\n\n/**\n * Negates a scalar.\n * @param {number} a The scalar.\n * @return {number} -a.\n */\ntdl.math.negativeScalar = function(a) {\n return -a;\n};\n\n/**\n * Negates a vector.\n * @param {tdl.math.Vector} v The vector.\n * @return {tdl.math.Vector} -v.\n */\ntdl.math.negativeVector = function(v) {\n var r = [];\n var vLength = v.length;\n for (var i = 0; i < vLength; ++i) {\n   r[i] = -v[i];\n }\n return r;\n};\n\n/**\n * Negates a matrix.\n * @param {tdl.math.Matrix} m The matrix.\n * @return {tdl.math.Matrix} -m.\n */\ntdl.math.negativeMatrix = function(m) {\n var r = [];\n var mLength = m.length;\n for (var i = 0; i < mLength; ++i) {\n   r[i] = -m[i];\n }\n return r;\n};\n\n/**\n * Copies a scalar.\n * @param {number} a The scalar.\n * @return {number} a.\n */\ntdl.math.copyScalar = function(a) {\n  return a;\n};\n\n/**\n * Copies a vector.\n * @param {tdl.math.Vector} v The vector.\n * @return {tdl.math.Vector} A copy of v.\n */\ntdl.math.copyVector = function(v) {\n  var r = [];\n  for (var i = 0; i < v.length; i++)\n    r[i] = v[i];\n  return r;\n};\n\n/**\n * Copies a matrix.\n * @param {tdl.math.Matrix} m The matrix.\n * @return {tdl.math.Matrix} A copy of m.\n */\ntdl.math.copyMatrix = function(m) {\n  var r = [];\n  var mLength = m.length;\n  for (var i = 0; i < mLength; ++i) {\n    r[i] = m[i];\n  }\n  return r;\n};\n\n/**\n * Multiplies two scalars.\n * @param {number} a Operand scalar.\n * @param {number} b Operand scalar.\n * @return {number} The product of a and b.\n */\ntdl.math.mulScalarScalar = function(a, b) {\n  return a * b;\n};\n\n/**\n * Multiplies a scalar by a vector.\n * @param {number} k The scalar.\n * @param {tdl.math.Vector} v The vector.\n * @return {tdl.math.Vector} The product of k and v.\n */\ntdl.math.mulScalarVector = function(k, v) {\n  var r = [];\n  var vLength = v.length;\n  for (var i = 0; i < vLength; ++i) {\n    r[i] = k * v[i];\n  }\n  return r;\n};\n\n/**\n * Multiplies a vector by a scalar.\n * @param {tdl.math.Vector} v The vector.\n * @param {number} k The scalar.\n * @return {tdl.math.Vector} The product of k and v.\n */\ntdl.math.mulVectorScalar = function(v, k) {\n  return tdl.math.mulScalarVector(k, v);\n};\n\n/**\n * Multiplies a scalar by a matrix.\n * @param {number} k The scalar.\n * @param {tdl.math.Matrix} m The matrix.\n * @return {tdl.math.Matrix} The product of m and k.\n */\ntdl.math.mulScalarMatrix = function(k, m) {\n  var r = [];\n  var mLength = m.length;\n  for (var i = 0; i < mLength; ++i) {\n    r[i] = k * m[i];\n  }\n  return r;\n};\n\n/**\n * Multiplies a matrix by a scalar.\n * @param {tdl.math.Matrix} m The matrix.\n * @param {number} k The scalar.\n * @return {tdl.math.Matrix} The product of m and k.\n */\ntdl.math.mulMatrixScalar = function(m, k) {\n  return tdl.math.mulScalarMatrix(k, m);\n};\n\n/**\n * Multiplies a vector by another vector (component-wise); assumes a and\n * b have the same length.\n * @param {tdl.math.Vector} a Operand vector.\n * @param {tdl.math.Vector} b Operand vector.\n * @return {tdl.math.Vector} The vector of products of entries of a and\n *     b.\n */\ntdl.math.mulVectorVector = function(a, b) {\n  var r = [];\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    r[i] = a[i] * b[i];\n  return r;\n};\n\n/**\n * Divides a vector by another vector (component-wise); assumes a and\n * b have the same length.\n * @param {tdl.math.Vector} a Operand vector.\n * @param {tdl.math.Vector} b Operand vector.\n * @return {tdl.math.Vector} The vector of quotients of entries of a and\n *     b.\n */\ntdl.math.divVectorVector = function(a, b) {\n  var r = [];\n  var aLength = a.length;\n  for (var i = 0; i < aLength; ++i)\n    r[i] = a[i] / b[i];\n  return r;\n};\n\n/**\n * Multiplies a vector by a matrix; treats the vector as a row vector; assumes\n * matrix entries are accessed in [row][column] fashion.\n * @param {tdl.math.Vector} v The vector.\n * @param {tdl.math.Matrix} m The matrix.\n * @return {tdl.math.Vector} The product of v and m as a row vector.\n */\ntdl.math.rowMajor.mulVectorMatrix4 = function(v, m) {\n  var r = [];\n  for (var i = 0; i < 4; ++i) {\n    r[i] = 0.0;\n    for (var j = 0; j < 4; ++j)\n      r[i] += v[j] * m[j * 4 + i];\n  }\n  return r;\n};\n\n/**\n * Multiplies a vector by a matrix; treats the vector as a row vector; assumes\n * matrix entries are accessed in [column][row] fashion.\n * @param {tdl.math.Vector} v The vector.\n * @param {tdl.math.Matrix} m The matrix.\n * @return {tdl.math.Vector} The product of v and m as a row vector.\n */\ntdl.math.columnMajor.mulVectorMatrix = function(v, m) {\n  var r = [];\n  for (var i = 0; i < 4; ++i) {\n    r[i] = 0.0;\n    for (var j = 0; j < 4; ++j)\n      r[i] += v[j] * r[i * 4 +  j];\n  }\n  return r;\n};\n\n/**\n * Multiplies a vector by a matrix; treats the vector as a row vector.\n * @param {tdl.math.Matrix} m The matrix.\n * @param {tdl.math.Vector} v The vector.\n * @return {tdl.math.Vector} The product of m and v as a row vector.\n */\ntdl.math.mulVectorMatrix = null;\n\n/**\n * Multiplies a matrix by a vector; treats the vector as a column vector.\n * assumes matrix entries are accessed in [row][column] fashion.\n * @param {tdl.math.Matrix} m The matrix.\n * @param {tdl.math.Vector} v The vector.\n * @return {tdl.math.Vector} The product of m and v as a column vector.\n */\ntdl.math.rowMajor.mulMatrixVector = function(m, v) {\n  var r = [];\n  for (var i = 0; i < 4; ++i) {\n    r[i] = 0.0;\n    for (var j = 0; j < 4; ++j)\n      r[i] += m[i * 4 + j] * v[j];\n  }\n  return r;\n};\n\n/**\n * Multiplies a matrix by a vector; treats the vector as a column vector;\n * assumes matrix entries are accessed in [column][row] fashion.\n * @param {tdl.math.Matrix} m The matrix.\n * @param {tdl.math.Vector} v The vector.\n * @return {tdl.math.Vector} The product of m and v as a column vector.\n */\ntdl.math.columnMajor.mulMatrixVector = function(m, v) {\n  var r = [];\n  for (var i = 0; i < 4; ++i) {\n    r[i] = 0.0;\n    for (var j = 0; j < 4; ++j)\n      r[i] += v[j] * m[j * 4 + i];\n  }\n  return r;\n};\n\n/**\n * Multiplies a matrix by a vector; treats the vector as a column vector.\n * @param {tdl.math.Matrix} m The matrix.\n * @param {tdl.math.Vector} v The vector.\n * @return {tdl.math.Vector} The product of m and v as a column vector.\n */\ntdl.math.mulMatrixVector = null;\n\n/**\n * Multiplies two 2-by-2 matrices; assumes that the given matrices are 2-by-2;\n * assumes matrix entries are accessed in [row][column] fashion.\n * @param {tdl.math.Matrix2} a The matrix on the left.\n * @param {tdl.math.Matrix2} b The matrix on the right.\n * @return {tdl.math.Matrix2} The matrix product of a and b.\n */\ntdl.math.rowMajor.mulMatrixMatrix2 = function(a, b) {\n  var a00 = a[0*2+0];\n  var a01 = a[0*2+1];\n  var a10 = a[1*2+0];\n  var a11 = a[1*2+1];\n  var b00 = b[0*2+0];\n  var b01 = b[0*2+1];\n  var b10 = b[1*2+0];\n  var b11 = b[1*2+1];\n  return [a00 * b00 + a01 * b10, a00 * b01 + a01 * b11,\n          a10 * b00 + a11 * b10, a10 * b01 + a11 * b11];\n};\n\n/**\n * Multiplies two 2-by-2 matrices; assumes that the given matrices are 2-by-2;\n * assumes matrix entries are accessed in [column][row] fashion.\n * @param {tdl.math.Matrix2} a The matrix on the left.\n * @param {tdl.math.Matrix2} b The matrix on the right.\n * @return {tdl.math.Matrix2} The matrix product of a and b.\n */\ntdl.math.columnMajor.mulMatrixMatrix2 = function(a, b) {\n  var a00 = a[0*2+0];\n  var a01 = a[0*2+1];\n  var a10 = a[1*2+0];\n  var a11 = a[1*2+1];\n  var b00 = b[0*2+0];\n  var b01 = b[0*2+1];\n  var b10 = b[1*2+0];\n  var b11 = b[1*2+1];\n  return [a00 * b00 + a10 * b01, a01 * b00 + a11 * b01,\n          a00 * b10 + a10 * b11, a01 * b10 + a11 * b11];\n};\n\n/**\n * Multiplies two 2-by-2 matrices.\n * @param {tdl.math.Matrix2} a The matrix on the left.\n * @param {tdl.math.Matrix2} b The matrix on the right.\n * @return {tdl.math.Matrix2} The matrix product of a and b.\n */\ntdl.math.mulMatrixMatrix2 = null;\n\n\n/**\n * Multiplies two 3-by-3 matrices; assumes that the given matrices are 3-by-3;\n * assumes matrix entries are accessed in [row][column] fashion.\n * @param {tdl.math.Matrix3} a The matrix on the left.\n * @param {tdl.math.Matrix3} b The matrix on the right.\n * @return {tdl.math.Matrix3} The matrix product of a and b.\n */\ntdl.math.rowMajor.mulMatrixMatrix3 = function(a, b) {\n  var a00 = a[0*3+0];\n  var a01 = a[0*3+1];\n  var a02 = a[0*3+2];\n  var a10 = a[1*3+0];\n  var a11 = a[1*3+1];\n  var a12 = a[1*3+2];\n  var a20 = a[2*3+0];\n  var a21 = a[2*3+1];\n  var a22 = a[2*3+2];\n  var b00 = b[0*3+0];\n  var b01 = b[0*3+1];\n  var b02 = b[0*3+2];\n  var b10 = b[1*3+0];\n  var b11 = b[1*3+1];\n  var b12 = b[1*3+2];\n  var b20 = b[2*3+0];\n  var b21 = b[2*3+1];\n  var b22 = b[2*3+2];\n  return [a00 * b00 + a01 * b10 + a02 * b20,\n          a00 * b01 + a01 * b11 + a02 * b21,\n          a00 * b02 + a01 * b12 + a02 * b22,\n          a10 * b00 + a11 * b10 + a12 * b20,\n          a10 * b01 + a11 * b11 + a12 * b21,\n          a10 * b02 + a11 * b12 + a12 * b22,\n          a20 * b00 + a21 * b10 + a22 * b20,\n          a20 * b01 + a21 * b11 + a22 * b21,\n          a20 * b02 + a21 * b12 + a22 * b22];\n};\n\n/**\n * Multiplies two 3-by-3 matrices; assumes that the given matrices are 3-by-3;\n * assumes matrix entries are accessed in [column][row] fashion.\n * @param {tdl.math.Matrix3} a The matrix on the left.\n * @param {tdl.math.Matrix3} b The matrix on the right.\n * @return {tdl.math.Matrix3} The matrix product of a and b.\n */\ntdl.math.columnMajor.mulMatrixMatrix3 = function(a, b) {\n  var a00 = a[0*3+0];\n  var a01 = a[0*3+1];\n  var a02 = a[0*3+2];\n  var a10 = a[1*3+0];\n  var a11 = a[1*3+1];\n  var a12 = a[1*3+2];\n  var a20 = a[2*3+0];\n  var a21 = a[2*3+1];\n  var a22 = a[2*3+2];\n  var b00 = b[0*3+0];\n  var b01 = b[0*3+1];\n  var b02 = b[0*3+2];\n  var b10 = b[1*3+0];\n  var b11 = b[1*3+1];\n  var b12 = b[1*3+2];\n  var b20 = b[2*3+0];\n  var b21 = b[2*3+1];\n  var b22 = b[2*3+2];\n  return [a00 * b00 + a10 * b01 + a20 * b02,\n          a01 * b00 + a11 * b01 + a21 * b02,\n          a02 * b00 + a12 * b01 + a22 * b02,\n          a00 * b10 + a10 * b11 + a20 * b12,\n          a01 * b10 + a11 * b11 + a21 * b12,\n          a02 * b10 + a12 * b11 + a22 * b12,\n          a00 * b20 + a10 * b21 + a20 * b22,\n          a01 * b20 + a11 * b21 + a21 * b22,\n          a02 * b20 + a12 * b21 + a22 * b22];\n};\n\n/**\n * Multiplies two 3-by-3 matrices; assumes that the given matrices are 3-by-3.\n * @param {tdl.math.Matrix3} a The matrix on the left.\n * @param {tdl.math.Matrix3} b The matrix on the right.\n * @return {tdl.math.Matrix3} The matrix product of a and b.\n */\ntdl.math.mulMatrixMatrix3 = null;\n\n/**\n * Multiplies two 4-by-4 matrices; assumes that the given matrices are 4-by-4;\n * assumes matrix entries are accessed in [row][column] fashion.\n * @param {tdl.math.Matrix4} a The matrix on the left.\n * @param {tdl.math.Matrix4} b The matrix on the right.\n * @return {tdl.math.Matrix4} The matrix product of a and b.\n */\ntdl.math.rowMajor.mulMatrixMatrix4 = function(a, b) {\n  var a00 = a[0*4+0];\n  var a01 = a[0*4+1];\n  var a02 = a[0*4+2];\n  var a03 = a[0*4+3];\n  var a10 = a[1*4+0];\n  var a11 = a[1*4+1];\n  var a12 = a[1*4+2];\n  var a13 = a[1*4+3];\n  var a20 = a[2*4+0];\n  var a21 = a[2*4+1];\n  var a22 = a[2*4+2];\n  var a23 = a[2*4+3];\n  var a30 = a[3*4+0];\n  var a31 = a[3*4+1];\n  var a32 = a[3*4+2];\n  var a33 = a[3*4+3];\n  var b00 = b[0*4+0];\n  var b01 = b[0*4+1];\n  var b02 = b[0*4+2];\n  var b03 = b[0*4+3];\n  var b10 = b[1*4+0];\n  var b11 = b[1*4+1];\n  var b12 = b[1*4+2];\n  var b13 = b[1*4+3];\n  var b20 = b[2*4+0];\n  var b21 = b[2*4+1];\n  var b22 = b[2*4+2];\n  var b23 = b[2*4+3];\n  var b30 = b[3*4+0];\n  var b31 = b[3*4+1];\n  var b32 = b[3*4+2];\n  var b33 = b[3*4+3];\n  return [a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30,\n          a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31,\n          a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32,\n          a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33,\n          a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30,\n          a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31,\n          a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32,\n          a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33,\n          a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30,\n          a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31,\n          a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32,\n          a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33,\n          a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30,\n          a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31,\n          a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32,\n          a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33];\n};\n\n/**\n * Multiplies two 4-by-4 matrices; assumes that the given matrices are 4-by-4;\n * assumes matrix entries are accessed in [column][row] fashion.\n * @param {tdl.math.Matrix4} a The matrix on the left.\n * @param {tdl.math.Matrix4} b The matrix on the right.\n * @return {tdl.math.Matrix4} The matrix product of a and b.\n */\ntdl.math.columnMajor.mulMatrixMatrix4 = function(a, b) {\n  var a00 = a[0*4+0];\n  var a01 = a[0*4+1];\n  var a02 = a[0*4+2];\n  var a03 = a[0*4+3];\n  var a10 = a[1*4+0];\n  var a11 = a[1*4+1];\n  var a12 = a[1*4+2];\n  var a13 = a[1*4+3];\n  var a20 = a[2*4+0];\n  var a21 = a[2*4+1];\n  var a22 = a[2*4+2];\n  var a23 = a[2*4+3];\n  var a30 = a[3*4+0];\n  var a31 = a[3*4+1];\n  var a32 = a[3*4+2];\n  var a33 = a[3*4+3];\n  var b00 = b[0*4+0];\n  var b01 = b[0*4+1];\n  var b02 = b[0*4+2];\n  var b03 = b[0*4+3];\n  var b10 = b[1*4+0];\n  var b11 = b[1*4+1];\n  var b12 = b[1*4+2];\n  var b13 = b[1*4+3];\n  var b20 = b[2*4+0];\n  var b21 = b[2*4+1];\n  var b22 = b[2*4+2];\n  var b23 = b[2*4+3];\n  var b30 = b[3*4+0];\n  var b31 = b[3*4+1];\n  var b32 = b[3*4+2];\n  var b33 = b[3*4+3];\n  return [a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03,\n          a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03,\n          a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03,\n          a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03,\n          a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13,\n          a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13,\n          a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13,\n          a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13,\n          a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23,\n          a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23,\n          a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23,\n          a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23,\n          a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33,\n          a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33,\n          a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33,\n          a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33];\n};\n\n/**\n * Multiplies two 4-by-4 matrices; assumes that the given matrices are 4-by-4.\n * @param {tdl.math.Matrix4} a The matrix on the left.\n * @param {tdl.math.Matrix4} b The matrix on the right.\n * @return {tdl.math.Matrix4} The matrix product of a and b.\n */\ntdl.math.mulMatrixMatrix4 = null;\n\n/**\n * Multiplies two matrices; assumes that the sizes of the matrices are\n * appropriately compatible; assumes matrix entries are accessed in\n * [row][column] fashion.\n * @param {tdl.math.Matrix} a The matrix on the left.\n * @param {tdl.math.Matrix} b The matrix on the right.\n * @return {tdl.math.Matrix} The matrix product of a and b.\n */\ntdl.math.rowMajor.mulMatrixMatrix = function(a, b) {\n  var r = [];\n  for (var i = 0; i < 4; ++i) {\n    for (var j = 0; j < 4; ++j) {\n      r[i*4+j] = 0.0;\n      for (var k = 0; k < 4; ++k)\n        r[i*4+j] += a[i*4+k] * b[k*4+j]; // kth row, jth column.\n    }\n  }\n  return r;\n};\n\n/**\n * Multiplies two matrices; assumes that the sizes of the matrices are\n * appropriately compatible; assumes matrix entries are accessed in\n * [row][column] fashion.\n * @param {tdl.math.Matrix} a The matrix on the left.\n * @param {tdl.math.Matrix} b The matrix on the right.\n * @return {tdl.math.Matrix} The matrix product of a and b.\n */\ntdl.math.columnMajor.mulMatrixMatrix = function(a, b) {\n  var r = [];\n  for (var i = 0; i < 4; ++i) {\n    for (var j = 0; j < 4; ++j) {\n      r[i*4+j] = 0.0;\n      for (var k = 0; k < 4; ++k)\n        r[i*4+j] += b[i*4+k] * a[k*4+j]; // kth column, jth row.\n    }\n  }\n  return r;\n};\n\n/**\n * Multiplies two matrices; assumes that the sizes of the matrices are\n * appropriately compatible.\n * @param {tdl.math.Matrix} a The matrix on the left.\n * @param {tdl.math.Matrix} b The matrix on the right.\n * @return {tdl.math.Matrix} The matrix product of a and b.\n */\ntdl.math.mulMatrixMatrix = null;\n\n/**\n * Gets the jth column of the given matrix m; assumes matrix entries are\n * accessed in [row][column] fashion.\n * @param {tdl.math.Matrix} m The matrix.\n * @param {number} j The index of the desired column.\n * @return {tdl.math.Vector} The jth column of m as a vector.\n */\ntdl.math.rowMajor.column = function(m, j) {\n  var r = [];\n  for (var i = 0; i < 4; ++i) {\n    r[i] = m[i*4+j];\n  }\n  return r;\n};\n\n/**\n * Gets the jth column of the given matrix m; assumes matrix entries are\n * accessed in [column][row] fashion.\n * @param {tdl.math.Matrix} m The matrix.\n * @param {number} j The index of the desired column.\n * @return {tdl.math.Vector} The jth column of m as a vector.\n */\ntdl.math.columnMajor.column = function(m, j) {\n  var r = [];\n  for (var i = 0; i < 4; ++i) {\n    r[i] = m[j*4+i];\n  }\n  return r;\n};\n\n/**\n * Gets the jth column of the given matrix m.\n * @param {tdl.math.Matrix} m The matrix.\n * @param {number} j The index of the desired column.\n * @return {tdl.math.Vector} The jth column of m as a vector.\n */\ntdl.math.column = null;\n\n/**\n * Gets the ith row of the given matrix m; assumes matrix entries are\n * accessed in [row][column] fashion.\n * @param {tdl.math.Matrix} m The matrix.\n * @param {number} i The index of the desired row.\n * @return {tdl.math.Vector} The ith row of m.\n */\ntdl.math.rowMajor.row = function(m, i) {\n  var r = [];\n  for (var j = 0; j < 4; ++j) {\n    r[i] = m[i*4+j];\n  }\n  return r;\n};\n\n/**\n * Gets the ith row of the given matrix m; assumes matrix entries are\n * accessed in [column][row] fashion.\n * @param {tdl.math.Matrix} m The matrix.\n * @param {number} i The index of the desired row.\n * @param {number} opt_size Unknown (to dkogan)\n * @return {tdl.math.Vector} The ith row of m.\n */\ntdl.math.columnMajor.row = function(m, i, opt_size) {\n  opt_size = opt_size || 4;\n  var r = [];\n  for (var j = 0; j < opt_size; ++j) {\n    r[j] = m[j*opt_size+i];\n  }\n  return r;\n};\n\n/**\n * Gets the ith row of the given matrix m.\n * @param {tdl.math.Matrix} m The matrix.\n * @param {number} i The index of the desired row.\n * @return {tdl.math.Vector} The ith row of m.\n */\ntdl.math.row = null;\n\n/**\n * Takes the transpose of a matrix.\n * @param {tdl.math.Matrix} m The matrix.\n * @return {tdl.math.Matrix} The transpose of m.\n */\ntdl.math.transpose = function(m) {\n  var r = [];\n  var m00 = m[0 * 4 + 0];\n  var m01 = m[0 * 4 + 1];\n  var m02 = m[0 * 4 + 2];\n  var m03 = m[0 * 4 + 3];\n  var m10 = m[1 * 4 + 0];\n  var m11 = m[1 * 4 + 1];\n  var m12 = m[1 * 4 + 2];\n  var m13 = m[1 * 4 + 3];\n  var m20 = m[2 * 4 + 0];\n  var m21 = m[2 * 4 + 1];\n  var m22 = m[2 * 4 + 2];\n  var m23 = m[2 * 4 + 3];\n  var m30 = m[3 * 4 + 0];\n  var m31 = m[3 * 4 + 1];\n  var m32 = m[3 * 4 + 2];\n  var m33 = m[3 * 4 + 3];\n\n  r[ 0] = m00;\n  r[ 1] = m10;\n  r[ 2] = m20;\n  r[ 3] = m30;\n  r[ 4] = m01;\n  r[ 5] = m11;\n  r[ 6] = m21;\n  r[ 7] = m31;\n  r[ 8] = m02;\n  r[ 9] = m12;\n  r[10] = m22;\n  r[11] = m32;\n  r[12] = m03;\n  r[13] = m13;\n  r[14] = m23;\n  r[15] = m33;\n  return r;\n};\n\n/**\n * Computes the trace (sum of the diagonal entries) of a square matrix;\n * assumes m is square.\n * @param {tdl.math.Matrix} m The matrix.\n * @return {number} The trace of m.\n */\ntdl.math.trace = function(m) {\n  var r = 0.0;\n  for (var i = 0; i < 4; ++i)\n    r += m[i*4+i];\n  return r;\n};\n\n/**\n * Computes the determinant of a 1-by-1 matrix.\n * @param {tdl.math.Matrix1} m The matrix.\n * @return {number} The determinant of m.\n */\ntdl.math.det1 = function(m) {\n  return m[0];\n};\n\n/**\n * Computes the determinant of a 2-by-2 matrix.\n * @param {tdl.math.Matrix2} m The matrix.\n * @return {number} The determinant of m.\n */\ntdl.math.det2 = function(m) {\n  return m[0*2+0] * m[1*2+1] - m[0*2+1] * m[1*2+0];\n};\n\n/**\n * Computes the determinant of a 3-by-3 matrix.\n * @param {tdl.math.Matrix3} m The matrix.\n * @return {number} The determinant of m.\n */\ntdl.math.det3 = function(m) {\n  return m[2*3+2] * (m[0*3+0] * m[1*3+1] - m[0*3+1] * m[1*3+0]) -\n         m[2*3+1] * (m[0*3+0] * m[1*3+2] - m[0*3+2] * m[1*3+0]) +\n         m[2*3+0] * (m[0*3+1] * m[1*3+2] - m[0*3+2] * m[1*3+1]);\n};\n\n/**\n * Computes the determinant of a 4-by-4 matrix.\n * @param {tdl.math.Matrix4} m The matrix.\n * @return {number} The determinant of m.\n */\ntdl.math.det4 = function(m) {\n  var t01 = m[0*4+0] * m[1*4+1] - m[0*4+1] * m[1*4+0];\n  var t02 = m[0*4+0] * m[1*4+2] - m[0*4+2] * m[1*4+0];\n  var t03 = m[0*4+0] * m[1*4+3] - m[0*4+3] * m[1*4+0];\n  var t12 = m[0*4+1] * m[1*4+2] - m[0*4+2] * m[1*4+1];\n  var t13 = m[0*4+1] * m[1*4+3] - m[0*4+3] * m[1*4+1];\n  var t23 = m[0*4+2] * m[1*4+3] - m[0*4+3] * m[1*4+2];\n  return m[3*4+3] * (m[2*4+2] * t01 - m[2*4+1] * t02 + m[2*4+0] * t12) -\n         m[3*4+2] * (m[2*4+3] * t01 - m[2*4+1] * t03 + m[2*4+0] * t13) +\n         m[3*4+1] * (m[2*4+3] * t02 - m[2*4+2] * t03 + m[2*4+0] * t23) -\n         m[3*4+0] * (m[2*4+3] * t12 - m[2*4+2] * t13 + m[2*4+1] * t23);\n};\n\n/**\n * Computes the inverse of a 1-by-1 matrix.\n * @param {tdl.math.Matrix1} m The matrix.\n * @return {tdl.math.Matrix1} The inverse of m.\n */\ntdl.math.inverse1 = function(m) {\n  return [[1.0 / m[0]]];\n};\n\n/**\n * Computes the inverse of a 2-by-2 matrix.\n * @param {tdl.math.Matrix2} m The matrix.\n * @return {tdl.math.Matrix2} The inverse of m.\n */\ntdl.math.inverse2 = function(m) {\n  var d = 1.0 / (m[0*2+0] * m[1*2+1] - m[0*2+1] * m[1*2+0]);\n  return [d * m[1*2+1], -d * m[0*2+1], -d * m[1*2+0], d * m[0*2+0]];\n};\n\n/**\n * Computes the inverse of a 3-by-3 matrix.\n * @param {tdl.math.Matrix3} m The matrix.\n * @return {tdl.math.Matrix3} The inverse of m.\n */\ntdl.math.inverse3 = function(m) {\n  var t00 = m[1*3+1] * m[2*3+2] - m[1*3+2] * m[2*3+1];\n  var t10 = m[0*3+1] * m[2*3+2] - m[0*3+2] * m[2*3+1];\n  var t20 = m[0*3+1] * m[1*3+2] - m[0*3+2] * m[1*3+1];\n  var d = 1.0 / (m[0*3+0] * t00 - m[1*3+0] * t10 + m[2*3+0] * t20);\n  return [ d * t00, -d * t10, d * t20,\n          -d * (m[1*3+0] * m[2*3+2] - m[1*3+2] * m[2*3+0]),\n           d * (m[0*3+0] * m[2*3+2] - m[0*3+2] * m[2*3+0]),\n          -d * (m[0*3+0] * m[1*3+2] - m[0*3+2] * m[1*3+0]),\n           d * (m[1*3+0] * m[2*3+1] - m[1*3+1] * m[2*3+0]),\n          -d * (m[0*3+0] * m[2*3+1] - m[0*3+1] * m[2*3+0]),\n           d * (m[0*3+0] * m[1*3+1] - m[0*3+1] * m[1*3+0])];\n};\n\n/**\n * Computes the inverse of a 4-by-4 matrix.\n * @param {tdl.math.Matrix4} m The matrix.\n * @return {tdl.math.Matrix4} The inverse of m.\n */\ntdl.math.inverse4 = function(m) {\n  var tmp_0 = m[2*4+2] * m[3*4+3];\n  var tmp_1 = m[3*4+2] * m[2*4+3];\n  var tmp_2 = m[1*4+2] * m[3*4+3];\n  var tmp_3 = m[3*4+2] * m[1*4+3];\n  var tmp_4 = m[1*4+2] * m[2*4+3];\n  var tmp_5 = m[2*4+2] * m[1*4+3];\n  var tmp_6 = m[0*4+2] * m[3*4+3];\n  var tmp_7 = m[3*4+2] * m[0*4+3];\n  var tmp_8 = m[0*4+2] * m[2*4+3];\n  var tmp_9 = m[2*4+2] * m[0*4+3];\n  var tmp_10 = m[0*4+2] * m[1*4+3];\n  var tmp_11 = m[1*4+2] * m[0*4+3];\n  var tmp_12 = m[2*4+0] * m[3*4+1];\n  var tmp_13 = m[3*4+0] * m[2*4+1];\n  var tmp_14 = m[1*4+0] * m[3*4+1];\n  var tmp_15 = m[3*4+0] * m[1*4+1];\n  var tmp_16 = m[1*4+0] * m[2*4+1];\n  var tmp_17 = m[2*4+0] * m[1*4+1];\n  var tmp_18 = m[0*4+0] * m[3*4+1];\n  var tmp_19 = m[3*4+0] * m[0*4+1];\n  var tmp_20 = m[0*4+0] * m[2*4+1];\n  var tmp_21 = m[2*4+0] * m[0*4+1];\n  var tmp_22 = m[0*4+0] * m[1*4+1];\n  var tmp_23 = m[1*4+0] * m[0*4+1];\n\n  var t0 = (tmp_0 * m[1*4+1] + tmp_3 * m[2*4+1] + tmp_4 * m[3*4+1]) -\n      (tmp_1 * m[1*4+1] + tmp_2 * m[2*4+1] + tmp_5 * m[3*4+1]);\n  var t1 = (tmp_1 * m[0*4+1] + tmp_6 * m[2*4+1] + tmp_9 * m[3*4+1]) -\n      (tmp_0 * m[0*4+1] + tmp_7 * m[2*4+1] + tmp_8 * m[3*4+1]);\n  var t2 = (tmp_2 * m[0*4+1] + tmp_7 * m[1*4+1] + tmp_10 * m[3*4+1]) -\n      (tmp_3 * m[0*4+1] + tmp_6 * m[1*4+1] + tmp_11 * m[3*4+1]);\n  var t3 = (tmp_5 * m[0*4+1] + tmp_8 * m[1*4+1] + tmp_11 * m[2*4+1]) -\n      (tmp_4 * m[0*4+1] + tmp_9 * m[1*4+1] + tmp_10 * m[2*4+1]);\n\n  var d = 1.0 / (m[0*4+0] * t0 + m[1*4+0] * t1 + m[2*4+0] * t2 + m[3*4+0] * t3);\n\n  return [d * t0, d * t1, d * t2, d * t3,\n       d * ((tmp_1 * m[1*4+0] + tmp_2 * m[2*4+0] + tmp_5 * m[3*4+0]) -\n          (tmp_0 * m[1*4+0] + tmp_3 * m[2*4+0] + tmp_4 * m[3*4+0])),\n       d * ((tmp_0 * m[0*4+0] + tmp_7 * m[2*4+0] + tmp_8 * m[3*4+0]) -\n          (tmp_1 * m[0*4+0] + tmp_6 * m[2*4+0] + tmp_9 * m[3*4+0])),\n       d * ((tmp_3 * m[0*4+0] + tmp_6 * m[1*4+0] + tmp_11 * m[3*4+0]) -\n          (tmp_2 * m[0*4+0] + tmp_7 * m[1*4+0] + tmp_10 * m[3*4+0])),\n       d * ((tmp_4 * m[0*4+0] + tmp_9 * m[1*4+0] + tmp_10 * m[2*4+0]) -\n          (tmp_5 * m[0*4+0] + tmp_8 * m[1*4+0] + tmp_11 * m[2*4+0])),\n       d * ((tmp_12 * m[1*4+3] + tmp_15 * m[2*4+3] + tmp_16 * m[3*4+3]) -\n          (tmp_13 * m[1*4+3] + tmp_14 * m[2*4+3] + tmp_17 * m[3*4+3])),\n       d * ((tmp_13 * m[0*4+3] + tmp_18 * m[2*4+3] + tmp_21 * m[3*4+3]) -\n          (tmp_12 * m[0*4+3] + tmp_19 * m[2*4+3] + tmp_20 * m[3*4+3])),\n       d * ((tmp_14 * m[0*4+3] + tmp_19 * m[1*4+3] + tmp_22 * m[3*4+3]) -\n          (tmp_15 * m[0*4+3] + tmp_18 * m[1*4+3] + tmp_23 * m[3*4+3])),\n       d * ((tmp_17 * m[0*4+3] + tmp_20 * m[1*4+3] + tmp_23 * m[2*4+3]) -\n          (tmp_16 * m[0*4+3] + tmp_21 * m[1*4+3] + tmp_22 * m[2*4+3])),\n       d * ((tmp_14 * m[2*4+2] + tmp_17 * m[3*4+2] + tmp_13 * m[1*4+2]) -\n          (tmp_16 * m[3*4+2] + tmp_12 * m[1*4+2] + tmp_15 * m[2*4+2])),\n       d * ((tmp_20 * m[3*4+2] + tmp_12 * m[0*4+2] + tmp_19 * m[2*4+2]) -\n          (tmp_18 * m[2*4+2] + tmp_21 * m[3*4+2] + tmp_13 * m[0*4+2])),\n       d * ((tmp_18 * m[1*4+2] + tmp_23 * m[3*4+2] + tmp_15 * m[0*4+2]) -\n          (tmp_22 * m[3*4+2] + tmp_14 * m[0*4+2] + tmp_19 * m[1*4+2])),\n       d * ((tmp_22 * m[2*4+2] + tmp_16 * m[0*4+2] + tmp_21 * m[1*4+2]) -\n          (tmp_20 * m[1*4+2] + tmp_23 * m[2*4+2] + tmp_17 * m[0*4+2]))];\n};\n\n/**\n * Computes the determinant of the cofactor matrix obtained by removal\n * of a specified row and column.  This is a helper function for the general\n * determinant and matrix inversion functions.\n * @param {tdl.math.Matrix} a The original matrix.\n * @param {number} x The row to be removed.\n * @param {number} y The column to be removed.\n * @return {number} The determinant of the matrix obtained by removing\n *     row x and column y from a.\n */\ntdl.math.codet = function(a, x, y) {\n  var size = 4;\n  var b = [];\n  var ai = 0;\n  for (var bi = 0; bi < size - 1; ++bi) {\n    if (ai == x)\n      ai++;\n    var aj = 0;\n    for (var bj = 0; bj < size - 1; ++bj) {\n      if (aj == y)\n        aj++;\n      b[bi*4+bj] = a[ai*4+aj];\n      aj++;\n    }\n    ai++;\n  }\n  return tdl.math.det(b);\n};\n\n/**\n * Computes the determinant of an arbitrary square matrix.\n * @param {tdl.math.Matrix} m The matrix.\n * @return {number} the determinant of m.\n */\ntdl.math.det = function(m) {\n  var d = 4;\n  if (d <= 4) {\n    return tdl.math['det' + d](m);\n  }\n  var r = 0.0;\n  var sign = 1;\n  var row = m[0];\n  var mLength = m.length;\n  for (var y = 0; y < mLength; y++) {\n    r += sign * row[y] * tdl.math.codet(m, 0, y);\n    sign *= -1;\n  }\n  return r;\n};\n\n/**\n * Computes the inverse of an arbitrary square matrix.\n * @param {tdl.math.Matrix} m The matrix.\n * @return {tdl.math.Matrix} The inverse of m.\n */\ntdl.math.inverse = function(m) {\n  var d = 4;\n  if (d <= 4) {\n    return tdl.math['inverse' + d](m);\n  }\n  var r = [];\n  var size = m.length;\n  for (var j = 0; j < size; ++j) {\n    r[j] = [];\n    for (var i = 0; i < size; ++i)\n      r[j][i] = ((i + j) % 2 ? -1 : 1) * tdl.math.codet(m, i, j);\n  }\n  return tdl.math.divMatrixScalar(r, tdl.math.det(m));\n};\n\n/**\n * Performs Graham-Schmidt orthogonalization on the vectors which make up the\n * given matrix and returns the result in the rows of a new matrix.  When\n * multiplying many orthogonal matrices together, errors can accumulate causing\n * the product to fail to be orthogonal.  This function can be used to correct\n * that.\n * @param {tdl.math.Matrix} m The matrix.\n * @return {tdl.math.Matrix} A matrix whose rows are obtained from the\n *     rows of m by the Graham-Schmidt process.\n */\ntdl.math.orthonormalize = function(m) {\n//  var r = [];\n//  for (var i = 0; i < 4; ++i) {\n//    var v = m[i];\n//    for (var j = 0; j < i; ++j) {\n//      v = tdl.math.subVector(v, tdl.math.mulScalarVector(\n//          tdl.math.dot(r[j], m[i]), r[j]));\n//    }\n//    r[i] = tdl.math.normalize(v);\n//  }\n//  return r;\n};\n\n/**\n * Computes the inverse of a 4-by-4 matrix.\n * Note: It is faster to call this than tdl.math.inverse.\n * @param {tdl.math.Matrix4} m The matrix.\n * @return {tdl.math.Matrix4} The inverse of m.\n */\ntdl.math.matrix4.inverse = function(m) {\n  return tdl.math.inverse4(m);\n};\n\n/**\n * Multiplies two 4-by-4 matrices; assumes that the given matrices are 4-by-4.\n * Note: It is faster to call this than tdl.math.mul.\n * @param {tdl.math.Matrix4} a The matrix on the left.\n * @param {tdl.math.Matrix4} b The matrix on the right.\n * @return {tdl.math.Matrix4} The matrix product of a and b.\n */\ntdl.math.matrix4.mul = function(a, b) {\n  return tdl.math.mulMatrixMatrix4(a, b);\n};\n\n/**\n * Computes the determinant of a 4-by-4 matrix.\n * Note: It is faster to call this than tdl.math.det.\n * @param {tdl.math.Matrix4} m The matrix.\n * @return {number} The determinant of m.\n */\ntdl.math.matrix4.det = function(m) {\n  return tdl.math.det4(m);\n};\n\n/**\n * Copies a Matrix4.\n * Note: It is faster to call this than tdl.math.copy.\n * @param {tdl.math.Matrix4} m The matrix.\n * @return {tdl.math.Matrix4} A copy of m.\n */\ntdl.math.matrix4.copy = function(m) {\n  return tdl.math.copyMatrix(m);\n};\n\ntdl.math.matrix4.transpose = tdl.math.transpose;\n\n/**\n * Sets the upper 3-by-3 block of matrix a to the upper 3-by-3 block of matrix\n * b; assumes that a and b are big enough to contain an upper 3-by-3 block.\n * @param {tdl.math.Matrix4} a A matrix.\n * @param {tdl.math.Matrix3} b A 3-by-3 matrix.\n * @return {tdl.math.Matrix4} a once modified.\n */\ntdl.math.matrix4.setUpper3x3 = function(a, b) {\n  a[0*4+0] = b[0*3+0];\n  a[0*4+1] = b[0*3+1];\n  a[0*4+2] = b[0*3+2];\n  a[1*4+0] = b[1*3+0];\n  a[1*4+1] = b[1*3+1];\n  a[1*4+2] = b[1*3+2];\n  a[2*4+0] = b[2*3+0];\n  a[2*4+1] = b[2*3+1];\n  a[2*4+2] = b[2*3+2];\n\n  return a;\n};\n\n/**\n * Returns a 3-by-3 matrix mimicking the upper 3-by-3 block of m; assumes m\n * is big enough to contain an upper 3-by-3 block.\n * @param {tdl.math.Matrix4} m The matrix.\n * @return {tdl.math.Matrix3} The upper 3-by-3 block of m.\n */\ntdl.math.matrix4.getUpper3x3 = function(m) {\n  return [\n    m[0*4+0],\n    m[0*4+1],\n    m[0*4+2],\n    m[1*4+0],\n    m[1*4+1],\n    m[1*4+2],\n    m[2*4+0],\n    m[2*4+1],\n    m[2*4+2]\n  ];\n};\n\n/**\n * Sets the translation component of a 4-by-4 matrix to the given\n * vector.\n * @param {tdl.math.Matrix4} a The matrix.\n * @param {(tdl.math.Vector3|tdl.math.Vector4)} v The vector.\n * @return {tdl.math.Matrix4} a once modified.\n */\ntdl.math.matrix4.setTranslation = function(a, v) {\n  a[12] = v[0];\n  a[13] = v[1];\n  a[14] = v[2];\n  a[15] = 1;\n  return a;\n};\n\n/**\n * Returns the translation component of a 4-by-4 matrix as a vector with 3\n * entries.\n * @param {tdl.math.Matrix4} m The matrix.\n * @return {tdl.math.Vector3} The translation component of m.\n */\ntdl.math.matrix4.getTranslation = function(m) {\n  return [m[12], m[13], m[14], m[15]];\n};\n\n/**\n * Takes a 4-by-4 matrix and a vector with 3 entries,\n * interprets the vector as a point, transforms that point by the matrix, and\n * returns the result as a vector with 3 entries.\n * @param {tdl.math.Matrix4} m The matrix.\n * @param {tdl.math.Vector3} v The point.\n * @return {tdl.math.Vector3} The transformed point.\n */\ntdl.math.matrix4.transformPoint = function(m, v) {\n  var v0 = v[0];\n  var v1 = v[1];\n  var v2 = v[2];\n  var d = v0 * m[0*4+3] + v1 * m[1*4+3] + v2 * m[2*4+3] + m[3*4+3];\n  return [(v0 * m[0*4+0] + v1 * m[1*4+0] + v2 * m[2*4+0] + m[3*4+0]) / d,\n          (v0 * m[0*4+1] + v1 * m[1*4+1] + v2 * m[2*4+1] + m[3*4+1]) / d,\n          (v0 * m[0*4+2] + v1 * m[1*4+2] + v2 * m[2*4+2] + m[3*4+2]) / d];\n};\n\n/**\n * Takes a 4-by-4 matrix and a vector with 4 entries, transforms that vector by\n * the matrix, and returns the result as a vector with 4 entries.\n * @param {tdl.math.Matrix4} m The matrix.\n * @param {tdl.math.Vector4} v The point in homogenous coordinates.\n * @return {tdl.math.Vector4} The transformed point in homogenous\n *     coordinates.\n */\ntdl.math.matrix4.transformVector4 = function(m, v) {\n  var v0 = v[0];\n  var v1 = v[1];\n  var v2 = v[2];\n  var v3 = v[3];\n\n  return [v0 * m[0*4+0] + v1 * m[1*4+0] + v2 * m[2*4+0] + v3 * m[3*4+0],\n          v0 * m[0*4+1] + v1 * m[1*4+1] + v2 * m[2*4+1] + v3 * m[3*4+1],\n          v0 * m[0*4+2] + v1 * m[1*4+2] + v2 * m[2*4+2] + v3 * m[3*4+2],\n          v0 * m[0*4+3] + v1 * m[1*4+3] + v2 * m[2*4+3] + v3 * m[3*4+3]];\n};\n\n/**\n * Takes a 4-by-4 matrix and a vector with 3 entries, interprets the vector as a\n * direction, transforms that direction by the matrix, and returns the result;\n * assumes the transformation of 3-dimensional space represented by the matrix\n * is parallel-preserving, i.e. any combination of rotation, scaling and\n * translation, but not a perspective distortion. Returns a vector with 3\n * entries.\n * @param {tdl.math.Matrix4} m The matrix.\n * @param {tdl.math.Vector3} v The direction.\n * @return {tdl.math.Vector3} The transformed direction.\n */\ntdl.math.matrix4.transformDirection = function(m, v) {\n  var v0 = v[0];\n  var v1 = v[1];\n  var v2 = v[2];\n\n  return [v0 * m[0*4+0] + v1 * m[1*4+0] + v2 * m[2*4+0],\n          v0 * m[0*4+1] + v1 * m[1*4+1] + v2 * m[2*4+1],\n          v0 * m[0*4+2] + v1 * m[1*4+2] + v2 * m[2*4+2]];\n};\n\n/**\n * Takes a 4-by-4 matrix m and a vector v with 3 entries, interprets the vector\n * as a normal to a surface, and computes a vector which is normal upon\n * transforming that surface by the matrix. The effect of this function is the\n * same as transforming v (as a direction) by the inverse-transpose of m.  This\n * function assumes the transformation of 3-dimensional space represented by the\n * matrix is parallel-preserving, i.e. any combination of rotation, scaling and\n * translation, but not a perspective distortion.  Returns a vector with 3\n * entries.\n * @param {tdl.math.Matrix4} m The matrix.\n * @param {tdl.math.Vector3} v The normal.\n * @return {tdl.math.Vector3} The transformed normal.\n */\ntdl.math.matrix4.transformNormal = function(m, v) {\n  var mi = tdl.math.inverse4(m);\n  var v0 = v[0];\n  var v1 = v[1];\n  var v2 = v[2];\n\n  return [v0 * mi[0*4+0] + v1 * mi[0*4+1] + v2 * mi[0*4+2],\n          v0 * mi[1*4+0] + v1 * mi[1*4+1] + v2 * mi[1*4+2],\n          v0 * mi[2*4+0] + v1 * mi[2*4+1] + v2 * mi[2*4+2]];\n};\n\n/**\n * Creates a 4-by-4 identity matrix.\n * @return {tdl.math.Matrix4} The 4-by-4 identity.\n */\ntdl.math.matrix4.identity = function() {\n  return [\n    1, 0, 0, 0,\n    0, 1, 0, 0,\n    0, 0, 1, 0,\n    0, 0, 0, 1\n  ];\n};\n\n/**\n * Sets the given 4-by-4 matrix to the identity matrix.\n * @param {tdl.math.Matrix4} m The matrix to set to identity.\n * @return {tdl.math.Matrix4} m once modified.\n */\ntdl.math.matrix4.setIdentity = function(m) {\n  for (var i = 0; i < 4; i++) {\n    for (var j = 0; j < 4; j++) {\n      if (i == j) {\n        m[i*4+j] = 1;\n      } else {\n        m[i*4+j] = 0;\n      }\n    }\n  }\n  return m;\n};\n\n/**\n * Computes a 4-by-4 perspective transformation matrix given the angular height\n * of the frustum, the aspect ratio, and the near and far clipping planes.  The\n * arguments define a frustum extending in the negative z direction.  The given\n * angle is the vertical angle of the frustum, and the horizontal angle is\n * determined to produce the given aspect ratio.  The arguments near and far are\n * the distances to the near and far clipping planes.  Note that near and far\n * are not z coordinates, but rather they are distances along the negative\n * z-axis.  The matrix generated sends the viewing frustum to the unit box.\n * We assume a unit box extending from -1 to 1 in the x and y dimensions and\n * from 0 to 1 in the z dimension.\n * @param {number} angle The camera angle from top to bottom (in radians).\n * @param {number} aspect The aspect ratio width / height.\n * @param {number} zNear The depth (negative z coordinate)\n *     of the near clipping plane.\n * @param {number} zFar The depth (negative z coordinate)\n *     of the far clipping plane.\n * @return {tdl.math.Matrix4} The perspective matrix.\n */\ntdl.math.matrix4.perspective = function(angle, aspect, zNear, zFar) {\n  var f = Math.tan(Math.PI * 0.5 - 0.5 * angle);\n  var rangeInv = 1.0 / (zNear - zFar);\n\n  return [\n    f / aspect, 0, 0, 0,\n    0, f, 0, 0,\n    0, 0, (zNear + zFar) * rangeInv, -1,\n    0, 0, zNear * zFar * rangeInv * 2, 0\n  ];\n};\n\n/**\n * Computes a 4-by-4 orthographic projection matrix given the coordinates of the\n * planes defining the axis-aligned, box-shaped viewing volume.  The matrix\n * generated sends that box to the unit box.  Note that although left and right\n * are x coordinates and bottom and top are y coordinates, near and far\n * are not z coordinates, but rather they are distances along the negative\n * z-axis.  We assume a unit box extending from -1 to 1 in the x and y\n * dimensions and from 0 to 1 in the z dimension.\n * @param {number} left The x coordinate of the left plane of the box.\n * @param {number} right The x coordinate of the right plane of the box.\n * @param {number} bottom The y coordinate of the bottom plane of the box.\n * @param {number} top The y coordinate of the right plane of the box.\n * @param {number} near The negative z coordinate of the near plane of the box.\n * @param {number} far The negative z coordinate of the far plane of the box.\n * @return {tdl.math.Matrix4} The orthographic projection matrix.\n */\ntdl.math.matrix4.orthographic =\n    function(left, right, bottom, top, near, far) {\n  return [\n    2 / (right - left), 0, 0, 0,\n    0, 2 / (top - bottom), 0, 0,\n    0, 0, 1 / (near - far), 0,\n    (left + right) / (left - right),\n    (bottom + top) / (bottom - top),\n    near / (near - far), 1\n  ];\n};\n\n/**\n * Computes a 4-by-4 perspective transformation matrix given the left, right,\n * top, bottom, near and far clipping planes. The arguments define a frustum\n * extending in the negative z direction. The arguments near and far are the\n * distances to the near and far clipping planes. Note that near and far are not\n * z coordinates, but rather they are distances along the negative z-axis. The\n * matrix generated sends the viewing frustum to the unit box. We assume a unit\n * box extending from -1 to 1 in the x and y dimensions and from 0 to 1 in the z\n * dimension.\n * @param {number} left The x coordinate of the left plane of the box.\n * @param {number} right The x coordinate of the right plane of the box.\n * @param {number} bottom The y coordinate of the bottom plane of the box.\n * @param {number} top The y coordinate of the right plane of the box.\n * @param {number} near The negative z coordinate of the near plane of the box.\n * @param {number} far The negative z coordinate of the far plane of the box.\n * @return {tdl.math.Matrix4} The perspective projection matrix.\n */\ntdl.math.matrix4.frustum = function(left, right, bottom, top, near, far) {\n  var dx = (right - left);\n  var dy = (top - bottom);\n  var dz = (near - far);\n  return [\n    2 * near / dx, 0, 0, 0,\n    0, 2 * near / dy, 0, 0,\n    (left + right) / dx, (top + bottom) / dy, far / dz, -1,\n    0, 0, near * far / dz, 0];\n};\n\n/**\n * Computes a 4-by-4 look-at transformation.  The transformation generated is\n * an orthogonal rotation matrix with translation component.  The translation\n * component sends the eye to the origin.  The rotation component sends the\n * vector pointing from the eye to the target to a vector pointing in the\n * negative z direction, and also sends the up vector into the upper half of\n * the yz plane.\n * @param {(tdl.math.Vector3|tdl.math.Vector4)} eye The position\n *     of the eye.\n * @param {(tdl.math.Vector3|tdl.math.Vector4)} target The\n *     position meant to be viewed.\n * @param {(tdl.math.Vector3|tdl.math.Vector4)} up A vector\n *     pointing up.\n * @return {tdl.math.Matrix4} The look-at matrix.\n */\ntdl.math.matrix4.lookAt = function(eye, target, up) {\n  return tdl.math.inverse(tdl.math.matrix4.cameraLookAt(\n      eye, target, up));\n};\n\n/**\n * Computes a 4-by-4 camera look-at transformation. This is the\n * inverse of lookAt The transformation generated is an\n * orthogonal rotation matrix with translation component.\n * @param {(tdl.math.Vector3|tdl.math.Vector4)} eye The position\n *     of the eye.\n * @param {(tdl.math.Vector3|tdl.math.Vector4)} target The\n *     position meant to be viewed.\n * @param {(tdl.math.Vector3|tdl.math.Vector4)} up A vector\n *     pointing up.\n * @return {tdl.math.Matrix4} The camera look-at matrix.\n */\ntdl.math.matrix4.cameraLookAt = function(eye, target, up) {\n  var vz = tdl.math.normalize(\n      tdl.math.subVector(eye, target));\n  var vx = tdl.math.normalize(\n      tdl.math.cross(up, vz));\n  var vy = tdl.math.cross(vz, vx);\n\n  return tdl.math.inverse([\n     vx[0], vx[1], vx[2], 0,\n     vy[0], vy[1], vy[2], 0,\n     vz[0], vz[1], vz[2], 0,\n     -tdl.math.dot(vx, eye),\n     -tdl.math.dot(vy, eye),\n     -tdl.math.dot(vz, eye), 1]);\n};\n\n/**\n * Takes two 4-by-4 matrices, a and b, and computes the product in the order\n * that pre-composes b with a.  In other words, the matrix returned will\n * transform by b first and then a.  Note this is subtly different from just\n * multiplying the matrices together.  For given a and b, this function returns\n * the same object in both row-major and column-major mode.\n * @param {tdl.math.Matrix4} a A 4-by-4 matrix.\n * @param {tdl.math.Matrix4} b A 4-by-4 matrix.\n * @return {tdl.math.Matrix4} the composition of a and b, b first then a.\n */\ntdl.math.matrix4.composition = function(a, b) {\n  var a00 = a[0*4+0];\n  var a01 = a[0*4+1];\n  var a02 = a[0*4+2];\n  var a03 = a[0*4+3];\n  var a10 = a[1*4+0];\n  var a11 = a[1*4+1];\n  var a12 = a[1*4+2];\n  var a13 = a[1*4+3];\n  var a20 = a[2*4+0];\n  var a21 = a[2*4+1];\n  var a22 = a[2*4+2];\n  var a23 = a[2*4+3];\n  var a30 = a[3*4+0];\n  var a31 = a[3*4+1];\n  var a32 = a[3*4+2];\n  var a33 = a[3*4+3];\n  var b00 = b[0*4+0];\n  var b01 = b[0*4+1];\n  var b02 = b[0*4+2];\n  var b03 = b[0*4+3];\n  var b10 = b[1*4+0];\n  var b11 = b[1*4+1];\n  var b12 = b[1*4+2];\n  var b13 = b[1*4+3];\n  var b20 = b[2*4+0];\n  var b21 = b[2*4+1];\n  var b22 = b[2*4+2];\n  var b23 = b[2*4+3];\n  var b30 = b[3*4+0];\n  var b31 = b[3*4+1];\n  var b32 = b[3*4+2];\n  var b33 = b[3*4+3];\n  return [a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03,\n          a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03,\n          a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03,\n          a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03,\n          a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13,\n          a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13,\n          a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13,\n          a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13,\n          a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23,\n          a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23,\n          a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23,\n          a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23,\n          a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33,\n          a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33,\n          a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33,\n          a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33];\n};\n\n/**\n * Takes two 4-by-4 matrices, a and b, and modifies a to be the product in the\n * order that pre-composes b with a.  The matrix a, upon modification will\n * transform by b first and then a.  Note this is subtly different from just\n * multiplying the matrices together.  For given a and b, a, upon modification,\n * will be the same object in both row-major and column-major mode.\n * @param {tdl.math.Matrix4} a A 4-by-4 matrix.\n * @param {tdl.math.Matrix4} b A 4-by-4 matrix.\n * @return {tdl.math.Matrix4} a once modified.\n */\ntdl.math.matrix4.compose = function(a, b) {\n  var a00 = a[0*4+0];\n  var a01 = a[0*4+1];\n  var a02 = a[0*4+2];\n  var a03 = a[0*4+3];\n  var a10 = a[1*4+0];\n  var a11 = a[1*4+1];\n  var a12 = a[1*4+2];\n  var a13 = a[1*4+3];\n  var a20 = a[2*4+0];\n  var a21 = a[2*4+1];\n  var a22 = a[2*4+2];\n  var a23 = a[2*4+3];\n  var a30 = a[3*4+0];\n  var a31 = a[3*4+1];\n  var a32 = a[3*4+2];\n  var a33 = a[3*4+3];\n  var b00 = b[0*4+0];\n  var b01 = b[0*4+1];\n  var b02 = b[0*4+2];\n  var b03 = b[0*4+3];\n  var b10 = b[1*4+0];\n  var b11 = b[1*4+1];\n  var b12 = b[1*4+2];\n  var b13 = b[1*4+3];\n  var b20 = b[2*4+0];\n  var b21 = b[2*4+1];\n  var b22 = b[2*4+2];\n  var b23 = b[2*4+3];\n  var b30 = b[3*4+0];\n  var b31 = b[3*4+1];\n  var b32 = b[3*4+2];\n  var b33 = b[3*4+3];\n  a[ 0] = a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03;\n  a[ 1] = a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03;\n  a[ 2] = a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03;\n  a[ 3] = a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03;\n  a[ 4] = a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13;\n  a[ 5] = a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13;\n  a[ 6] = a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13;\n  a[ 7] = a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13;\n  a[ 8] = a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23;\n  a[ 9] = a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23;\n  a[10] = a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23;\n  a[11] = a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23;\n  a[12] = a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33;\n  a[13] = a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33;\n  a[14] = a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33;\n  a[15] = a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33;\n  return a;\n};\n\n/**\n * Creates a 4-by-4 matrix which translates by the given vector v.\n * @param {(tdl.math.Vector3|tdl.math.Vector4)} v The vector by\n *     which to translate.\n * @return {tdl.math.Matrix4} The translation matrix.\n */\ntdl.math.matrix4.translation = function(v) {\n  return [\n    1, 0, 0, 0,\n    0, 1, 0, 0,\n    0, 0, 1, 0,\n    v[0], v[1], v[2], 1\n  ];\n};\n\n/**\n * Modifies the given 4-by-4 matrix by translation by the given vector v.\n * @param {tdl.math.Matrix4} m The matrix.\n * @param {(tdl.math.Vector3|tdl.math.Vector4)} v The vector by\n *     which to translate.\n * @return {tdl.math.Matrix4} m once modified.\n */\ntdl.math.matrix4.translate = function(m, v) {\n  var m00 = m[0*4+0];\n  var m01 = m[0*4+1];\n  var m02 = m[0*4+2];\n  var m03 = m[0*4+3];\n  var m10 = m[1*4+0];\n  var m11 = m[1*4+1];\n  var m12 = m[1*4+2];\n  var m13 = m[1*4+3];\n  var m20 = m[2*4+0];\n  var m21 = m[2*4+1];\n  var m22 = m[2*4+2];\n  var m23 = m[2*4+3];\n  var m30 = m[3*4+0];\n  var m31 = m[3*4+1];\n  var m32 = m[3*4+2];\n  var m33 = m[3*4+3];\n  var v0 = v[0];\n  var v1 = v[1];\n  var v2 = v[2];\n\n  m[12] = m00 * v0 + m10 * v1 + m20 * v2 + m30;\n  m[13] = m01 * v0 + m11 * v1 + m21 * v2 + m31;\n  m[14] = m02 * v0 + m12 * v1 + m22 * v2 + m32;\n  m[15] = m03 * v0 + m13 * v1 + m23 * v2 + m33;\n\n  return m;\n};\n\n/**\n * Creates a 4-by-4 matrix which scales in each dimension by an amount given by\n * the corresponding entry in the given vector; assumes the vector has three\n * entries.\n * @param {tdl.math.Vector3} v A vector of\n *     three entries specifying the factor by which to scale in each dimension.\n * @return {tdl.math.Matrix4} The scaling matrix.\n */\ntdl.math.matrix4.scaling = function(v) {\n  return [\n    v[0], 0, 0, 0,\n    0, v[1], 0, 0,\n    0, 0, v[2], 0,\n    0, 0, 0, 1\n  ];\n};\n\n/**\n * Modifies the given 4-by-4 matrix, scaling in each dimension by an amount\n * given by the corresponding entry in the given vector; assumes the vector has\n * three entries.\n * @param {tdl.math.Matrix4} m The matrix to be modified.\n * @param {tdl.math.Vector3} v A vector of three entries specifying the\n *     factor by which to scale in each dimension.\n * @return {tdl.math.Matrix4} m once modified.\n */\ntdl.math.matrix4.scale = function(m, v) {\n  var v0 = v[0];\n  var v1 = v[1];\n  var v2 = v[2];\n\n  m[0] = v0 * m[0*4+0];\n  m[1] = v0 * m[0*4+1];\n  m[2] = v0 * m[0*4+2];\n  m[3] = v0 * m[0*4+3];\n  m[4] = v1 * m[1*4+0];\n  m[5] = v1 * m[1*4+1];\n  m[6] = v1 * m[1*4+2];\n  m[7] = v1 * m[1*4+3];\n  m[8] = v2 * m[2*4+0];\n  m[9] = v2 * m[2*4+1];\n  m[10] = v2 * m[2*4+2];\n  m[11] = v2 * m[2*4+3];\n\n  return m;\n};\n\n/**\n * Creates a 4-by-4 matrix which rotates around the x-axis by the given angle.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.math.Matrix4} The rotation matrix.\n */\ntdl.math.matrix4.rotationX = function(angle) {\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n\n  return [\n    1, 0, 0, 0,\n    0, c, s, 0,\n    0, -s, c, 0,\n    0, 0, 0, 1\n  ];\n};\n\n/**\n * Modifies the given 4-by-4 matrix by a rotation around the x-axis by the given\n * angle.\n * @param {tdl.math.Matrix4} m The matrix.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.math.Matrix4} m once modified.\n */\ntdl.math.matrix4.rotateX = function(m, angle) {\n  var m10 = m[1*4+0];\n  var m11 = m[1*4+1];\n  var m12 = m[1*4+2];\n  var m13 = m[1*4+3];\n  var m20 = m[2*4+0];\n  var m21 = m[2*4+1];\n  var m22 = m[2*4+2];\n  var m23 = m[2*4+3];\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n\n  m[4]  = c * m10 + s * m20;\n  m[5]  = c * m11 + s * m21;\n  m[6]  = c * m12 + s * m22;\n  m[7]  = c * m13 + s * m23;\n  m[8]  = c * m20 - s * m10;\n  m[9]  = c * m21 - s * m11;\n  m[10] = c * m22 - s * m12;\n  m[11] = c * m23 - s * m13;\n\n  return m;\n};\n\n/**\n * Creates a 4-by-4 matrix which rotates around the y-axis by the given angle.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.math.Matrix4} The rotation matrix.\n */\ntdl.math.matrix4.rotationY = function(angle) {\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n\n  return [\n    c, 0, -s, 0,\n    0, 1, 0, 0,\n    s, 0, c, 0,\n    0, 0, 0, 1\n  ];\n};\n\n/**\n * Modifies the given 4-by-4 matrix by a rotation around the y-axis by the given\n * angle.\n * @param {tdl.math.Matrix4} m The matrix.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.math.Matrix4} m once modified.\n */\ntdl.math.matrix4.rotateY = function(m, angle) {\n  var m00 = m[0*4+0];\n  var m01 = m[0*4+1];\n  var m02 = m[0*4+2];\n  var m03 = m[0*4+3];\n  var m20 = m[2*4+0];\n  var m21 = m[2*4+1];\n  var m22 = m[2*4+2];\n  var m23 = m[2*4+3];\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n\n  m[ 0] = c * m00 - s * m20;\n  m[ 1] = c * m01 - s * m21;\n  m[ 2] = c * m02 - s * m22;\n  m[ 3] = c * m03 - s * m23;\n  m[ 8] = c * m20 + s * m00;\n  m[ 9] = c * m21 + s * m01;\n  m[10] = c * m22 + s * m02;\n  m[11] = c * m23 + s * m03;\n\n  return m;\n};\n\n/**\n * Creates a 4-by-4 matrix which rotates around the z-axis by the given angle.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.math.Matrix4} The rotation matrix.\n */\ntdl.math.matrix4.rotationZ = function(angle) {\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n\n  return [\n    c, s, 0, 0,\n    -s, c, 0, 0,\n    0, 0, 1, 0,\n    0, 0, 0, 1\n  ];\n};\n\n/**\n * Modifies the given 4-by-4 matrix by a rotation around the z-axis by the given\n * angle.\n * @param {tdl.math.Matrix4} m The matrix.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.math.Matrix4} m once modified.\n */\ntdl.math.matrix4.rotateZ = function(m, angle) {\n  var m00 = m[0*4+0];\n  var m01 = m[0*4+1];\n  var m02 = m[0*4+2];\n  var m03 = m[0*4+3];\n  var m10 = m[1*4+0];\n  var m11 = m[1*4+1];\n  var m12 = m[1*4+2];\n  var m13 = m[1*4+3];\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n\n  m[ 0] = c * m00 + s * m10;\n  m[ 1] = c * m01 + s * m11;\n  m[ 2] = c * m02 + s * m12;\n  m[ 3] = c * m03 + s * m13;\n  m[ 4] = c * m10 - s * m00;\n  m[ 5] = c * m11 - s * m01;\n  m[ 6] = c * m12 - s * m02;\n  m[ 7] = c * m13 - s * m03;\n\n  return m;\n};\n\n/**\n * Creates a 4-by-4 rotation matrix.  Interprets the entries of the given\n * vector as angles by which to rotate around the x, y and z axes, returns a\n * a matrix which rotates around the x-axis first, then the y-axis, then the\n * z-axis.\n * @param {tdl.math.Vector3} v A vector of angles (in radians).\n * @return {tdl.math.Matrix4} The rotation matrix.\n */\ntdl.math.matrix4.rotationZYX = function(v) {\n  var sinx = Math.sin(v[0]);\n  var cosx = Math.cos(v[0]);\n  var siny = Math.sin(v[1]);\n  var cosy = Math.cos(v[1]);\n  var sinz = Math.sin(v[2]);\n  var cosz = Math.cos(v[2]);\n\n  var coszsiny = cosz * siny;\n  var sinzsiny = sinz * siny;\n\n  return [\n    cosz * cosy, sinz * cosy, -siny, 0,\n    coszsiny * sinx - sinz * cosx,\n    sinzsiny * sinx + cosz * cosx,\n    cosy * sinx,\n    0,\n    coszsiny * cosx + sinz * sinx,\n    sinzsiny * cosx - cosz * sinx,\n    cosy * cosx,\n    0,\n    0, 0, 0, 1\n  ];\n};\n\n/**\n * Modifies a 4-by-4 matrix by a rotation.  Interprets the coordinates of the\n * given vector as angles by which to rotate around the x, y and z axes, rotates\n * around the x-axis first, then the y-axis, then the z-axis.\n * @param {tdl.math.Matrix4} m The matrix.\n * @param {tdl.math.Vector3} v A vector of angles (in radians).\n * @return {tdl.math.Matrix4} m once modified.\n */\ntdl.math.matrix4.rotateZYX = function(m, v) {\n  var sinX = Math.sin(v[0]);\n  var cosX = Math.cos(v[0]);\n  var sinY = Math.sin(v[1]);\n  var cosY = Math.cos(v[1]);\n  var sinZ = Math.sin(v[2]);\n  var cosZ = Math.cos(v[2]);\n\n  var cosZSinY = cosZ * sinY;\n  var sinZSinY = sinZ * sinY;\n\n  var r00 = cosZ * cosY;\n  var r01 = sinZ * cosY;\n  var r02 = -sinY;\n  var r10 = cosZSinY * sinX - sinZ * cosX;\n  var r11 = sinZSinY * sinX + cosZ * cosX;\n  var r12 = cosY * sinX;\n  var r20 = cosZSinY * cosX + sinZ * sinX;\n  var r21 = sinZSinY * cosX - cosZ * sinX;\n  var r22 = cosY * cosX;\n\n  var m00 = m[0*4+0];\n  var m01 = m[0*4+1];\n  var m02 = m[0*4+2];\n  var m03 = m[0*4+3];\n  var m10 = m[1*4+0];\n  var m11 = m[1*4+1];\n  var m12 = m[1*4+2];\n  var m13 = m[1*4+3];\n  var m20 = m[2*4+0];\n  var m21 = m[2*4+1];\n  var m22 = m[2*4+2];\n  var m23 = m[2*4+3];\n  var m30 = m[3*4+0];\n  var m31 = m[3*4+1];\n  var m32 = m[3*4+2];\n  var m33 = m[3*4+3];\n\n  m[ 0] = r00 * m00 + r01 * m10 + r02 * m20;\n  m[ 1] = r00 * m01 + r01 * m11 + r02 * m21;\n  m[ 2] = r00 * m02 + r01 * m12 + r02 * m22;\n  m[ 3] = r00 * m03 + r01 * m13 + r02 * m23;\n  m[ 4] = r10 * m00 + r11 * m10 + r12 * m20;\n  m[ 5] = r10 * m01 + r11 * m11 + r12 * m21;\n  m[ 6] = r10 * m02 + r11 * m12 + r12 * m22;\n  m[ 7] = r10 * m03 + r11 * m13 + r12 * m23;\n  m[ 8] = r20 * m00 + r21 * m10 + r22 * m20;\n  m[ 9] = r20 * m01 + r21 * m11 + r22 * m21;\n  m[10] = r20 * m02 + r21 * m12 + r22 * m22;\n  m[11] = r20 * m03 + r21 * m13 + r22 * m23;\n\n  return m;\n};\n\n/**\n * Creates a 4-by-4 matrix which rotates around the given axis by the given\n * angle.\n * @param {(tdl.math.Vector3|tdl.math.Vector4)} axis The axis\n *     about which to rotate.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.math.Matrix4} A matrix which rotates angle radians\n *     around the axis.\n */\ntdl.math.matrix4.axisRotation = function(axis, angle) {\n  var x = axis[0];\n  var y = axis[1];\n  var z = axis[2];\n  var n = Math.sqrt(x * x + y * y + z * z);\n  x /= n;\n  y /= n;\n  z /= n;\n  var xx = x * x;\n  var yy = y * y;\n  var zz = z * z;\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n  var oneMinusCosine = 1 - c;\n\n  return [\n    xx + (1 - xx) * c,\n    x * y * oneMinusCosine + z * s,\n    x * z * oneMinusCosine - y * s,\n    0,\n    x * y * oneMinusCosine - z * s,\n    yy + (1 - yy) * c,\n    y * z * oneMinusCosine + x * s,\n    0,\n    x * z * oneMinusCosine + y * s,\n    y * z * oneMinusCosine - x * s,\n    zz + (1 - zz) * c,\n    0,\n    0, 0, 0, 1\n  ];\n};\n\n/**\n * Modifies the given 4-by-4 matrix by rotation around the given axis by the\n * given angle.\n * @param {tdl.math.Matrix4} m The matrix.\n * @param {(tdl.math.Vector3|tdl.math.Vector4)} axis The axis\n *     about which to rotate.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.math.Matrix4} m once modified.\n */\ntdl.math.matrix4.axisRotate = function(m, axis, angle) {\n  var x = axis[0];\n  var y = axis[1];\n  var z = axis[2];\n  var n = Math.sqrt(x * x + y * y + z * z);\n  x /= n;\n  y /= n;\n  z /= n;\n  var xx = x * x;\n  var yy = y * y;\n  var zz = z * z;\n  var c = Math.cos(angle);\n  var s = Math.sin(angle);\n  var oneMinusCosine = 1 - c;\n\n  var r00 = xx + (1 - xx) * c;\n  var r01 = x * y * oneMinusCosine + z * s;\n  var r02 = x * z * oneMinusCosine - y * s;\n  var r10 = x * y * oneMinusCosine - z * s;\n  var r11 = yy + (1 - yy) * c;\n  var r12 = y * z * oneMinusCosine + x * s;\n  var r20 = x * z * oneMinusCosine + y * s;\n  var r21 = y * z * oneMinusCosine - x * s;\n  var r22 = zz + (1 - zz) * c;\n\n  var m00 = m[0*4+0];\n  var m01 = m[0*4+1];\n  var m02 = m[0*4+2];\n  var m03 = m[0*4+3];\n  var m10 = m[1*4+0];\n  var m11 = m[1*4+1];\n  var m12 = m[1*4+2];\n  var m13 = m[1*4+3];\n  var m20 = m[2*4+0];\n  var m21 = m[2*4+1];\n  var m22 = m[2*4+2];\n  var m23 = m[2*4+3];\n  var m30 = m[3*4+0];\n  var m31 = m[3*4+1];\n  var m32 = m[3*4+2];\n  var m33 = m[3*4+3];\n\n  m[ 0] = r00 * m00 + r01 * m10 + r02 * m20;\n  m[ 1] = r00 * m01 + r01 * m11 + r02 * m21;\n  m[ 2] = r00 * m02 + r01 * m12 + r02 * m22;\n  m[ 3] = r00 * m03 + r01 * m13 + r02 * m23;\n  m[ 4] = r10 * m00 + r11 * m10 + r12 * m20;\n  m[ 5] = r10 * m01 + r11 * m11 + r12 * m21;\n  m[ 6] = r10 * m02 + r11 * m12 + r12 * m22;\n  m[ 7] = r10 * m03 + r11 * m13 + r12 * m23;\n  m[ 8] = r20 * m00 + r21 * m10 + r22 * m20;\n  m[ 9] = r20 * m01 + r21 * m11 + r22 * m21;\n  m[10] = r20 * m02 + r21 * m12 + r22 * m22;\n  m[11] = r20 * m03 + r21 * m13 + r22 * m23;\n\n  return m;\n};\n\n/**\n * Sets each function in the namespace tdl.math to the row major\n * version in tdl.math.rowMajor (provided such a function exists in\n * tdl.math.rowMajor).  Call this function to establish the row major\n * convention.\n */\ntdl.math.installRowMajorFunctions = function() {\n  for (var f in tdl.math.rowMajor) {\n    tdl.math[f] = tdl.math.rowMajor[f];\n  }\n};\n\n/**\n * Sets each function in the namespace tdl.math to the column major\n * version in tdl.math.columnMajor (provided such a function exists in\n * tdl.math.columnMajor).  Call this function to establish the column\n * major convention.\n */\ntdl.math.installColumnMajorFunctions = function() {\n  for (var f in tdl.math.columnMajor) {\n    tdl.math[f] = tdl.math.columnMajor[f];\n  }\n};\n\n/**\n * Sets each function in the namespace tdl.math to the error checking\n * version in tdl.math.errorCheck (provided such a function exists in\n * tdl.math.errorCheck).\n */\ntdl.math.installErrorCheckFunctions = function() {\n  for (var f in tdl.math.errorCheck) {\n    tdl.math[f] = tdl.math.errorCheck[f];\n  }\n};\n\n/**\n * Sets each function in the namespace tdl.math to the error checking free\n * version in tdl.math.errorCheckFree (provided such a function exists in\n * tdl.math.errorCheckFree).\n */\ntdl.math.installErrorCheckFreeFunctions = function() {\n  for (var f in tdl.math.errorCheckFree) {\n    tdl.math[f] = tdl.math.errorCheckFree[f];\n  }\n}\n\n// By default, install the row-major functions.\ntdl.math.installRowMajorFunctions();\n\n// By default, install prechecking.\ntdl.math.installErrorCheckFunctions();\n\nreturn tdl.math;\n});\n"
  },
  {
    "path": "tdl/misc.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains misc functions that don't fit elsewhere.\n */\ndefine(['./base-rs', './log'], function(BaseRS, Log) {\n\ntdl.provide('tdl.misc');\n/**\n * A module for misc.\n * @namespace\n */\ntdl.misc = tdl.misc || {};\n\n/**\n * Lets you parse JS object literal has JSON.\n *\n * JSON requires property names be quoted, js object literal\n * does not.\n *\n * @example\n * var o = tdl.misc.parseUnquoteJSObjectString('{a:\"b\",c:123}');\n * @param {string} str js object literal string.\n */\ntdl.misc.parseUnquotedJSObjectString = function(str) {\n  // NOTE: does not handle strings with : in them.\n  var quoted = str.replace(/([a-zA-Z0-9_]+):/g,'\"$1\":')\n  return JSON.parse(quoted);\n};\n\n/**\n * Applies part of the query string to an object.\n * @param {object} object to apply properties to\n * @param {string?} name of query parameter that has settings.\n *        Default = 'settings'\n * @example\n * // Assuming the current URL is\n * // http://foo.com?settings={a:123,b:\"hello\",c:[1,2]}\n * // then\n * var o = {};\n * tdl.misc.applyUrlSettings(o);\n * console.log(JSON.stringify(o, undefined, \"  \"));\n *\n * // should print\n * {\n *   \"a\": 123,\n *   \"b\": \"hello\",\n *   \"c\": [1, 2]\n * }\n */\ntdl.misc.applyUrlSettings = function(obj, opt_argumentName) {\n  var argumentName = opt_argumentName || 'settings';\n  try {\n    var s = window.location.href;\n    var q = s.indexOf(\"?\");\n    var e = s.indexOf(\"#\");\n    if (e < 0) {\n      e = s.length;\n    }\n    var query = s.substring(q + 1, e);\n    //tdl.log(\"query:\", query);\n    var pairs = query.split(\"&\");\n    //tdl.log(\"pairs:\", pairs.length);\n    for (var ii = 0; ii < pairs.length; ++ii) {\n      var keyValue = pairs[ii].split(\"=\");\n      var key = keyValue[0];\n      var value = decodeURIComponent(keyValue[1]);\n      //tdl.log(ii, \":\", key, \"=\", value);\n      switch (key) {\n      case argumentName:\n        //tdl.log(value);\n        var settings = tdl.misc.parseUnquotedJSObjectString(value)\n        //tdl.log(\"settings:\", settings);\n        tdl.misc.copyProperties(settings, obj);\n        break;\n      }\n    }\n  } catch (e) {\n    tdl.error(e);\n    tdl.error(\"settings:\", settings);\n    return;\n  }\n};\n\n/**\n * Copies properties from obj to dst recursively.\n * @private\n * @param {!Object} obj Object with new settings.\n * @param {!Object} dst Object to receive new settings.\n */\ntdl.misc.copyProperties = function(obj, dst) {\n  for (var name in obj) {\n    var value = obj[name];\n    if (value instanceof Array) {\n      //tdl.log(\"apply->: \", name, \"[]\");\n      var newDst = dst[name];\n      if (!newDst) {\n        newDst = [];\n        dst[name] = newDst;\n      }\n      tdl.misc.copyProperties(value, newDst);\n    } else if (typeof value == 'object') {\n      //tdl.log(\"apply->: \", name);\n      var newDst = dst[name];\n      if (!newDst) {\n        newDst = {};\n        dst[name] = newDst;\n      }\n      tdl.misc.copyProperties(value, newDst);\n    } else {\n      //tdl.log(\"apply: \", name, \"=\", value);\n      dst[name] = value;\n    }\n  }\n};\n\nreturn tdl.misc;\n});\n"
  },
  {
    "path": "tdl/models.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains objects to manage models.\n */\ndefine(['./base-rs', './buffers'], function(BaseRS, Buffers) {\n\ntdl.provide('tdl.models');\n/**\n * A module for models.\n * @namespace\n */\ntdl.models = tdl.models || {};\n\n/**\n * Manages a program, buffers and textures for easier drawing.\n * @constructor\n * @param {tdl.programs.Program} program The program to render\n *     this model with\n * @param {Object.<string, AttribBuffer>} arrays The\n *     AttribBuffers to bind to draw this model.\n * @param {Object.<string, Texture>} textures The textures to\n *     bind to draw this model.\n * @param {number} opt_mode Mode to call drawElements with. Default =\n *        gl.TRIANGLES\n */\ntdl.models.Model = function(program, arrays, textures, opt_mode) {\n  this.buffers = { };\n  this.setBuffers(arrays);\n\n  var textureUnits = { }\n  var unit = 0;\n  for (var texture in program.textures) {\n    textureUnits[texture] = unit++;\n  }\n\n  this.mode = (opt_mode === undefined) ? gl.TRIANGLES : opt_mode;\n  this.textures = textures;\n  this.textureUnits = textureUnits;\n  this.setProgram(program);\n}\n\n/**\n * Sets the program for this model\n * @param {tdl.programs.Program} program The new program for\n *        this model.\n */\ntdl.models.Model.prototype.setProgram = function(program) {\n  this.program = program;\n}\n\n/**\n * Sets a buffer on this model\n * @param {string} name The name of the buffer to set.\n * @param {tdl.primitives.AttribBuffer} array The AttribBuffer\n *        to set on this model.\n * @param {boolean?} true if a new WebGLBuffer should be\n *        created.\n */\ntdl.models.Model.prototype.setBuffer = function(name, array, opt_newBuffer) {\n  var target = (name == 'indices') ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER;\n  var b = this.buffers[name];\n  if (!b || opt_newBuffer) {\n    b = new tdl.buffers.Buffer(array, target);\n  } else {\n    b.set(array);\n  }\n  this.buffers[name] = b;\n};\n\n/**\n * Sets the buffers on this model\n * @param {Object.<string, AttribBuffer>} arrays The\n *     AttribBuffers to bind to draw this model.\n * @param {boolean?} true if new WebGLBuffers should be created.\n */\ntdl.models.Model.prototype.setBuffers = function(arrays, opt_newBuffers) {\n  var that = this;\n  for (var name in arrays) {\n    this.setBuffer(name, arrays[name], opt_newBuffers);\n  }\n  if (this.buffers.indices) {\n    this.baseBuffer = this.buffers.indices;\n    this.drawFunc = function(totalComponents, startOffset) {\n      gl.drawElements(that.mode, totalComponents, gl.UNSIGNED_SHORT, startOffset);\n    }\n  } else {\n    for (var key in this.buffers) {\n      this.baseBuffer = this.buffers[key];\n      break;\n    }\n    this.drawFunc = function(totalComponents, startOffset) {\n      gl.drawArrays(that.mode, startOffset, totalComponents);\n    }\n  }\n};\n\ntdl.models.Model.prototype.applyUniforms_ = function(opt_uniforms) {\n  if (opt_uniforms) {\n    this.program.applyUniforms(opt_uniforms);\n  }\n};\n\n/**\n * Sets up the shared parts of drawing this model. Uses the\n * program, binds the buffers, sets the textures.\n *\n * @param {...Object.<string, *>} opt_uniforms An object of\n *     names to values to set on this models uniforms.\n */\ntdl.models.Model.prototype.drawPrep = function(opt_uniforms) {\n  var program = this.program;\n  var buffers = this.buffers;\n  var textures = this.textures;\n\n  program.use();\n  for (var buffer in buffers) {\n    var b = buffers[buffer];\n    if (buffer == 'indices') {\n      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.buffer());\n    } else {\n      var attrib = program.attrib[buffer];\n      if (attrib) {\n        attrib(b);\n      }\n    }\n  }\n\n  this.applyUniforms_(textures);\n  for (var ii = 0; ii < arguments.length; ++ii) {\n    this.applyUniforms_(arguments[ii]);\n  }\n};\n\n/**\n * Draws this model.\n *\n * After calling tdl.models.Model.drawPrep you can call this\n * function multiple times to draw this model.\n *\n * @param {Object.<string, *>} opt_uniforms An object of names to\n *     values to set on this models uniforms.\n * @param {Object.<string, *>} opt_textures An object of names to\n *     textures to set on this models uniforms.\n */\ntdl.models.Model.prototype.draw = function() {\n  var buffers = this.buffers;\n  // if no indices buffer then assume drawFunc is drawArrays and thus\n  // totalComponents is the number of vertices (not indices).\n  var totalComponents = buffers.indices? buffers.indices.totalComponents(): buffers.position.numElements();\n  var startOffset = 0;\n  for (var ii = 0; ii < arguments.length; ++ii) {\n    var arg = arguments[ii];\n    if (typeof arg == 'number') {\n      switch (ii) {\n      case 0:\n        totalComponents = arg;\n        break;\n      case 1:\n        startOffset = arg;\n        break;\n      default:\n        throw 'unvalid argument';\n      }\n    } else {\n      this.applyUniforms_(arg);\n    }\n  }\n\n  this.drawFunc(totalComponents, startOffset);\n};\n\nreturn tdl.models;\n});\n"
  },
  {
    "path": "tdl/particles.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains various functions and classes for rendering\n * gpu based particles.\n */\ndefine(['./base-rs', './math', './shader'], function(BaseRS, Maths, Shader) {\n\ntdl.provide('tdl.particles');\n/**\n * A Module with particle stuff\n * @namespace\n */\ntdl.particles = tdl.particles || {};\n\n/**\n * Enum for pre-made particle states.\n * @enum\n */\ntdl.particles.ParticleStateIds = {\n  BLEND: 0,\n  ADD: 1,\n  BLEND_PREMULTIPLY: 2,\n  BLEND_NO_ALPHA: 3,\n  SUBTRACT: 4,\n  INVERSE: 5};\n\n/**\n * Vertex and fragment program strings for 2D and 3D particles.\n * @private\n * @type {string[]}\n */\ntdl.particles.SHADER_STRINGS = [\n  // 3D (oriented) vertex shader\n  'uniform mat4 worldViewProjection;\\n' +\n  'uniform mat4 world;\\n' +\n  'uniform vec3 worldVelocity;\\n' +\n  'uniform vec3 worldAcceleration;\\n' +\n  'uniform float timeRange;\\n' +\n  'uniform float time;\\n' +\n  'uniform float timeOffset;\\n' +\n  'uniform float frameDuration;\\n' +\n  'uniform float numFrames;\\n' +\n  '\\n' +\n  '// Incoming vertex attributes\\n' +\n  'attribute vec4 uvLifeTimeFrameStart; // uv, lifeTime, frameStart\\n' +\n  'attribute vec4 positionStartTime;    // position.xyz, startTime\\n' +\n  'attribute vec4 velocityStartSize;    // velocity.xyz, startSize\\n' +\n  'attribute vec4 accelerationEndSize;  // acceleration.xyz, endSize\\n' +\n  'attribute vec4 spinStartSpinSpeed;   // spinStart.x, spinSpeed.y\\n' +\n  'attribute vec4 orientation;          // orientation quaternion\\n' +\n  'attribute vec4 colorMult;            // multiplies color and ramp textures\\n' +\n  '\\n' +\n  '// Outgoing variables to fragment shader\\n' +\n  'varying vec2 outputTexcoord;\\n' +\n  'varying float outputPercentLife;\\n' +\n  'varying vec4 outputColorMult;\\n' +\n  '\\n' +\n  'void main() {\\n' +\n  '  vec2 uv = uvLifeTimeFrameStart.xy;\\n' +\n  '  float lifeTime = uvLifeTimeFrameStart.z;\\n' +\n  '  float frameStart = uvLifeTimeFrameStart.w;\\n' +\n  '  vec3 position = positionStartTime.xyz;\\n' +\n  '  float startTime = positionStartTime.w;\\n' +\n  '  vec3 velocity = (world * vec4(velocityStartSize.xyz,\\n' +\n  '                                0.)).xyz + worldVelocity;\\n' +\n  '  float startSize = velocityStartSize.w;\\n' +\n  '  vec3 acceleration = (world * vec4(accelerationEndSize.xyz,\\n' +\n  '                                    0)).xyz + worldAcceleration;\\n' +\n  '  float endSize = accelerationEndSize.w;\\n' +\n  '  float spinStart = spinStartSpinSpeed.x;\\n' +\n  '  float spinSpeed = spinStartSpinSpeed.y;\\n' +\n  '\\n' +\n  '  float localTime = mod((time - timeOffset - startTime), timeRange);\\n' +\n  '  float percentLife = localTime / lifeTime;\\n' +\n  '\\n' +\n  '  float frame = mod(floor(localTime / frameDuration + frameStart),\\n' +\n  '                    numFrames);\\n' +\n  '  float uOffset = frame / numFrames;\\n' +\n  '  float u = uOffset + (uv.x + 0.5) * (1. / numFrames);\\n' +\n  '\\n' +\n  '  outputTexcoord = vec2(u, uv.y + 0.5);\\n' +\n  '  outputColorMult = colorMult;\\n' +\n  '\\n' +\n  '  float size = mix(startSize, endSize, percentLife);\\n' +\n  '  size = (percentLife < 0. || percentLife > 1.) ? 0. : size;\\n' +\n  '  float s = sin(spinStart + spinSpeed * localTime);\\n' +\n  '  float c = cos(spinStart + spinSpeed * localTime);\\n' +\n  '\\n' +\n  '  vec4 rotatedPoint = vec4((uv.x * c + uv.y * s) * size, 0., \\n' +\n  '                           (uv.x * s - uv.y * c) * size, 1.);\\n' +\n  '  vec3 center = velocity * localTime +\\n' +\n  '                acceleration * localTime * localTime + \\n' +\n  '                position;\\n' +\n  '\\n' +\n  '  vec4 q2 = orientation + orientation;\\n' +\n  '  vec4 qx = orientation.xxxw * q2.xyzx;\\n' +\n  '  vec4 qy = orientation.xyyw * q2.xyzy;\\n' +\n  '  vec4 qz = orientation.xxzw * q2.xxzz;\\n' +\n  '\\n' +\n  '  mat4 localMatrix = mat4(\\n' +\n  '      (1.0 - qy.y) - qz.z, \\n' +\n  '      qx.y + qz.w, \\n' +\n  '      qx.z - qy.w,\\n' +\n  '      0,\\n' +\n  '\\n' +\n  '      qx.y - qz.w, \\n' +\n  '      (1.0 - qx.x) - qz.z, \\n' +\n  '      qy.z + qx.w,\\n' +\n  '      0,\\n' +\n  '\\n' +\n  '      qx.z + qy.w, \\n' +\n  '      qy.z - qx.w, \\n' +\n  '      (1.0 - qx.x) - qy.y,\\n' +\n  '      0,\\n' +\n  '\\n' +\n  '      center.x, center.y, center.z, 1);\\n' +\n  '  rotatedPoint = localMatrix * rotatedPoint;\\n' +\n  '  outputPercentLife = percentLife;\\n' +\n  '  gl_Position = worldViewProjection * rotatedPoint;\\n' +\n  '}\\n',\n\n  // 2D (billboarded) vertex shader\n  'uniform mat4 viewProjection;\\n' +\n  'uniform mat4 world;\\n' +\n  'uniform mat4 viewInverse;\\n' +\n  'uniform vec3 worldVelocity;\\n' +\n  'uniform vec3 worldAcceleration;\\n' +\n  'uniform float timeRange;\\n' +\n  'uniform float time;\\n' +\n  'uniform float timeOffset;\\n' +\n  'uniform float frameDuration;\\n' +\n  'uniform float numFrames;\\n' +\n  '\\n' +\n  '// Incoming vertex attributes\\n' +\n  'attribute vec4 uvLifeTimeFrameStart; // uv, lifeTime, frameStart\\n' +\n  'attribute vec4 positionStartTime;    // position.xyz, startTime\\n' +\n  'attribute vec4 velocityStartSize;    // velocity.xyz, startSize\\n' +\n  'attribute vec4 accelerationEndSize;  // acceleration.xyz, endSize\\n' +\n  'attribute vec4 spinStartSpinSpeed;   // spinStart.x, spinSpeed.y\\n' +\n  'attribute vec4 colorMult;            // multiplies color and ramp textures\\n' +\n  '\\n' +\n  '// Outgoing variables to fragment shader\\n' +\n  'varying vec2 outputTexcoord;\\n' +\n  'varying float outputPercentLife;\\n' +\n  'varying vec4 outputColorMult;\\n' +\n  '\\n' +\n  'void main() {\\n' +\n  '  vec2 uv = uvLifeTimeFrameStart.xy;\\n' +\n  '  float lifeTime = uvLifeTimeFrameStart.z;\\n' +\n  '  float frameStart = uvLifeTimeFrameStart.w;\\n' +\n  '  vec3 position = positionStartTime.xyz;\\n' +\n//  '  vec3 position = (world * vec4(positionStartTime.xyz, 1.0)).xyz;\\n' +\n  '  float startTime = positionStartTime.w;\\n' +\n  '  vec3 velocity = (world * vec4(velocityStartSize.xyz,\\n' +\n  '                                0.)).xyz + worldVelocity;\\n' +\n  '  float startSize = velocityStartSize.w;\\n' +\n  '  vec3 acceleration = (world * vec4(accelerationEndSize.xyz,\\n' +\n  '                                    0)).xyz + worldAcceleration;\\n' +\n  '  float endSize = accelerationEndSize.w;\\n' +\n  '  float spinStart = spinStartSpinSpeed.x;\\n' +\n  '  float spinSpeed = spinStartSpinSpeed.y;\\n' +\n  '\\n' +\n  '  float localTime = mod((time - timeOffset - startTime), timeRange);\\n' +\n  '  float percentLife = localTime / lifeTime;\\n' +\n  '\\n' +\n  '  float frame = mod(floor(localTime / frameDuration + frameStart),\\n' +\n  '                    numFrames);\\n' +\n  '  float uOffset = frame / numFrames;\\n' +\n  '  float u = uOffset + (uv.x + 0.5) * (1. / numFrames);\\n' +\n  '\\n' +\n  '  outputTexcoord = vec2(u, uv.y + 0.5);\\n' +\n  '  outputColorMult = colorMult;\\n' +\n  '\\n' +\n  '  vec3 basisX = viewInverse[0].xyz;\\n' +\n  '  vec3 basisZ = viewInverse[1].xyz;\\n' +\n  '\\n' +\n  '  float size = mix(startSize, endSize, percentLife);\\n' +\n  '  size = (percentLife < 0. || percentLife > 1.) ? 0. : size;\\n' +\n  '  float s = sin(spinStart + spinSpeed * localTime);\\n' +\n  '  float c = cos(spinStart + spinSpeed * localTime);\\n' +\n  '\\n' +\n  '  vec2 rotatedPoint = vec2(uv.x * c + uv.y * s, \\n' +\n  '                           -uv.x * s + uv.y * c);\\n' +\n  '  vec3 localPosition = vec3(basisX * rotatedPoint.x +\\n' +\n  '                            basisZ * rotatedPoint.y) * size +\\n' +\n  '                       velocity * localTime +\\n' +\n  '                       acceleration * localTime * localTime + \\n' +\n  '                       position;\\n' +\n  '\\n' +\n  '  outputPercentLife = percentLife;\\n' +\n  '  gl_Position = viewProjection * vec4(localPosition + world[3].xyz, 1.);\\n' +\n  '}\\n',\n\n  // Fragment shader used by both 2D and 3D vertex shaders\n  '#ifdef GL_ES\\n' +\n  'precision mediump float;\\n' +\n  '#endif\\n' +\n  'uniform sampler2D rampSampler;\\n' +\n  'uniform sampler2D colorSampler;\\n' +\n  '\\n' +\n  '// Incoming variables from vertex shader\\n' +\n  'varying vec2 outputTexcoord;\\n' +\n  'varying float outputPercentLife;\\n' +\n  'varying vec4 outputColorMult;\\n' +\n  '\\n' +\n  'void main() {\\n' +\n  '  vec4 colorMult = texture2D(rampSampler, \\n' +\n  '                             vec2(outputPercentLife, 0.5)) *\\n' +\n  '                   outputColorMult;\\n' +\n  '  gl_FragColor = texture2D(colorSampler, outputTexcoord) * colorMult;\\n' +\n  // For debugging: requires setup of some uniforms and vertex\n  // attributes to be commented out to avoid GL errors\n  //  '  gl_FragColor = vec4(1., 0., 0., 1.);\\n' +\n  '}\\n'\n];\n\n/**\n * Corner values.\n * @private\n * @type {Array.<number[]>}\n */\ntdl.particles.CORNERS_ = [\n  [-0.5, -0.5],\n  [+0.5, -0.5],\n  [+0.5, +0.5],\n  [-0.5, +0.5]];\n\n/**\n * A function that returns the number of seconds elapsed. A\n * function, returning seconds elapsed, to be the time source\n * for the emitter.\n * @callback Particle~Clock\n * @memberOf tdl.particles\n * @return {number} the time.\n */\n\n/**\n * @callback Particle~Random\n * @memberOf tdl.particles\n * @return {number} random number between 0 and 1\n */\n\n/**\n * Creates a particle system.\n *\n * You only need one of these to run multiple emitters of different types\n * of particles.\n *\n * @constructor\n * @param {WebGLRenderingContext} gl The WebGLRenderingContext\n *     into which the particles will be rendered.\n * @param {tdl.particle.Particle~Clock?} opt_clock A function to\n *        be the clock for the emitter.\n * @param {tdl.particles.Particle~Random?} opt_randomFunction A function that\n *     returns a random number between 0.0 and 1.0. This allows\n *     you to pass in a pseudo random function if you need\n *     particles that are reproducible.\n */\ntdl.particles.ParticleSystem = function(\n    gl,\n    opt_clock,\n    opt_randomFunction) {\n  this.gl = gl;\n\n  // Entities which can be drawn -- emitters or OneShots\n  this.drawables_ = [];\n\n  var shaders = [];\n  shaders.push(new tdl.shader.Shader(gl,\n                                       tdl.particles.SHADER_STRINGS[0],\n                                       tdl.particles.SHADER_STRINGS[2]));\n  shaders.push(new tdl.shader.Shader(gl,\n                                       tdl.particles.SHADER_STRINGS[1],\n                                       tdl.particles.SHADER_STRINGS[2]));\n\n  var blendFuncs = {};\n  blendFuncs[tdl.particles.ParticleStateIds.BLEND] = {\n    src:  gl.SRC_ALPHA,\n    dest: gl.ONE_MINUS_SRC_ALPHA\n  };\n  blendFuncs[tdl.particles.ParticleStateIds.ADD] = {\n    src:  gl.SRC_ALPHA,\n    dest: gl.ONE\n  };\n  blendFuncs[tdl.particles.ParticleStateIds.BLEND_PREMULTIPLY] = {\n    src:  gl.ONE,\n    dest: gl.ONE_MINUS_SRC_ALPHA\n  };\n  blendFuncs[tdl.particles.ParticleStateIds.BLEND_NO_ALPHA] = {\n    src:  gl.SRC_COLOR,\n    dest: gl.ONE_MINUS_SRC_COLOR\n  };\n  blendFuncs[tdl.particles.ParticleStateIds.SUBTRACT] = {\n    src:  gl.SRC_ALPHA,\n    dest: gl.ONE_MINUS_SRC_ALPHA,\n    eq:   gl.FUNC_REVERSE_SUBTRACT\n  };\n  blendFuncs[tdl.particles.ParticleStateIds.INVERSE] = {\n    src:  gl.ONE_MINUS_DST_COLOR,\n    dest: gl.ONE_MINUS_SRC_COLOR\n  };\n  this.blendFuncs_ = blendFuncs;\n\n  var pixelBase = [0, 0.20, 0.70, 1, 0.70, 0.20, 0, 0];\n  var pixels = [];\n  for (var yy = 0; yy < 8; ++yy) {\n    for (var xx = 0; xx < 8; ++xx) {\n      var pixel = pixelBase[xx] * pixelBase[yy];\n      pixels.push(pixel, pixel, pixel, pixel);\n    }\n  }\n  var colorTexture = this.createTextureFromFloats(8, 8, pixels);\n  var rampTexture = this.createTextureFromFloats(2, 1, [1, 1, 1, 1,\n                                                        1, 1, 1, 0]);\n\n  this.now_ = new Date();\n  this.timeBase_ = new Date();\n  if (opt_clock) {\n    this.timeSource_ = opt_clock;\n  } else {\n    this.timeSource_ = tdl.particles.createDefaultClock_(this);\n  }\n\n  this.randomFunction_ = opt_randomFunction || function() {\n        return Math.random();\n      };\n\n  // This FloatArray is used to store a single particle's data\n  // in the VBO. As of this writing there wasn't a way to store less\n  // than a full WebGLArray's data in a bufferSubData call.\n  this.singleParticleArray_ = new Float32Array(4 * tdl.particles.LAST_IDX);\n\n  /**\n   * The shaders for particles.\n   * @type {tdl.shader.Shader[]}\n   */\n  this.shaders = shaders;\n\n  /**\n   * The default color texture for particles.\n   * @type {tdl.textures.Texture2D}\n   */\n  this.defaultColorTexture = colorTexture;\n\n  /**\n   * The default ramp texture for particles.\n   * @type {tdl.textures.Texture2D}\n   */\n  this.defaultRampTexture = rampTexture;\n};\n\ntdl.particles.createDefaultClock_ = function(particleSystem) {\n  return function() {\n    var now = particleSystem.now_;\n    var base = particleSystem.timeBase_;\n    return (now.getTime() - base.getTime()) / 1000.0;\n  }\n}\n\n/**\n * Creates an OpenGL texture from an array of floating point values.\n * @private\n * @param {number} width width in pixels\n * @param {number} height height in pixels\n * @param {number[]} pixels pixels in rgba units where each\n *        value is in the 0 to 1 range.\n * @param {WebGLTexture?} opt_texture texture to use.\n */\ntdl.particles.ParticleSystem.prototype.createTextureFromFloats = function(width, height, pixels, opt_texture) {\n  var gl = this.gl;\n  var texture = null;\n  if (opt_texture != null) {\n    texture = opt_texture;\n  } else {\n    texture = gl.createTexture();\n  }\n  // = opt_texture || gl.createTexture();\n  gl.bindTexture(gl.TEXTURE_2D, texture);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n  // FIXME: this is not 100% correct; will end up extending the ends\n  // of the range too far out toward the edge. Really need to pull in\n  // the texture coordinates used to sample this texture by half a\n  // texel.\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n  var data = new Uint8Array(pixels.length);\n  for (var i = 0; i < pixels.length; i++) {\n    var t = pixels[i] * 255.;\n    data[i] = t;\n  }\n  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);\n  return texture;\n}\n\n/**\n * A ParticleSpec specifies how to emit particles.\n *\n * NOTE: For all particle functions you can specific a ParticleSpec as a\n * Javascript object, only specifying the fields that you care about.\n *\n *     emitter.setParameters({\n *       numParticles: 40,\n *       lifeTime: 2,\n *       timeRange: 2,\n *       startSize: 50,\n *       endSize: 90,\n *       positionRange: [10, 10, 10],\n *       velocity:[0, 0, 60], velocityRange: [15, 15, 15],\n *       acceleration: [0, 0, -20],\n *       spinSpeedRange: 4}\n *     );\n *\n * Many of these parameters are in pairs. For paired paramters each particle\n * specfic value is set like this\n *\n *     particle.field = value + Math.random() - 0.5 * valueRange * 2;\n *\n * or in English\n *\n *     particle.field = value plus or minus valueRange.\n *\n * So for example, if you wanted a value from 10 to 20 you'd pass 15 for value\n * and 5 for valueRange because\n *\n *     15 + or - 5  = (10 to 20)\n *\n * @constructor\n */\ntdl.particles.ParticleSpec = function() {\n  /**\n   * The number of particles to emit.\n   * @type {number}\n   */\n  this.numParticles = 1;\n\n  /**\n   * The number of frames in the particle texture.\n   * @type {number}\n   */\n  this.numFrames = 1;\n\n  /**\n   * The frame duration at which to animate the particle texture in seconds per\n   * frame.\n   * @type {number}\n   */\n  this.frameDuration = 1;\n\n  /**\n   * The initial frame to display for a particular particle.\n   * @type {number}\n   */\n  this.frameStart = 0;\n\n  /**\n   * The frame start range.\n   * @type {number}\n   */\n  this.frameStartRange = 0;\n\n  /**\n   * The life time of the entire particle system.\n   * To make a particle system be continuous set this to match the lifeTime.\n   * @type {number}\n   */\n  this.timeRange = 99999999;\n\n  /**\n   * The startTime of a particle.\n   * @type {?number}\n   */\n  this.startTime = null;\n  // TODO: Describe what happens if this is not set. I still have some\n  //     work to do there.\n\n  /**\n   * The lifeTime of a particle.\n   * @type {number}\n   */\n  this.lifeTime = 1;\n\n  /**\n   * The lifeTime range.\n   * @type {number}\n   */\n  this.lifeTimeRange = 0;\n\n  /**\n   * The starting size of a particle.\n   * @type {number}\n   */\n  this.startSize = 1;\n\n  /**\n   * The starting size range.\n   * @type {number}\n   */\n  this.startSizeRange = 0;\n\n  /**\n   * The ending size of a particle.\n   * @type {number}\n   */\n  this.endSize = 1;\n\n  /**\n   * The ending size range.\n   * @type {number}\n   */\n  this.endSizeRange = 0;\n\n  /**\n   * The starting position of a particle in local space.\n   * @type {tdl.math.Vector3}\n   */\n  this.position = [0, 0, 0];\n\n  /**\n   * The starting position range.\n   * @type {tdl.math.Vector3}\n   */\n  this.positionRange = [0, 0, 0];\n\n  /**\n   * The velocity of a paritcle in local space.\n   * @type {tdl.math.Vector3}\n   */\n  this.velocity = [0, 0, 0];\n\n  /**\n   * The velocity range.\n   * @type {tdl.math.Vector3}\n   */\n  this.velocityRange = [0, 0, 0];\n\n  /**\n   * The acceleration of a particle in local space.\n   * @type {tdl.math.Vector3}\n   */\n  this.acceleration = [0, 0, 0];\n\n  /**\n   * The accleration range.\n   * @type {tdl.math.Vector3}\n   */\n  this.accelerationRange = [0, 0, 0];\n\n  /**\n   * The starting spin value for a particle in radians.\n   * @type {number}\n   */\n  this.spinStart = 0;\n\n  /**\n   * The spin start range.\n   * @type {number}\n   */\n  this.spinStartRange = 0;\n\n  /**\n   * The spin speed of a particle in radians.\n   * @type {number}\n   */\n  this.spinSpeed = 0;\n\n  /**\n   * The spin speed range.\n   * @type {number}\n   */\n  this.spinSpeedRange = 0;\n\n  /**\n   * The color multiplier of a particle.\n   * @type {tdl.math.Vector4}\n   */\n  this.colorMult = [1, 1, 1, 1];\n\n  /**\n   * The color multiplier range.\n   * @type {tdl.math.Vector4}\n   */\n  this.colorMultRange = [0, 0, 0, 0];\n\n  /**\n   * The velocity of all paritcles in world space.\n   * @type {tdl.math.Vector3}\n   */\n  this.worldVelocity = [0, 0, 0];\n\n  /**\n   * The acceleration of all paritcles in world space.\n   * @type {tdl.math.Vector3}\n   */\n  this.worldAcceleration = [0, 0, 0];\n\n  /**\n   * Whether these particles are oriented in 2d or 3d. true = 2d, false = 3d.\n   * @type {boolean}\n   */\n  this.billboard = true;\n\n  /**\n   * The orientation of a particle. This is only used if billboard is false.\n   * @type {tdl.quaternions.Quaternion}\n   */\n  this.orientation = [0, 0, 0, 1];\n};\n\n/**\n * Creates a particle emitter.\n * @param {tdl.textures.Texture?} opt_texture The texture to use\n *     for the particles. If you don't supply a texture a\n *     default is provided.\n * @param {tdl.particles.Particle~Clock?} opt_clock A function to be the clock\n *        for the emitter.\n * @return {tdl.particles.ParticleEmitter} The new emitter.\n */\ntdl.particles.ParticleSystem.prototype.createParticleEmitter =\n    function(opt_texture, opt_clock) {\n  var emitter = new tdl.particles.ParticleEmitter(this, opt_texture, opt_clock);\n  this.drawables_.push(emitter);\n  return emitter;\n};\n\n/**\n * A function that is called for each particle to allow it's\n * parameters to be adjusted per particle. The number is the\n * index of the particle being created, in other words, if numParticles is\n * 20 this value will be 0 to 19. The ParticleSpec is a spec for this\n * particular particle. You can set any per particle value\n * before returning\n * @callback Particle~Setup\n * @memberOf tdl.particles\n * @param {number} index of particle\n * @param {tdl.particles.ParticleSpec} spec a particle spec for\n *        this particle.\n */\n\n/**\n * Creates a Trail particle emitter.\n * You can use this for jet exhaust, etc...\n * @param {number} maxParticles Maximum number of particles to appear at once.\n * @param {tdl.particles.ParticleSpec} parameters The parameters used to\n *     generate particles.\n * @param {tdl.textures.Texture?} opt_texture The texture to\n *     use for the particles. If you don't supply a texture a\n *     default is provided.\n * @param {tdl.particles.Particle~Setup?} opt_perParticleParamSetter function\n *        to setup particles\n * @param {tdl.particles.Particle~Clock?} opt_clock A function to be the clock\n *        for the emitter.\n * @return {tdl.particles.Trail} A Trail object.\n */\ntdl.particles.ParticleSystem.prototype.createTrail = function(\n    maxParticles,\n    parameters,\n    opt_texture,\n    opt_perParticleParamSetter,\n    opt_clock) {\n  var trail = new tdl.particles.Trail(\n      this,\n      maxParticles,\n      parameters,\n      opt_texture,\n      opt_perParticleParamSetter,\n      opt_clock);\n  this.drawables_.push(trail);\n  return trail;\n};\n\n/**\n * Draws all of the particle emitters managed by this ParticleSystem.\n * This modifies the depth mask, depth test, blend function and its\n * enabling, array buffer binding, element array buffer binding, the\n * textures bound to texture units 0 and 1, and which is the active\n * texture unit.\n * @param {tdl.math.Matrix4|tdl.fast.Matrix4} viewProjection The\n *        viewProjection matrix.\n * @param {tdl.math.Matrix4|tdl.fast.Matrix4} world The world\n *        matrix.\n * @param {tdl.math.Matrix4|tdl.fast.Matrix4} viewInverse The\n *        viewInverse matrix.\n */\ntdl.particles.ParticleSystem.prototype.draw = function(viewProjection, world, viewInverse) {\n  // Update notion of current time\n  this.now_ = new Date();\n  // Set up global state\n  var gl = this.gl;\n  gl.depthMask(false);\n  gl.enable(gl.DEPTH_TEST);\n  // Set up certain uniforms once per shader per draw.\n  var shader = this.shaders[1];\n  shader.bind();\n  gl.uniformMatrix4fv(shader.viewProjectionLoc,\n                      false,\n                      viewProjection);\n  gl.uniformMatrix4fv(shader.viewInverseLoc,\n                      false,\n                      viewInverse);\n  // Draw all emitters\n  // FIXME: this is missing O3D's z-sorting logic from the\n  // zOrderedDrawList\n  for (var ii = 0; ii < this.drawables_.length; ++ii) {\n    this.drawables_[ii].draw(world, viewProjection, 0);\n  }\n};\n\n// Base element indices for the interleaved floating point data.\n// Each of the four corners of the particle has four floats for each\n// of these pieces of information.\ntdl.particles.UV_LIFE_TIME_FRAME_START_IDX = 0;\ntdl.particles.POSITION_START_TIME_IDX = 4;\ntdl.particles.VELOCITY_START_SIZE_IDX = 8;\ntdl.particles.ACCELERATION_END_SIZE_IDX = 12;\ntdl.particles.SPIN_START_SPIN_SPEED_IDX = 16;\ntdl.particles.ORIENTATION_IDX = 20;\ntdl.particles.COLOR_MULT_IDX = 24;\ntdl.particles.LAST_IDX = 28;\n\n/**\n * A ParticleEmitter\n * @private\n * @constructor\n * @param {tdl.particles.ParticleSystem} particleSystem The particle system\n *     to manage this emitter.\n * @param {tdl.textures.Texture?} opt_texture The texture to use\n *     for the particles. If you don't supply a texture a\n *     default is provided.\n * @param {tdl.particles.Particle~Clock?} opt_clock Clock function.\n */\ntdl.particles.ParticleEmitter = function(\n    particleSystem,\n    opt_texture,\n    opt_clock) {\n  opt_clock = opt_clock || particleSystem.timeSource_;\n\n  this.gl = particleSystem.gl;\n\n  this.createdParticles_ = false;\n\n  this.tmpWorld_ = new Float32Array(16);\n\n  // This Float32Array is used to store a single particle's data\n  // in the VBO. As of this writing there wasn't a way to store less\n  // than a full WebGLArray's data in a bufferSubData call.\n  this.singleParticleArray_ = new Float32Array(4 * tdl.particles.LAST_IDX);\n\n  // The VBO holding the particles' data, (re-)allocated in\n  // allocateParticles_().\n  this.particleBuffer_ = gl.createBuffer();\n\n  // The buffer object holding the particles' indices, (re-)allocated\n  // in allocateParticles_().\n  this.indexBuffer_ = gl.createBuffer();\n\n  // The number of particles that are stored in the particle buffer.\n  this.numParticles_ = 0;\n\n  this.rampTexture_ = particleSystem.defaultRampTexture;\n  this.colorTexture_ = opt_texture || particleSystem.defaultColorTexture;\n\n  /**\n   * The particle system managing this emitter.\n   * @type {tdl.particles.ParticleSystem}\n   */\n  this.particleSystem = particleSystem;\n\n  /**\n   * A function that is the source for the time for this emitter.\n   * @private\n   * @type {function(): number}\n   */\n  this.timeSource_ = opt_clock;\n\n  /**\n   * The translation for this ParticleEmitter. (FIXME: generalize.)\n   * @private\n   * @type {tdl.math.Vector3}\n   */\n  this.translation_ = [0, 0, 0];\n\n  // Set up the blend functions for drawing the particles.\n  this.setState(tdl.particles.ParticleStateIds.BLEND);\n};\n\n/**\n * Sets the world translation for this ParticleEmitter.\n * @param {tdl.math.Vector3} translation The translation for this emitter.\n */\ntdl.particles.ParticleEmitter.prototype.setTranslation = function(x, y, z) {\n  this.translation_[0] = x;\n  this.translation_[1] = y;\n  this.translation_[2] = z;\n};\n\n/**\n * Sets the blend state for the particles.\n * You can use this to set the emitter to draw with BLEND, ADD, SUBTRACT, etc.\n * @param {tdl.particles.ParticleStateIds} stateId The state you\n *        want.\n */\ntdl.particles.ParticleEmitter.prototype.setState = function(stateId) {\n  this.blendFunc_ = this.particleSystem.blendFuncs_[stateId];\n};\n\n/**\n * Sets the colorRamp for the particles.\n * The colorRamp is used as a multiplier for the texture. When a particle\n * starts it is multiplied by the first color, as it ages to progressed\n * through the colors in the ramp.\n *\n *     particleEmitter.setColorRamp([\n *       1, 0, 0, 1,    // red\n *       0, 1, 0, 1,    // green\n *       1, 0, 1, 0,    // purple but with zero alpha\n *     ]);\n *\n * The code above sets the particle to start red, change to green then\n * fade out while changing to purple.\n *\n * @param {number[]} colorRamp An array of color values in\n *     the form RGBA.\n */\ntdl.particles.ParticleEmitter.prototype.setColorRamp = function(colorRamp) {\n  var width = colorRamp.length / 4;\n  if (width % 1 != 0) {\n    throw 'colorRamp must have multiple of 4 entries';\n  }\n\n  if (this.rampTexture_ == this.particleSystem.defaultRampTexture) {\n    this.rampTexture_ = null;\n  }\n\n  this.rampTexture_ = this.particleSystem.createTextureFromFloats(width, 1, colorRamp, this.rampTexture_);\n};\n\n/**\n * Validates and adds missing particle parameters.\n * @param {tdl.particles.ParticleSpec} parameters The parameters to validate.\n */\ntdl.particles.ParticleEmitter.prototype.validateParameters = function(\n    parameters) {\n  var defaults = new tdl.particles.ParticleSpec();\n  for (var key in parameters) {\n    if (typeof defaults[key] === 'undefined') {\n      throw 'unknown particle parameter \"' + key + '\"';\n    }\n  }\n  for (var key in defaults) {\n    if (typeof parameters[key] === 'undefined') {\n      parameters[key] = defaults[key];\n    }\n  }\n};\n\n/**\n * Creates particles.\n * @private\n * @param {number} firstParticleIndex Index of first particle to create.\n * @param {number} numParticles The number of particles to create.\n * @param {tdl.particles.ParticleSpec} parameters The parameters for the\n *     emitters.\n * @param {tdl.particles.Particle~Setup?} opt_perParticleParamSetter function\n *        to setup particles.\n */\ntdl.particles.ParticleEmitter.prototype.createParticles_ = function(\n    firstParticleIndex,\n    numParticles,\n    parameters,\n    opt_perParticleParamSetter) {\n  var singleParticleArray = this.particleSystem.singleParticleArray_;\n  var gl = this.gl;\n\n  // Set the globals.\n  this.billboard_ = parameters.billboard;\n  this.timeRange_ = parameters.timeRange || 99999999;\n  this.numFrames_ = parameters.numFrames;\n  this.frameDuration_ = parameters.frameDuration;\n  this.worldVelocity_ = [ parameters.worldVelocity[0],\n                          parameters.worldVelocity[1],\n                          parameters.worldVelocity[2] ];\n  this.worldAcceleration_ = [ parameters.worldAcceleration[0],\n                              parameters.worldAcceleration[1],\n                              parameters.worldAcceleration[2] ];\n\n  var random = this.particleSystem.randomFunction_;\n\n  var plusMinus = function(range) {\n    return (random() - 0.5) * range * 2;\n  };\n\n  // TODO: change to not allocate.\n  var plusMinusVector = function(range) {\n    var v = [];\n    for (var ii = 0; ii < range.length; ++ii) {\n      v.push(plusMinus(range[ii]));\n    }\n    return v;\n  };\n\n  gl.bindBuffer(gl.ARRAY_BUFFER, this.particleBuffer_);\n\n  for (var ii = 0; ii < numParticles; ++ii) {\n    if (opt_perParticleParamSetter) {\n      opt_perParticleParamSetter(ii, parameters);\n    }\n    var pLifeTime = parameters.lifeTime;\n    var pStartTime = (parameters.startTime === null) ?\n        (ii * parameters.lifeTime / numParticles) : parameters.startTime;\n    var pFrameStart =\n        parameters.frameStart + plusMinus(parameters.frameStartRange);\n    var pPosition = tdl.math.addVector(\n        parameters.position, plusMinusVector(parameters.positionRange));\n    var pVelocity = tdl.math.addVector(\n        parameters.velocity, plusMinusVector(parameters.velocityRange));\n    var pAcceleration = tdl.math.addVector(\n        parameters.acceleration,\n        plusMinusVector(parameters.accelerationRange));\n    var pColorMult = tdl.math.addVector(\n        parameters.colorMult, plusMinusVector(parameters.colorMultRange));\n    var pSpinStart =\n        parameters.spinStart + plusMinus(parameters.spinStartRange);\n    var pSpinSpeed =\n        parameters.spinSpeed + plusMinus(parameters.spinSpeedRange);\n    var pStartSize =\n        parameters.startSize + plusMinus(parameters.startSizeRange);\n    var pEndSize = parameters.endSize + plusMinus(parameters.endSizeRange);\n    var pOrientation = parameters.orientation;\n\n    // make each corner of the particle.\n    for (var jj = 0; jj < 4; ++jj) {\n      var offset0 = tdl.particles.LAST_IDX * jj;\n      var offset1 = offset0 + 1;\n      var offset2 = offset0 + 2;\n      var offset3 = offset0 + 3;\n\n      singleParticleArray[tdl.particles.UV_LIFE_TIME_FRAME_START_IDX + offset0] = tdl.particles.CORNERS_[jj][0];\n      singleParticleArray[tdl.particles.UV_LIFE_TIME_FRAME_START_IDX + offset1] = tdl.particles.CORNERS_[jj][1];\n      singleParticleArray[tdl.particles.UV_LIFE_TIME_FRAME_START_IDX + offset2] = pLifeTime;\n      singleParticleArray[tdl.particles.UV_LIFE_TIME_FRAME_START_IDX + offset3] = pFrameStart;\n\n      singleParticleArray[tdl.particles.POSITION_START_TIME_IDX + offset0] = pPosition[0];\n      singleParticleArray[tdl.particles.POSITION_START_TIME_IDX + offset1] = pPosition[1];\n      singleParticleArray[tdl.particles.POSITION_START_TIME_IDX + offset2] = pPosition[2];\n      singleParticleArray[tdl.particles.POSITION_START_TIME_IDX + offset3] = pStartTime;\n\n      singleParticleArray[tdl.particles.VELOCITY_START_SIZE_IDX + offset0] = pVelocity[0];\n      singleParticleArray[tdl.particles.VELOCITY_START_SIZE_IDX + offset1] = pVelocity[1];\n      singleParticleArray[tdl.particles.VELOCITY_START_SIZE_IDX + offset2] = pVelocity[2];\n      singleParticleArray[tdl.particles.VELOCITY_START_SIZE_IDX + offset3] = pStartSize;\n\n      singleParticleArray[tdl.particles.ACCELERATION_END_SIZE_IDX + offset0] = pAcceleration[0];\n      singleParticleArray[tdl.particles.ACCELERATION_END_SIZE_IDX + offset1] = pAcceleration[1];\n      singleParticleArray[tdl.particles.ACCELERATION_END_SIZE_IDX + offset2] = pAcceleration[2];\n      singleParticleArray[tdl.particles.ACCELERATION_END_SIZE_IDX + offset3] = pEndSize;\n\n      singleParticleArray[tdl.particles.SPIN_START_SPIN_SPEED_IDX + offset0] = pSpinStart;\n      singleParticleArray[tdl.particles.SPIN_START_SPIN_SPEED_IDX + offset1] = pSpinSpeed;\n      singleParticleArray[tdl.particles.SPIN_START_SPIN_SPEED_IDX + offset2] = 0;\n      singleParticleArray[tdl.particles.SPIN_START_SPIN_SPEED_IDX + offset3] = 0;\n\n      singleParticleArray[tdl.particles.ORIENTATION_IDX + offset0] = pOrientation[0];\n      singleParticleArray[tdl.particles.ORIENTATION_IDX + offset1] = pOrientation[1];\n      singleParticleArray[tdl.particles.ORIENTATION_IDX + offset2] = pOrientation[2];\n      singleParticleArray[tdl.particles.ORIENTATION_IDX + offset3] = pOrientation[3];\n\n      singleParticleArray[tdl.particles.COLOR_MULT_IDX + offset0] = pColorMult[0];\n      singleParticleArray[tdl.particles.COLOR_MULT_IDX + offset1] = pColorMult[1];\n      singleParticleArray[tdl.particles.COLOR_MULT_IDX + offset2] = pColorMult[2];\n      singleParticleArray[tdl.particles.COLOR_MULT_IDX + offset3] = pColorMult[3];\n    }\n\n    // Upload this particle's information into the VBO.\n    // FIXME: probably want to make fewer bufferSubData calls\n    gl.bufferSubData(gl.ARRAY_BUFFER,\n                       singleParticleArray.byteLength * (ii + firstParticleIndex),\n                       singleParticleArray);\n  }\n\n  this.createdParticles_ = true;\n};\n\n/**\n * Allocates particles.\n * @private\n * @param {number} numParticles Number of particles to allocate.\n */\ntdl.particles.ParticleEmitter.prototype.allocateParticles_ = function(\n    numParticles) {\n  if (this.numParticles_ != numParticles) {\n    var gl = this.gl;\n    gl.bindBuffer(gl.ARRAY_BUFFER, this.particleBuffer_);\n    gl.bufferData(gl.ARRAY_BUFFER,\n                  numParticles * this.particleSystem.singleParticleArray_.byteLength,\n                  gl.DYNAMIC_DRAW);\n    var numIndices = 6 * numParticles;\n    if (numIndices > 65536) {\n      throw \"can't have more than 10922 particles per emitter\";\n    }\n    var indices = new Uint16Array(numIndices);\n    var idx = 0;\n    for (var ii = 0; ii < numParticles; ++ii) {\n      // Make 2 triangles for the quad.\n      var startIndex = ii * 4;\n      indices[idx++] = startIndex + 0;\n      indices[idx++] = startIndex + 1;\n      indices[idx++] = startIndex + 2;\n      indices[idx++] = startIndex + 0;\n      indices[idx++] = startIndex + 2;\n      indices[idx++] = startIndex + 3;\n    }\n    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,\n                    this.indexBuffer_);\n    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,\n                    indices,\n                    gl.STATIC_DRAW);\n    this.numParticles_ = numParticles;\n  }\n};\n\n/**\n * Sets the parameters of the particle emitter.\n *\n * Each of these parameters are in pairs. The used to create a table\n * of particle parameters. For each particle a specfic value is\n * set like this\n *\n *     particle.field = value + Math.random() - 0.5 * valueRange * 2;\n *\n * or in English\n *\n *     particle.field = value plus or minus valueRange.\n *\n * So for example, if you wanted a value from 10 to 20 you'd pass 15 for value\n * and 5 for valueRange because\n *\n *     15 + or - 5  = (10 to 20)\n *\n * @param {tdl.particles.ParticleSpec} parameters The parameters for the\n *     emitters.\n * @param {tdl.particles.Particle~Setup?} opt_perParticleParamSetter function\n *        to setup particles.\n */\ntdl.particles.ParticleEmitter.prototype.setParameters = function(\n    parameters,\n    opt_perParticleParamSetter) {\n  this.validateParameters(parameters);\n\n  var numParticles = parameters.numParticles;\n\n  this.allocateParticles_(numParticles);\n  this.createParticles_(\n      0,\n      numParticles,\n      parameters,\n      opt_perParticleParamSetter);\n};\n\n/**\n * Draws the particles for this ParticleEmitter\n * @param {tdl.math.Matrix4|tdl.fast.Matrix4} world The world\n *        matrix.\n * @param {tdl.math.Matrix4|tdl.fast.Matrix4} viewProjection The\n *        viewProjection matrix.\n * @param {number} timeOffset time offset to compute state of\n *        particles.\n */\ntdl.particles.ParticleEmitter.prototype.draw = function(world, viewProjection, timeOffset) {\n  if (!this.createdParticles_) {\n    return;\n  }\n\n  var gl = this.gl;\n\n  // Set up blend function\n  gl.enable(gl.BLEND);\n  var blendFunc = this.blendFunc_;\n  gl.blendFunc(blendFunc.src, blendFunc.dest);\n  if (blendFunc.eq) {\n    gl.blendEquation(blendFunc.eq);\n  } else {\n    gl.blendEquation(gl.FUNC_ADD);\n  }\n\n  var shader = this.particleSystem.shaders[this.billboard_ ? 1 : 0];\n  shader.bind();\n\n  var tmpWorld = this.tmpWorld_;\n  tdl.fast.matrix4.copy(tmpWorld, world);\n\n  tdl.fast.matrix4.translate(tmpWorld, this.translation_);\n  gl.uniformMatrix4fv(shader.worldLoc,\n                      false,\n                      tmpWorld);\n  if (!this.billboard_) {\n    var worldViewProjection = new Float32Array(16);\n    tdl.fast.matrix4.mul(worldViewProjection, tmpWorld, viewProjection);\n    gl.uniformMatrix4fv(shader.worldViewProjectionLoc,\n                        false,\n                        worldViewProjection);\n  }\n\n  gl.uniform3f(shader.worldVelocityLoc,\n               this.worldVelocity_[0],\n               this.worldVelocity_[1],\n               this.worldVelocity_[2]);\n  gl.uniform3f(shader.worldAccelerationLoc,\n               this.worldAcceleration_[0],\n               this.worldAcceleration_[1],\n               this.worldAcceleration_[2]);\n  gl.uniform1f(shader.timeRangeLoc, this.timeRange_);\n  gl.uniform1f(shader.numFramesLoc, this.numFrames_);\n  gl.uniform1f(shader.frameDurationLoc, this.frameDuration_);\n\n  // Compute and set time\n  var curTime = this.timeSource_();\n  gl.uniform1f(shader.timeLoc, curTime);\n  // This is non-zero only for OneShots\n  gl.uniform1f(shader.timeOffsetLoc, timeOffset);\n\n  // Set up textures\n  gl.activeTexture(gl.TEXTURE0);\n  gl.bindTexture(gl.TEXTURE_2D, this.rampTexture_);\n  gl.uniform1i(shader.rampSamplerLoc, 0);\n  gl.activeTexture(gl.TEXTURE1);\n  gl.bindTexture(gl.TEXTURE_2D, this.colorTexture_);\n  gl.uniform1i(shader.colorSamplerLoc, 1);\n  gl.activeTexture(gl.TEXTURE0);\n\n  // Set up vertex attributes\n  var sizeofFloat = 4;\n  var stride = sizeofFloat * tdl.particles.LAST_IDX;\n  gl.bindBuffer(gl.ARRAY_BUFFER, this.particleBuffer_);\n  gl.vertexAttribPointer(shader.uvLifeTimeFrameStartLoc, 4, gl.FLOAT, false, stride,\n                         sizeofFloat * tdl.particles.UV_LIFE_TIME_FRAME_START_IDX);\n  gl.enableVertexAttribArray(shader.uvLifeTimeFrameStartLoc);\n  gl.vertexAttribPointer(shader.positionStartTimeLoc, 4, gl.FLOAT, false, stride,\n                         sizeofFloat * tdl.particles.POSITION_START_TIME_IDX);\n  gl.enableVertexAttribArray(shader.positionStartTimeLoc);\n  gl.vertexAttribPointer(shader.velocityStartSizeLoc, 4, gl.FLOAT, false, stride,\n                         sizeofFloat * tdl.particles.VELOCITY_START_SIZE_IDX);\n  gl.enableVertexAttribArray(shader.velocityStartSizeLoc);\n  gl.vertexAttribPointer(shader.accelerationEndSizeLoc, 4, gl.FLOAT, false, stride,\n                         sizeofFloat * tdl.particles.ACCELERATION_END_SIZE_IDX);\n  gl.enableVertexAttribArray(shader.accelerationEndSizeLoc);\n  gl.vertexAttribPointer(shader.spinStartSpinSpeedLoc, 4, gl.FLOAT, false, stride,\n                         sizeofFloat * tdl.particles.SPIN_START_SPIN_SPEED_IDX);\n  gl.enableVertexAttribArray(shader.spinStartSpinSpeedLoc);\n  // Only for non-billboarded, i.e., 3D, particles\n  if (shader.orientationLoc != undefined) {\n    gl.vertexAttribPointer(shader.orientationLoc, 4, gl.FLOAT, false, stride,\n                           sizeofFloat * tdl.particles.ORIENTATION_IDX);\n    gl.enableVertexAttribArray(shader.orientationLoc);\n  }\n  // NOTE: comment out the next two calls if using debug shader which\n  // only outputs red.\n  gl.vertexAttribPointer(shader.colorMultLoc, 4, gl.FLOAT, false, stride,\n                         sizeofFloat * tdl.particles.COLOR_MULT_IDX);\n  gl.enableVertexAttribArray(shader.colorMultLoc);\n  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer_);\n  gl.drawElements(gl.TRIANGLES, this.numParticles_ * 6,\n                  gl.UNSIGNED_SHORT, 0);\n\n  gl.disableVertexAttribArray(shader.uvLifeTimeFrameStartLoc);\n  gl.disableVertexAttribArray(shader.positionStartTimeLoc);\n  gl.disableVertexAttribArray(shader.velocityStartSizeLoc);\n  gl.disableVertexAttribArray(shader.accelerationEndSizeLoc);\n  gl.disableVertexAttribArray(shader.spinStartSpinSpeedLoc);\n  // FIXME: only for billboarded, i.e., 3D, particles?\n  if (shader.orientationLoc != undefined) {\n    gl.disableVertexAttribArray(shader.orientationLoc);\n  }\n  gl.disableVertexAttribArray(shader.colorMultLoc);\n};\n\n/**\n * Creates a OneShot particle emitter instance.\n * You can use this for dust puffs, explosions, fireworks, etc...\n * @return {tdl.particles.OneShot} A OneShot object.\n */\ntdl.particles.ParticleEmitter.prototype.createOneShot = function() {\n  return new tdl.particles.OneShot(this);\n};\n\n/**\n * An object to manage a particle emitter instance as a one\n * shot. Examples of one shot effects are things like an explosion,\n * some fireworks. Note that once a OneShot has been created for a\n * given emitter, that emitter can only be treated as containing one\n * or more OneShots.\n * @private\n * @constructor\n * @param {tdl.particles.ParticleEmitter} emitter The emitter to use for the\n *     one shot.\n */\ntdl.particles.OneShot = function(emitter) {\n  this.emitter_ = emitter;\n\n  /**\n   * Translation for OneShot.\n   * @type {tdl.math.Vector3}\n   */\n  this.world_ = tdl.fast.matrix4.translation(new Float32Array(16), [0, 0, 0]);\n  this.tempWorld_ = tdl.fast.matrix4.translation(new Float32Array(16), [0, 0, 0]);\n  this.timeOffset_ = 0;\n  this.visible_ = false;\n\n  // Remove the parent emitter from the particle system's drawable\n  // list (if it's still there) and add ourselves instead.\n  var particleSystem = emitter.particleSystem;\n  var idx = particleSystem.drawables_.indexOf(emitter);\n  if (idx >= 0) {\n    particleSystem.drawables_.splice(idx, 1);\n  }\n  particleSystem.drawables_.push(this);\n};\n\n/**\n * Triggers the oneshot.\n *\n * Note: You must have set the parent either at creation, with setParent, or by\n * passing in a parent here.\n *\n * @param {tdl.math.Vector3} opt_position The position of the one shot\n *     relative to its parent.\n */\ntdl.particles.OneShot.prototype.trigger = function(opt_world) {\n  if (opt_world) {\n    this.world_.set(opt_world)\n  }\n  this.visible_ = true;\n  this.timeOffset_ = this.emitter_.timeSource_();\n};\n\n/**\n * Draws the oneshot.\n *\n * @private\n * @param {tdl.math.Matrix4|tdl.fast.Matrix4} world The world\n *        matrix.\n * @param {tdl.math.Matrix4|tdl.fast.Matrix4} viewProjection The\n *        viewProjection matrix.\n * @param {number} timeOffset time offset to compute state of\n *        particles.\n */\ntdl.particles.OneShot.prototype.draw = function(world, viewProjection, timeOffset) {\n  if (this.visible_) {\n    tdl.fast.matrix4.mul(this.tempWorld_, this.world_, world);\n    this.emitter_.draw(this.tempWorld_, viewProjection, this.timeOffset_);\n  }\n};\n\n/**\n * A type of emitter to use for particle effects that leave trails like exhaust.\n * @constructor\n * @extends {tdl.particles.ParticleEmitter}\n * @param {tdl.particles.ParticleSystem} particleSystem The particle system\n *     to manage this emitter.\n * @param {number} maxParticles Maximum number of particles to appear at once.\n * @param {tdl.particles.ParticleSpec} parameters The parameters used to\n *     generate particles.\n * @param {tdl.textures.Texture?} opt_texture The texture to use\n *     for the particles. If you don't supply a texture a\n *     default is provided.\n * @param {tdl.particles.Particle~Setup?} opt_perParticleParamSetter function\n *        to setup particles.\n * @param {tdl.particles.Particle~Clock?} opt_clock A function to be the clock\n *        for the emitter.\n */\ntdl.particles.Trail = function(\n    particleSystem,\n    maxParticles,\n    parameters,\n    opt_texture,\n    opt_perParticleParamSetter,\n    opt_clock) {\n  tdl.particles.ParticleEmitter.call(\n      this, particleSystem, opt_texture, opt_clock);\n\n  this.allocateParticles_(maxParticles);\n  this.validateParameters(parameters);\n\n  this.parameters = parameters;\n  this.perParticleParamSetter = opt_perParticleParamSetter;\n  this.birthIndex_ = 0;\n  this.maxParticles_ = maxParticles;\n};\n\ntdl.base.inherit(tdl.particles.Trail, tdl.particles.ParticleEmitter);\n\n/**\n * Births particles from this Trail.\n * @param {tdl.math.Vector3} position Position to birth particles at.\n */\ntdl.particles.Trail.prototype.birthParticles = function(position) {\n  var numParticles = this.parameters.numParticles;\n  this.parameters.startTime = this.timeSource_();\n  this.parameters.position = position;\n  while (this.birthIndex_ + numParticles >= this.maxParticles_) {\n    var numParticlesToEnd = this.maxParticles_ - this.birthIndex_;\n    this.createParticles_(this.birthIndex_,\n                          numParticlesToEnd,\n                          this.parameters,\n                          this.perParticleParamSetter);\n    numParticles -= numParticlesToEnd;\n    this.birthIndex_ = 0;\n  }\n  this.createParticles_(this.birthIndex_,\n                        numParticles,\n                        this.parameters,\n                        this.perParticleParamSetter);\n  this.birthIndex_ += numParticles;\n};\n\n/**\n * Manages OneShots\n * @constructor\n * @param {tdl.particles.ParticleEmitter} emitter Emitter to\n *        use.\n * @param {number} numOneshots Number of one shots to manage.\n */\ntdl.particles.OneShotManager = function(emitter, numOneshots) {\n  this.numOneshots = numOneshots;\n  this.oneshotIndex = 0;\n  this.oneshots = [];\n  for (var ii = 0; ii < numOneshots; ++ii) {\n     this.oneshots.push(emitter.createOneShot());\n  }\n};\n\n/**\n * Starts a one shot\n * @param {tdl.math.Matrix4|tdl.fast.Matrix4} worldMatrix The\n *        world matrix to start the one shot\n */\ntdl.particles.OneShotManager.prototype.startOneShot = function(worldMatrix) {\n  this.oneshots[this.oneshotIndex].trigger(worldMatrix);\n  this.oneshotIndex = (this.oneshotIndex + 1) % this.numOneshots;\n};\n\n/**\n * Creates a OneShot manager\n * @param {tdl.particles.ParticleEmitter} emitter Emitter to\n *        use.\n * @param {number} numOneshots Number of one shots to manage.\n * @returns {tdl.particles.OneShotManager} the created\n *        OneShotManager\n */\ntdl.particles.createOneShotManager = function(emitter, numOneshots) {\n  return new tdl.particles.OneShotManager(emitter, numOneshots);\n};\n\n\nreturn tdl.particles;\n});\n"
  },
  {
    "path": "tdl/primitives.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains objects to make primitives.\n */\ndefine(['./base-rs', './math', './log'], function(BaseRS, Maths, Log) {\n\ntdl.provide('tdl.primitives');\n/**\n * A module for primitives.\n * @namespace\n */\ntdl.primitives = tdl.primitives || {};\n\n/**\n * AttribBuffer manages a TypedArray as an array of vectors.\n * @constructor\n * @param {number} numComponents Number of components per\n *     vector.\n * @param {(number|number[])} numElements Number of vectors or\n *        the data.\n * @param {string} opt_type The type of the TypedArray to\n *     create. Default = 'Float32Array'.\n * @param {number[]|ArrayBufferView} opt_data The data for the\n *        array.\n */\ntdl.primitives.AttribBuffer = function(\n    numComponents, numElements, opt_type) {\n  opt_type = opt_type || 'Float32Array';\n  var type = window[opt_type];\n  if (numElements.length) {\n    this.buffer = new type(numElements);\n    numElements = this.buffer.length / numComponents;\n    this.cursor = numElements;\n  } else {\n    this.buffer = new type(numComponents * numElements);\n    this.cursor = 0;\n  }\n  this.numComponents = numComponents;\n  this.numElements = numElements;\n  this.type = opt_type;\n};\n\n/**\n * Stride for this AtrribBuffer\n * @return {number} stride\n */\ntdl.primitives.AttribBuffer.prototype.stride = function() {\n  return 0;\n};\n\n/**\n * Offset of this AttribBuffer\n * @returns {number} offset\n */\ntdl.primitives.AttribBuffer.prototype.offset = function() {\n  return 0;\n};\n\n/**\n * Gets an element from this AttribBuffer\n * @param number index index of element\n * @return {number[]} returned element\n */\ntdl.primitives.AttribBuffer.prototype.getElement = function(index) {\n  var offset = index * this.numComponents;\n  var value = [];\n  for (var ii = 0; ii < this.numComponents; ++ii) {\n    value.push(this.buffer[offset + ii]);\n  }\n  return value;\n};\n\n/**\n * Sets an element in this AttribBuffer\n * @param {number} index index of element to set\n * @param {number[]|ArrayBufferView} value new value for element\n */\ntdl.primitives.AttribBuffer.prototype.setElement = function(index, value) {\n  var offset = index * this.numComponents;\n  for (var ii = 0; ii < this.numComponents; ++ii) {\n    this.buffer[offset + ii] = value[ii];\n  }\n};\n\n/**\n * Sets a range of elements\n * @param {number} index index of starting element\n * @param {number} count number of elements to set\n * @param {number[]|ArrayBufferView) value values of new\n *        elements\n */\ntdl.primitives.AttribBuffer.prototype.fillRange = function(index, count, value) {\n  var offset = index * this.numComponents;\n  for (var jj = 0; jj < count; ++jj) {\n    for (var ii = 0; ii < this.numComponents; ++ii) {\n      this.buffer[offset++] = value[ii];\n    }\n  }\n};\n\n/**\n * Clones an AttribBuffer.\n * @return {tdl.primitives.AttribBuffer} new AttribBuffer that\n *         is a copy of this one.\n */\ntdl.primitives.AttribBuffer.prototype.clone = function() {\n  var copy = new tdl.primitives.AttribBuffer(\n      this.numComponents, this.numElements, this.type);\n  copy.pushArray(this);\n  return copy;\n}\n\n/**\n * Push an element into an AttribBuffer.\n * AttribBuffers start with a cursor allowed you to\n * progressively *push* values into them up to their size limit.\n * @param {number[]|ArrayBufferView} value value of element to\n *        push.\n */\ntdl.primitives.AttribBuffer.prototype.push = function(value) {\n  this.setElement(this.cursor++, value);\n};\n\n/**\n * Push an array of elements from another AttribBuffer\n * @param {tdl.primitives.AttribBuffer} array buffer to push\n *        elements from.\n */\ntdl.primitives.AttribBuffer.prototype.pushArray = function(array) {\n//  this.buffer.set(array, this.cursor * this.numComponents);\n//  this.cursor += array.numElements;\n  for (var ii = 0; ii < array.numElements; ++ii) {\n    this.push(array.getElement(ii));\n  }\n};\n\n/**\n * Push elements from AttribBuffer into this buffer adding\n * offsets.\n *\n * This is mostly used to contactinate index buffers.\n *\n * @param {tdl.primtives.AttribBuffer} array buffer to copy from\n * @param {number[]|ArrayBufferView} offset array of offsets,\n *        one for each element.\n */\ntdl.primitives.AttribBuffer.prototype.pushArrayWithOffset =\n   function(array, offset) {\n  for (var ii = 0; ii < array.numElements; ++ii) {\n    var elem = array.getElement(ii);\n    for (var jj = 0; jj < offset.length; ++jj) {\n      elem[jj] += offset[jj];\n    }\n    this.push(elem);\n  }\n};\n\n/**\n * Computes the extents\n * @return {{min: tdl.math.Vector3, max:tdl.math.Vector3}}\n *     The min and max extents.\n */\ntdl.primitives.AttribBuffer.prototype.computeExtents = function() {\n  var numElements = this.numElements;\n  var numComponents = this.numComponents;\n  var minExtent = this.getElement(0);\n  var maxExtent = this.getElement(0);\n  for (var ii = 1; ii < numElements; ++ii) {\n    var element = this.getElement(ii);\n    for (var jj = 0; jj < numComponents; ++jj) {\n      minExtent[jj] = Math.min(minExtent[jj], element[jj]);\n      maxExtent[jj] = Math.max(maxExtent[jj], element[jj]);\n    }\n  }\n  return {min: minExtent, max: maxExtent};\n};\n\n/**\n * Multiplies elements by a vector.\n * @param {tdl.primitives.AttribBuffer} array AttribBuffer to\n *     reorient.\n * @param {number[]|ArrayBufferView} multiplier vector by\n *     which to multiply.\n */\ntdl.primitives.mulComponents = function(array, multiplier) {\n  var numElements = array.numElements;\n  var numComponents = array.numComponents;\n  for (var ii = 0; ii < numElements; ++ii) {\n    var element = array.getElement(ii);\n    for (var jj = 0; jj < numComponents; ++jj) {\n      element[jj] *= multiplier[jj];\n    }\n    array.setElement(ii, element);\n  }\n};\n\n/**\n * Reorients positions by the given matrix. In other words, it\n * multiplies each vertex by the given matrix.\n * @param {tdl.primitives.AttribBuffer} array AttribBuffer to\n *     reorient.\n * @param {tdl.math.Matrix4} matrix Matrix by which to\n *     multiply.\n */\ntdl.primitives.reorientPositions = function(array, matrix) {\n  var math = tdl.math;\n  var numElements = array.numElements;\n  for (var ii = 0; ii < numElements; ++ii) {\n    array.setElement(ii,\n        math.matrix4.transformPoint(matrix,\n            array.getElement(ii)));\n  }\n};\n\n/**\n * Reorients normals by the inverse-transpose of the given\n * matrix..\n * @param {tdl.primitives.AttribBuffer} array AttribBuffer to\n *     reorient.\n * @param {tdl.math.Matrix4} matrix Matrix by which to\n *     multiply.\n */\ntdl.primitives.reorientNormals = function(array, matrix) {\n  var math = tdl.math;\n  var numElements = array.numElements;\n  for (var ii = 0; ii < numElements; ++ii) {\n    array.setElement(ii,\n        math.matrix4.transformNormal(matrix,\n            array.getElement(ii)));\n  }\n};\n\n/**\n * Reorients directions by the given matrix..\n * @param {tdl.primitives.AttribBuffer} array AttribBuffer to\n *     reorient.\n * @param {tdl.math.Matrix4} matrix Matrix by which to\n *     multiply.\n */\ntdl.primitives.reorientDirections = function(array, matrix) {\n  var math = tdl.math;\n\n  var numElements = array.numElements;\n  for (var ii = 0; ii < numElements; ++ii) {\n    array.setElement(ii,\n        math.matrix4.transformDirection(matrix,\n            array.getElement(ii)));\n  }\n};\n\n/**\n * Reorients arrays by the given matrix. Assumes arrays have\n * names that start with 'position', 'normal', 'tangent',\n * 'binormal'\n *\n * @param {Object.<string, tdl.primitives.AttribBuffer>} arrays\n *        The arrays to remap.\n * @param {tdl.math.Matrix4} matrix The matrix to remap by\n */\ntdl.primitives.reorient = function(arrays, matrix) {\n  for (var array in arrays) {\n    if (array.match(/^position/)) {\n      tdl.primitives.reorientPositions(arrays[array], matrix);\n    } else if (array.match(/^normal/)) {\n      tdl.primitives.reorientNormals(arrays[array], matrix);\n    } else if (array.match(/^tangent/) || array.match(/^binormal/)) {\n      tdl.primitives.reorientDirections(arrays[array], matrix);\n    }\n  }\n};\n\n/**\n * Creates tangents and normals.\n *\n * @param {tdl.primitives.AttibBuffer} positionArray Positions\n * @param {tdl.primitives.AttibBuffer} normalArray Normals\n * @param {tdl.primitives.AttibBuffer} normalMapUVArray UVs for the normal map.\n * @param {tdl.primitives.AttibBuffer} triangles The indicies of the trianlges.\n * @return {{tangent: {tdl.primitives.AttribBuffer}, binormal:\n *           {tdl.primitives.AttribBuffer}}\n */\ntdl.primitives.createTangentsAndBinormals = function(\n    positionArray, normalArray, normalMapUVArray, triangles) {\n  var math = tdl.math;\n  // Maps from position, normal key to tangent and binormal matrix.\n  var tangentFrames = {};\n\n  // Rounds a vector to integer components.\n  function roundVector(v) {\n    return [Math.round(v[0]), Math.round(v[1]), Math.round(v[2])];\n  }\n\n  // Generates a key for the tangentFrames map from a position and normal\n  // vector. Rounds position and normal to allow some tolerance.\n  function tangentFrameKey(position, normal) {\n    return roundVector(math.mulVectorScalar(position, 100)) + ',' +\n        roundVector(math.mulVectorScalar(normal, 100));\n  }\n\n  // Accumulates into the tangent and binormal matrix at the approximate\n  // position and normal.\n  function addTangentFrame(position, normal, tangent, binormal) {\n    var key = tangentFrameKey(position, normal);\n    var frame = tangentFrames[key];\n    if (!frame) {\n      frame = [[0, 0, 0], [0, 0, 0]];\n    }\n    frame[0] = math.addVector(frame[0], tangent);\n    frame[1] = math.addVector(frame[1], binormal);\n    tangentFrames[key] = frame;\n  }\n\n  // Get the tangent and binormal matrix at the approximate position and\n  // normal.\n  function getTangentFrame(position, normal) {\n    var key = tangentFrameKey(position, normal);\n    return tangentFrames[key];\n  }\n\n  var numTriangles = triangles.numElements;\n  for (var triangleIndex = 0; triangleIndex < numTriangles; ++triangleIndex) {\n    // Get the vertex indices, uvs and positions for the triangle.\n    var vertexIndices = triangles.getElement(triangleIndex);\n    var uvs = [];\n    var positions = [];\n    var normals = [];\n    for (var i = 0; i < 3; ++i) {\n      var vertexIndex = vertexIndices[i];\n      uvs[i] = normalMapUVArray.getElement(vertexIndex);\n      positions[i] = positionArray.getElement(vertexIndex);\n      normals[i] = normalArray.getElement(vertexIndex);\n    }\n\n    // Calculate the tangent and binormal for the triangle using method\n    // described in Maya documentation appendix A: tangent and binormal\n    // vectors.\n    var tangent = [0, 0, 0];\n    var binormal = [0, 0, 0];\n    for (var axis = 0; axis < 3; ++axis) {\n      var edge1 = [positions[1][axis] - positions[0][axis],\n                   uvs[1][0] - uvs[0][0], uvs[1][1] - uvs[0][1]];\n      var edge2 = [positions[2][axis] - positions[0][axis],\n                   uvs[2][0] - uvs[0][0], uvs[2][1] - uvs[0][1]];\n      var edgeCross = math.normalize(math.cross(edge1, edge2));\n      if (edgeCross[0] == 0) {\n        edgeCross[0] = 1;\n      }\n      tangent[axis] = -edgeCross[1] / edgeCross[0];\n      binormal[axis] = -edgeCross[2] / edgeCross[0];\n    }\n\n    // Normalize the tangent and binornmal.\n    var tangentLength = math.length(tangent);\n    if (tangentLength > 0.00001) {\n      tangent = math.mulVectorScalar(tangent, 1 / tangentLength);\n    }\n    var binormalLength = math.length(binormal);\n    if (binormalLength > 0.00001) {\n      binormal = math.mulVectorScalar(binormal, 1 / binormalLength);\n    }\n\n    // Accumulate the tangent and binormal into the tangent frame map.\n    for (var i = 0; i < 3; ++i) {\n      addTangentFrame(positions[i], normals[i], tangent, binormal);\n    }\n  }\n\n  // Add the tangent and binormal streams.\n  var numVertices = positionArray.numElements;\n  var tangents = new tdl.primitives.AttribBuffer(3, numVertices);\n  var binormals = new tdl.primitives.AttribBuffer(3, numVertices);\n\n  // Extract the tangent and binormal for each vertex.\n  for (var vertexIndex = 0; vertexIndex < numVertices; ++vertexIndex) {\n    var position = positionArray.getElement(vertexIndex);\n    var normal = normalArray.getElement(vertexIndex);\n    var frame = getTangentFrame(position, normal);\n\n    // Orthonormalize the tangent with respect to the normal.\n    var tangent = frame[0];\n    tangent = math.subVector(\n        tangent, math.mulVectorScalar(normal, math.dot(normal, tangent)));\n    var tangentLength = math.length(tangent);\n    if (tangentLength > 0.00001) {\n      tangent = math.mulVectorScalar(tangent, 1 / tangentLength);\n    }\n\n    // Orthonormalize the binormal with respect to the normal and the tangent.\n    var binormal = frame[1];\n    binormal = math.subVector(\n        binormal, math.mulVectorScalar(tangent, math.dot(tangent, binormal)));\n    binormal = math.subVector(\n        binormal, math.mulVectorScalar(normal, math.dot(normal, binormal)));\n    var binormalLength = math.length(binormal);\n    if (binormalLength > 0.00001) {\n      binormal = math.mulVectorScalar(binormal, 1 / binormalLength);\n    }\n\n    tangents.push(tangent);\n    binormals.push(binormal);\n  }\n\n  return {\n    tangent: tangents,\n    binormal: binormals};\n};\n\n/**\n * Adds tangents and binormals.\n *\n * @param {Object.<string,tdl.primitives.AttribBuffer>} arrays Arrays containing position,\n *        normal and texCoord.\n */\ntdl.primitives.addTangentsAndBinormals = function(arrays) {\n  var bn = tdl.primitives.createTangentsAndBinormals(\n      arrays.position,\n      arrays.normal,\n      arrays.texCoord,\n      arrays.indices);\n  arrays.tangent = bn.tangent;\n  arrays.binormal = bn.binormal;\n  return arrays;\n};\n\n/**\n * Clones a set of arrays\n * @param {Object.<string,tdl.primitives.AttribBuffer>} arrays\n *        Arrays to clone.\n * @return {Object.<string,tdl.primitives.AttribBuffer>}\n *        new arrays.\n */\ntdl.primitives.clone = function(arrays) {\n  var newArrays = { };\n  for (var array in arrays) {\n    newArrays[array] = arrays[array].clone();\n  }\n  return newArrays;\n};\n\n/**\n * Concats 2 or more sets of arrays. Assumes each set of arrays has arrays that\n * match the other sets.\n * @param {Array.<Object.<string, tdl.primitives.AttribBuffer>>}\n *        arrays Arrays to concat\n * @return {Object.<string, tdl.primitives.AttribBuffer>}\n *         concatenated result.\n */\ntdl.primitives.concat = function(arrayOfArrays) {\n  var names = {};\n  var baseName;\n  // get names of all arrays.\n  for (var ii = 0; ii < arrayOfArrays.length; ++ii) {\n    var arrays = arrayOfArrays[ii];\n    for (var name in arrays) {\n      if (!names[name]) {\n        names[name] = [];\n      }\n      if (!baseName && name != 'indices') {\n        baseName = name;\n      }\n      var array = arrays[name];\n      names[name].push(array.numElements);\n    }\n  }\n\n  var base = names[baseName];\n\n  var newArrays = {};\n  for (var name in names) {\n    var numElements = 0;\n    var numComponents;\n    var type;\n    for (var ii = 0; ii < arrayOfArrays.length; ++ii) {\n      var arrays = arrayOfArrays[ii];\n      var array = arrays[name];\n      numElements += array.numElements;\n      numComponents = array.numComponents;\n      type = array.type;\n    }\n    var newArray = new tdl.primitives.AttribBuffer(\n        numComponents, numElements, type);\n    var baseIndex = 0;\n    for (var ii = 0; ii < arrayOfArrays.length; ++ii) {\n      var arrays = arrayOfArrays[ii];\n      var array = arrays[name];\n      if (name == 'indices') {\n        newArray.pushArrayWithOffset(\n            array, [baseIndex, baseIndex, baseIndex]);\n        baseIndex += base[ii];\n      } else {\n        newArray.pushArray(array);\n      }\n    }\n    newArrays[name] = newArray;\n  }\n  return newArrays;\n};\n\n/**\n * Same as `tdl.primitives.concat` except this one returns an\n * array of arrays if the models have indices. This is because\n * WebGL can only handle 16bit indices (ie, < 65536) So, as it\n * is concatenating, if the data would make indices > 65535 it\n * starts a new set of arrays.\n *\n * @param {Array.<Object.<string, tdl.primitives.AttribBuffer>>}\n *        arrays Arrays to concat\n * @return {{arrays:{Object.<string, AttribBuffer>,\n *     instances:{Array<{firstVertex:number, numVertices:number, arrayIndex:\n *     number}>}} object result.\n */\n//\ntdl.primitives.concatLarge = function(arrayOfArrays) {\n  // Step 2: convert instances to expanded geometry\n  var instances = [];\n  var expandedArrays = [];\n  var expandedArray;\n  var totalElements = 0;\n  for (var ii = 0; ii < arrayOfArrays.length; ++ii) {\n    // WebGL can only handle 65536 indexed vertices so check if this\n    // geometry can fit the current model\n    var array = arrayOfArrays[ii];\n    if (!expandedArray || totalElements + array.position.numElements > 65536) {\n      // Start a new array.\n      totalElements = 0;\n      expandedArray = [array];\n      expandedArrays.push(expandedArray);\n    } else {\n      // Add our new stuff on to the old one.\n      expandedArray.push(array);\n    }\n    instances.push({\n        firstVertex: totalElements,\n        numVertices: array.position.numElements,\n        arrayIndex: expandedArrays.length - 1\n    });\n    totalElements += array.position.numElements;\n  }\n\n  for (var ii = 0; ii < expandedArrays.length; ++ii) {\n    //tdl.log(\"concat:\", ii, \" of \", expandedArrays.length);\n    expandedArrays[ii] = tdl.primitives.concat(expandedArrays[ii]);\n  }\n  return {\n      arrays: expandedArrays,\n      instances: instances\n  };\n};\n\n/**\n * Applies planar UV mapping in the XZ plane.\n * @param {tdl.primitives.AttribBuffer} positions The positions\n * @param {tdl.primitives.AttribBuffer} texCoords The texCoords\n */\ntdl.primitives.applyPlanarUVMapping = function(positions, texCoords) {\n  // compute the extents\n  var extents = positions.computeExtents();\n  var ranges = tdl.math.subVector(extents.max, extents.min);\n\n  var numElements = positions.numElements;\n  for (var ii = 0; ii < numElements; ++ii) {\n    var position = positions.getElement(ii);\n    var u = (position[0] - extents.min[0]) / ranges[0];\n    var v = (position[2] - extents.min[2]) / ranges[2];\n    texCoords.setElement(ii, [u, v]);\n  }\n};\n\n/**\n * Creates sphere vertices.\n * The created sphere has position, normal and uv streams.\n *\n * @param {number} radius radius of the sphere.\n * @param {number} subdivisionsAxis number of steps around the sphere.\n * @param {number} subdivisionsHeight number of vertically on the sphere.\n * @param {number?} opt_startLatitudeInRadians where to start\n *     the top of the sphere. Default = 0.\n * @param {number?} opt_endLatitudeInRadians Where to end the\n *     bottom of the sphere. Default = Math.PI.\n * @param {number?} opt_startLongitudeInRadians where to start\n *     wrapping the sphere. Default = 0.\n * @param {number?} opt_endLongitudeInRadians where to end\n *     wrapping the sphere. Default = 2 * Math.PI.\n * @return {Object.<string, tdl.primitives.AttribBuffer>} The\n *         created plane vertices.\n */\ntdl.primitives.createSphere = function(\n    radius,\n    subdivisionsAxis,\n    subdivisionsHeight,\n    opt_startLatitudeInRadians,\n    opt_endLatitudeInRadians,\n    opt_startLongitudeInRadians,\n    opt_endLongitudeInRadians) {\n  if (subdivisionsAxis <= 0 || subdivisionsHeight <= 0) {\n    throw Error('subdivisionAxis and subdivisionHeight must be > 0');\n  }\n  var math = tdl.math;\n\n  opt_startLatitudeInRadians = opt_startLatitudeInRadians || 0;\n  opt_endLatitudeInRadians = opt_endLatitudeInRadians || Math.PI;\n  opt_startLongitudeInRadians = opt_startLongitudeInRadians || 0;\n  opt_endLongitudeInRadians = opt_endLongitudeInRadians || (Math.PI * 2);\n\n  var latRange = opt_endLatitudeInRadians - opt_startLatitudeInRadians;\n  var longRange = opt_endLongitudeInRadians - opt_startLongitudeInRadians;\n\n  // We are going to generate our sphere by iterating through its\n  // spherical coordinates and generating 2 triangles for each quad on a\n  // ring of the sphere.\n  var numVertices = (subdivisionsAxis + 1) * (subdivisionsHeight + 1);\n  var positions = new tdl.primitives.AttribBuffer(3, numVertices);\n  var normals = new tdl.primitives.AttribBuffer(3, numVertices);\n  var texCoords = new tdl.primitives.AttribBuffer(2, numVertices);\n\n  // Generate the individual vertices in our vertex buffer.\n  for (var y = 0; y <= subdivisionsHeight; y++) {\n    for (var x = 0; x <= subdivisionsAxis; x++) {\n      // Generate a vertex based on its spherical coordinates\n      var u = x / subdivisionsAxis;\n      var v = y / subdivisionsHeight;\n      var theta = longRange * u;\n      var phi = latRange * v;\n      var sinTheta = Math.sin(theta);\n      var cosTheta = Math.cos(theta);\n      var sinPhi = Math.sin(phi);\n      var cosPhi = Math.cos(phi);\n      var ux = cosTheta * sinPhi;\n      var uy = cosPhi;\n      var uz = sinTheta * sinPhi;\n      positions.push([radius * ux, radius * uy, radius * uz]);\n      normals.push([ux, uy, uz]);\n      texCoords.push([1 - u, v]);\n    }\n  }\n\n  var numVertsAround = subdivisionsAxis + 1;\n  var indices = new tdl.primitives.AttribBuffer(\n      3, subdivisionsAxis * subdivisionsHeight * 2, 'Uint16Array');\n  for (var x = 0; x < subdivisionsAxis; x++) {\n    for (var y = 0; y < subdivisionsHeight; y++) {\n      // Make triangle 1 of quad.\n      indices.push([\n          (y + 0) * numVertsAround + x,\n          (y + 0) * numVertsAround + x + 1,\n          (y + 1) * numVertsAround + x]);\n\n      // Make triangle 2 of quad.\n      indices.push([\n          (y + 1) * numVertsAround + x,\n          (y + 0) * numVertsAround + x + 1,\n          (y + 1) * numVertsAround + x + 1]);\n    }\n  }\n\n  return {\n    position: positions,\n    normal: normals,\n    texCoord: texCoords,\n    indices: indices};\n};\n\n/**\n * Creates cresent vertices. The created sphere has position, normal and uv\n * streams.\n *\n * @param {number} verticalRadius The vertical radius of the cresent.\n * @param {number} outerRadius The outer radius of the cresent.\n * @param {number} innerRadius The inner radius of the cresent.\n * @param {number} thickness The thickness of the cresent.\n * @param {number} subdivisionsDown number of steps around the sphere.\n * @param {number} subdivisionsThick number of vertically on the sphere.\n * @param {number?} opt_startOffset Where to start arc Default\n *        0.\n * @param {number?} opt_endOffset Where to end arg Default 1.\n * @return {Object.<string, tdl.primitives.AttribBuffer>} The\n *         created plane vertices.\n */\ntdl.primitives.createCresent = function(\n    verticalRadius,\n    outerRadius,\n    innerRadius,\n    thickness,\n    subdivisionsDown,\n    opt_startOffset,\n    opt_endOffset) {\n  if (subdivisionsDown <= 0) {\n    throw Error('subdivisionDown must be > 0');\n  }\n  var math = tdl.math;\n\n  opt_startOffset = opt_startOffset || 0;\n  opt_endOffset = opt_endOffset || 1;\n\n  var subdivisionsThick = 2;\n\n  var offsetRange = opt_endOffset - opt_startOffset;\n  var numVertices = (subdivisionsDown + 1) * 2 * (2 + subdivisionsThick);\n  var positions = new tdl.primitives.AttribBuffer(3, numVertices);\n  var normals = new tdl.primitives.AttribBuffer(3, numVertices);\n  var texCoords = new tdl.primitives.AttribBuffer(2, numVertices);\n\n  function createArc(arcRadius, x, normalMult, normalAdd, uMult, uAdd) {\n    for (var z = 0; z <= subdivisionsDown; z++) {\n      var uBack = x / (subdivisionsThick - 1);\n      var v = z / subdivisionsDown;\n      var xBack = (uBack - 0.5) * 2;\n      var angle = (opt_startOffset + (v * offsetRange)) * Math.PI;\n      var s = Math.sin(angle);\n      var c = Math.cos(angle);\n      var radius = math.lerpScalar(verticalRadius, arcRadius, s);\n      var px = xBack * thickness;\n      var py = c * verticalRadius;\n      var pz = s * radius;\n      positions.push([px, py, pz]);\n      // TODO(gman): fix! This is not correct.\n      var n = math.addVector(\n          math.mulVectorVector([0, s, c], normalMult), normalAdd);\n      normals.push(n);\n      texCoords.push([uBack * uMult + uAdd, v]);\n    }\n  }\n\n  // Generate the individual vertices in our vertex buffer.\n  for (var x = 0; x < subdivisionsThick; x++) {\n    var uBack = (x / (subdivisionsThick - 1) - 0.5) * 2;\n    createArc(outerRadius, x, [1, 1, 1], [0,     0, 0], 1, 0);\n    createArc(outerRadius, x, [0, 0, 0], [uBack, 0, 0], 0, 0);\n    createArc(innerRadius, x, [1, 1, 1], [0,     0, 0], 1, 0);\n    createArc(innerRadius, x, [0, 0, 0], [uBack, 0, 0], 0, 1);\n  }\n\n  // Do outer surface.\n  var indices = new tdl.primitives.AttribBuffer(\n      3, (subdivisionsDown * 2) * (2 + subdivisionsThick), 'Uint16Array');\n\n  function createSurface(leftArcOffset, rightArcOffset) {\n    for (var z = 0; z < subdivisionsDown; ++z) {\n      // Make triangle 1 of quad.\n      indices.push([\n          leftArcOffset + z + 0,\n          leftArcOffset + z + 1,\n          rightArcOffset + z + 0]);\n\n      // Make triangle 2 of quad.\n      indices.push([\n          leftArcOffset + z + 1,\n          rightArcOffset + z + 1,\n          rightArcOffset + z + 0]);\n    }\n  }\n\n  var numVerticesDown = subdivisionsDown + 1;\n  // front\n  createSurface(numVerticesDown * 0, numVerticesDown * 4);\n  // right\n  createSurface(numVerticesDown * 5, numVerticesDown * 7);\n  // back\n  createSurface(numVerticesDown * 6, numVerticesDown * 2);\n  // left\n  createSurface(numVerticesDown * 3, numVerticesDown * 1);\n\n  return {\n    position: positions,\n    normal: normals,\n    texCoord: texCoords,\n    indices: indices};\n};\n\n/**\n * Creates line vertices.\n * The created line has position and normal.\n *\n * @param {Array.<number>} coords coords of the line.\n * @return {Object.<string, tdl.primitives.AttribBuffer>} The\n *         created line vertices.\n */\ntdl.primitives.createLine = function(coords) {\n  var cL = coords.length;\n  var numVertices =  cL / 3;\n  if (numVertices <= 1) {\n    throw Error('line coords length must be > 3');\n  }\n  if (coords.length % 3 != 0) {\n    throw Error('line coords length must be the multiple of 3');\n  }\n  var math = tdl.math;\n  \n  var positions = new tdl.primitives.AttribBuffer(3, numVertices);\n  var normals = new tdl.primitives.AttribBuffer(3, numVertices);\n    for (var i = 0; i < cL; i+=3) {\n      positions.push([\n          coords[i],\n          coords[i+1],\n          coords[i+2]\n        ]);\n      normals.push([0, 1, 0]);\n    }\n\n  var indices = new tdl.primitives.AttribBuffer(\n      2, numVertices - 1, 'Uint16Array');\n  for (var i = 0; i < numVertices -1; i++) {\n    indices.push([\n      i,\n      i+1]); \n  }\n  \n  return {\n    position: positions,\n    normal: normals,\n    indices: indices};\n};\n\n/**\n * Creates XZ plane vertices.\n * The created plane has position, normal and uv streams.\n *\n * @param {number} width Width of the plane.\n * @param {number} depth Depth of the plane.\n * @param {number} subdivisionsWidth Number of steps across the plane.\n * @param {number} subdivisionsDepth Number of steps down the plane.\n * @param {(tdl.math.Matrix4|tdl.fast.Matrix4)?} opt_matrix A\n *     matrix by which to multiply all the vertices.\n * @return {Object.<string, tdl.primitives.AttribBuffer>} The\n *         created plane vertices.\n */\ntdl.primitives.createPlane = function(\n    width,\n    depth,\n    subdivisionsWidth,\n    subdivisionsDepth) {\n  if (subdivisionsWidth <= 0 || subdivisionsDepth <= 0) {\n    throw Error('subdivisionWidth and subdivisionDepth must be > 0');\n  }\n  var math = tdl.math;\n\n  var numVertices = (subdivisionsWidth + 1) * (subdivisionsDepth + 1);\n  var positions = new tdl.primitives.AttribBuffer(3, numVertices);\n  var normals = new tdl.primitives.AttribBuffer(3, numVertices);\n  var texCoords = new tdl.primitives.AttribBuffer(2, numVertices);\n\n  for (var z = 0; z <= subdivisionsDepth; z++) {\n    for (var x = 0; x <= subdivisionsWidth; x++) {\n      var u = x / subdivisionsWidth;\n      var v = z / subdivisionsDepth;\n      positions.push([\n          width * u - width * 0.5,\n          0,\n          depth * v - depth * 0.5]);\n      normals.push([0, 1, 0]);\n      texCoords.push([u, v]);\n    }\n  }\n\n  var numVertsAcross = subdivisionsWidth + 1;\n  var indices = new tdl.primitives.AttribBuffer(\n      3, subdivisionsWidth * subdivisionsDepth * 2, 'Uint16Array');\n\n  for (var z = 0; z < subdivisionsDepth; z++) {\n    for (var x = 0; x < subdivisionsWidth; x++) {\n      // Make triangle 1 of quad.\n      indices.push([\n          (z + 0) * numVertsAcross + x,\n          (z + 1) * numVertsAcross + x,\n          (z + 0) * numVertsAcross + x + 1]);\n\n      // Make triangle 2 of quad.\n      indices.push([\n          (z + 1) * numVertsAcross + x,\n          (z + 1) * numVertsAcross + x + 1,\n          (z + 0) * numVertsAcross + x + 1]);\n    }\n  }\n\n  return {\n    position: positions,\n    normal: normals,\n    texCoord: texCoords,\n    indices: indices};\n};\n\n\n/**\n * Array of the indices of corners of each face of a cube.\n * @private\n * @type {Array.<Array.<number>>}\n */\ntdl.primitives.CUBE_FACE_INDICES_ = [\n  [3, 7, 5, 1], // right\n  [6, 2, 0, 4], // left\n  [6, 7, 3, 2], // ??\n  [0, 1, 5, 4], // ??\n  [7, 6, 4, 5], // front\n  [2, 3, 1, 0]  // back\n];\n\n/**\n * Creates the vertices and indices for a cube. The\n * cube will be created around the origin. (-size / 2, size / 2)\n *\n * @param {number} size Width, height and depth of the cube.\n * @return {Object.<string, tdl.primitives.AttribBuffer>} The\n *         created plane vertices.\n */\ntdl.primitives.createCube = function(size) {\n  var k = size / 2;\n\n  var cornerVertices = [\n    [-k, -k, -k],\n    [+k, -k, -k],\n    [-k, +k, -k],\n    [+k, +k, -k],\n    [-k, -k, +k],\n    [+k, -k, +k],\n    [-k, +k, +k],\n    [+k, +k, +k]\n  ];\n\n  var faceNormals = [\n    [+1, +0, +0],\n    [-1, +0, +0],\n    [+0, +1, +0],\n    [+0, -1, +0],\n    [+0, +0, +1],\n    [+0, +0, -1]\n  ];\n\n  var uvCoords = [\n    [1, 0],\n    [0, 0],\n    [0, 1],\n    [1, 1]\n  ];\n\n  var numVertices = 6 * 4;\n  var positions = new tdl.primitives.AttribBuffer(3, numVertices);\n  var normals = new tdl.primitives.AttribBuffer(3, numVertices);\n  var texCoords = new tdl.primitives.AttribBuffer(2, numVertices);\n  var indices = new tdl.primitives.AttribBuffer(3, 6 * 2, 'Uint16Array');\n\n  for (var f = 0; f < 6; ++f) {\n    var faceIndices = tdl.primitives.CUBE_FACE_INDICES_[f];\n    for (var v = 0; v < 4; ++v) {\n      var position = cornerVertices[faceIndices[v]];\n      var normal = faceNormals[f];\n      var uv = uvCoords[v];\n\n      // Each face needs all four vertices because the normals and texture\n      // coordinates are not all the same.\n      positions.push(position);\n      normals.push(normal);\n      texCoords.push(uv);\n\n    }\n    // Two triangles make a square face.\n    var offset = 4 * f;\n    indices.push([offset + 0, offset + 1, offset + 2]);\n    indices.push([offset + 0, offset + 2, offset + 3]);\n  }\n\n  return {\n    position: positions,\n    normal: normals,\n    texCoord: texCoords,\n    indices: indices};\n};\n\n\n/**\n * Creates the vertices and indices for a flared cube (extrude the edges).\n * The U texture coordinate will be a gradient 0-1 from inside out. Use\n * the vertex shader to distort using U as an angle for fun effects.\n *\n * @param {number} size Width, height and depth of the cube.\n * @return {Object.<string, tdl.primitives.AttribBuffer>} The\n *         created plane vertices.\n */\ntdl.primitives.createFlaredCube = function(innerSize, outerSize, layerCount) {\n  var numVertices = 2 * (layerCount + 1);\n  var positions = new tdl.primitives.AttribBuffer(3, numVertices);\n  var normals = new tdl.primitives.AttribBuffer(3, numVertices);\n  var texCoords = new tdl.primitives.AttribBuffer(2, numVertices);\n  var indices = new tdl.primitives.AttribBuffer(\n      3, layerCount * 2, 'Uint16Array');\n\n  // make a trapazoid plane.\n  for (var z = 0; z <= layerCount; z++) {\n    for (var x = 0; x <= 1; x++) {\n      var u = x;\n      var v = z / layerCount;\n      var width = tdl.math.lerpScalar(innerSize, outerSize, v);\n      positions.push([\n          width * u - width * 0.5,\n          0,\n          tdl.math.lerpScalar(innerSize, outerSize, v) * 0.7\n      ]);\n      normals.push([0, 1, 0]);\n      texCoords.push([v, u]);\n    }\n  }\n\n  var numVertsAcross = 2;\n  for (var z = 0; z < layerCount; z++) {\n    // Make triangle 1 of quad.\n    indices.push([\n        (z + 0) * numVertsAcross,\n        (z + 1) * numVertsAcross,\n        (z + 0) * numVertsAcross + 1]);\n\n    // Make triangle 2 of quad.\n    indices.push([\n        (z + 1) * numVertsAcross,\n        (z + 1) * numVertsAcross + 1,\n        (z + 0) * numVertsAcross + 1]);\n  }\n\n  var arrays = {\n    position: positions,\n    normal: normals,\n    texCoord: texCoords,\n    indices: indices\n  };\n\n  // rotate it 45 degrees\n  tdl.primitives.reorient(arrays, tdl.math.matrix4.rotationX(Math.PI / 4));\n\n  // make 3 copies of plane each rotated 90\n  var planes = [arrays];\n  for (var ii = 0; ii < 3; ++ii) {\n    var clone = tdl.primitives.clone(arrays);\n    tdl.primitives.reorient(clone, tdl.math.matrix4.rotationZ(Math.PI * (ii + 1) / 2));\n    planes.push(clone);\n  }\n  // concat 4 planes to make a cone\n  var arrays = tdl.primitives.concat(planes);\n\n  // make 3 copies of cone each rotated 90\n  var cones = [arrays];\n  for (var ii = 0; ii < 3; ++ii) {\n    var clone = tdl.primitives.clone(arrays);\n    tdl.primitives.reorient(clone, tdl.math.matrix4.rotationY(Math.PI * (ii + 1) / 2));\n    cones.push(clone);\n  }\n  // concat 4 cones to make flared cube\n  var arrays = tdl.primitives.concat(cones);\n  return arrays;\n};\n\n\n\n/**\n * Creates vertices for a truncated cone, which is like a cylinder\n * except that it has different top and bottom radii. A truncated cone\n * can also be used to create cylinders and regular cones. The\n * truncated cone will be created centered about the origin, with the\n * y axis as its vertical axis. The created cone has position, normal\n * and uv streams.\n *\n * @param {number} bottomRadius Bottom radius of truncated cone.\n * @param {number} topRadius Top radius of truncated cone.\n * @param {number} height Height of truncated cone.\n * @param {number} radialSubdivisions The number of subdivisions around the\n *     truncated cone.\n * @param {number} verticalSubdivisions The number of subdivisions down the\n *     truncated cone.\n * @param {boolean?} opt_topCap Create top cap. Default = true.\n * @param {boolean?} opt_bottomCap Create bottom cap. Default =\n *        true.\n * @return {Object.<string, tdl.primitives.AttribBuffer>} The\n *         created plane vertices.\n */\ntdl.primitives.createTruncatedCone = function(\n    bottomRadius,\n    topRadius,\n    height,\n    radialSubdivisions,\n    verticalSubdivisions,\n    opt_topCap,\n    opt_bottomCap) {\n  if (radialSubdivisions < 3) {\n    throw Error('radialSubdivisions must be 3 or greater');\n  }\n\n  if (verticalSubdivisions < 1) {\n    throw Error('verticalSubdivisions must be 1 or greater');\n  }\n\n  var topCap = (opt_topCap === undefined) ? true : opt_topCap;\n  var bottomCap = (opt_bottomCap === undefined) ? true : opt_bottomCap;\n\n  var extra = (topCap ? 2 : 0) + (bottomCap ? 2 : 0);\n\n  var numVertices = (radialSubdivisions + 1) * (verticalSubdivisions + 1 + extra);\n  var positions = new tdl.primitives.AttribBuffer(3, numVertices);\n  var normals = new tdl.primitives.AttribBuffer(3, numVertices);\n  var texCoords = new tdl.primitives.AttribBuffer(2, numVertices);\n  var indices = new tdl.primitives.AttribBuffer(\n      3, radialSubdivisions * (verticalSubdivisions + extra) * 2, 'Uint16Array');\n\n  var vertsAroundEdge = radialSubdivisions + 1;\n\n  // The slant of the cone is constant across its surface\n  var slant = Math.atan2(bottomRadius - topRadius, height);\n  var cosSlant = Math.cos(slant);\n  var sinSlant = Math.sin(slant);\n\n  var start = topCap ? -2 : 0;\n  var end = verticalSubdivisions + (bottomCap ? 2 : 0);\n\n  for (var yy = start; yy <= end; ++yy) {\n    var v = yy / verticalSubdivisions\n    var y = height * v;\n    var ringRadius;\n    if (yy < 0) {\n      y = 0;\n      v = 1;\n      ringRadius = bottomRadius;\n    } else if (yy > verticalSubdivisions) {\n      y = height;\n      v = 1;\n      ringRadius = topRadius;\n    } else {\n      ringRadius = bottomRadius +\n        (topRadius - bottomRadius) * (yy / verticalSubdivisions);\n    }\n    if (yy == -2 || yy == verticalSubdivisions + 2) {\n      ringRadius = 0;\n      v = 0;\n    }\n    y -= height / 2;\n    for (var ii = 0; ii < vertsAroundEdge; ++ii) {\n      var sin = Math.sin(ii * Math.PI * 2 / radialSubdivisions);\n      var cos = Math.cos(ii * Math.PI * 2 / radialSubdivisions);\n      positions.push([sin * ringRadius, y, cos * ringRadius]);\n      normals.push([\n          (yy < 0 || yy > verticalSubdivisions) ? 0 : (sin * cosSlant),\n          (yy < 0) ? -1 : (yy > verticalSubdivisions ? 1 : sinSlant),\n          (yy < 0 || yy > verticalSubdivisions) ? 0 : (cos * cosSlant)]);\n      texCoords.push([(ii / radialSubdivisions), 1 - v]);\n    }\n  }\n\n  for (var yy = 0; yy < verticalSubdivisions + extra; ++yy) {\n    for (var ii = 0; ii < radialSubdivisions; ++ii) {\n      indices.push([vertsAroundEdge * (yy + 0) + 0 + ii,\n                   vertsAroundEdge * (yy + 0) + 1 + ii,\n                   vertsAroundEdge * (yy + 1) + 1 + ii]);\n      indices.push([vertsAroundEdge * (yy + 0) + 0 + ii,\n                   vertsAroundEdge * (yy + 1) + 1 + ii,\n                   vertsAroundEdge * (yy + 1) + 0 + ii]);\n    }\n  }\n\n  return {\n    position: positions,\n    normal: normals,\n    texCoord: texCoords,\n    indices: indices};\n};\n\n/**\n * Creates cylinder vertices. The cylinder will be created around the origin\n * along the y-axis. The created cylinder has position, normal and uv streams.\n *\n * @param {number} radius Radius of cylinder.\n * @param {number} height Height of cylinder.\n * @param {number} radialSubdivisions The number of subdivisions around the\n *     cylinder.\n * @param {number} verticalSubdivisions The number of subdivisions down the\n *     cylinder.\n * @param {boolean?} opt_topCap Create top cap. Default = true.\n * @param {boolean?} opt_bottomCap Create bottom cap. Default =\n *        true.\n * @return {Object.<string, tdl.primitives.AttribBuffer>} The\n *         created plane vertices.\n */\ntdl.primitives.createCylinder = function(\n    radius,\n    height,\n    radialSubdivisions,\n    verticalSubdivisions,\n    opt_topCap,\n    opt_bottomCap) {\n  return tdl.primitives.createTruncatedCone(\n      radius,\n      radius,\n      height,\n      radialSubdivisions,\n      verticalSubdivisions,\n      opt_topCap,\n      opt_bottomCap);\n};\n\n/**\n * Creates vertices for a torus, The created cone has position, normal\n * and texCoord streams.\n *\n * @param {number} radius radius of center of torus circle.\n * @param {number} thickness radius of torus ring.\n * @param {number} radialSubdivisions The number of subdivisions around the\n *     torus.\n * @param {number} bodySubdivisions The number of subdivisions around the\n *     body torus.\n * @param {boolean?} opt_startAngle start angle in radians.\n *        Default = 0.\n * @param {boolean?} opt_endAngle end angle in radians. Default\n *        = Math.PI * 2.\n * @return {Object.<string, tdl.primitives.AttribBuffer>} The\n *         created torus vertices.\n */\ntdl.primitives.createTorus = function(\n    radius,\n    thickness,\n    radialSubdivisions,\n    bodySubdivisions,\n    opt_startAngle,\n    opt_endAngle) {\n  if (radialSubdivisions < 3) {\n    throw Error('radialSubdivisions must be 3 or greater');\n  }\n\n  if (bodySubdivisions < 3) {\n    throw Error('verticalSubdivisions must be 3 or greater');\n  }\n\n  var startAngle = opt_startAngle || 0;\n  var endAngle = opt_endAngle || Math.PI * 2;\n  var range = endAngle - startAngle;\n\n  // TODO(gman): cap the ends if not a full circle.\n\n  var numVertices = (radialSubdivisions) * (bodySubdivisions);\n  var positions = new tdl.primitives.AttribBuffer(3, numVertices);\n  var normals = new tdl.primitives.AttribBuffer(3, numVertices);\n  var texCoords = new tdl.primitives.AttribBuffer(2, numVertices);\n  var indices = new tdl.primitives.AttribBuffer(\n      3, (radialSubdivisions) * (bodySubdivisions) * 2, 'Uint16Array');\n\n  for (var slice = 0; slice < bodySubdivisions; ++slice) {\n    var v = slice / bodySubdivisions;\n    var sliceAngle = v * Math.PI * 2;\n    var sliceSin = Math.sin(sliceAngle);\n    var ringRadius = radius + sliceSin * thickness;\n    var ny = Math.cos(sliceAngle);\n    var y = ny * thickness;\n    for (var ring = 0; ring < radialSubdivisions; ++ring) {\n      var u = ring / radialSubdivisions;\n      var ringAngle = startAngle + u * range;\n      var xSin = Math.sin(ringAngle);\n      var zCos = Math.cos(ringAngle);\n      var x = xSin * ringRadius;\n      var z = zCos * ringRadius;\n      var nx = xSin * sliceSin;\n      var nz = zCos * sliceSin;\n      positions.push([x, y, z]);\n      normals.push([nx, ny, nz]);\n      texCoords.push([u, 1 - v]);\n    }\n  }\n\n  for (var slice = 0; slice < bodySubdivisions; ++slice) {\n    for (var ring = 0; ring < radialSubdivisions; ++ring) {\n      var nextRingIndex = (1 + ring) % radialSubdivisions;\n      var nextSliceIndex = (1 + slice) % bodySubdivisions;\n      indices.push([radialSubdivisions * slice          + ring,\n                    radialSubdivisions * nextSliceIndex + ring,\n                    radialSubdivisions * slice          + nextRingIndex]);\n      indices.push([radialSubdivisions * nextSliceIndex + ring,\n                    radialSubdivisions * nextSliceIndex + nextRingIndex,\n                    radialSubdivisions * slice          + nextRingIndex]);\n    }\n  }\n\n  return {\n    position: positions,\n    normal: normals,\n    texCoord: texCoords,\n    indices: indices};\n};\n\n\n/**\n * Creates a disc. The disc will be in the xz plane, centered at\n * the origin. When creating, at least 3 divisions, or pie\n * pieces, need to be specified, otherwise the triangles making\n * up the disc will be degenerate. You can also specify the\n * number of radial pieces (opt_stacks). A value of 1 for\n * opt_stacks will give you a simple disc of pie pieces.  If you\n * want to create an annulus you can set opt_innerRadius to a\n * value > 0. Finally, stackPower allows you to have the widths\n * increase or decrease as you move away from the center. This\n * is particularly useful when using the disc as a ground plane\n * with a fixed camera such that you don't need the resolution\n * of small triangles near the perimeter. For example, a value\n * of 2 will produce stacks whose ouside radius increases with\n * the square of the stack index. A value of 1 will give uniform\n * stacks.\n *\n * @param {number} radius Radius of the ground plane.\n * @param {number} divisions Number of triangles in the ground plane\n *                 (at least 3).\n * @param {number?} opt_stacks Number of radial divisions (default=1).\n * @param {number?} opt_innerRadius. Default 0.\n * @param {number?} opt_stackPower Power to raise stack size to for decreasing\n *                 width.\n * @return {Object.<string, tdl.primitives.AttribBuffer>} The\n *         created vertices.\n */\ntdl.primitives.createDisc = function(\n    radius,\n    divisions,\n    opt_stacks,\n    opt_innerRadius,\n    opt_stackPower) {\n  if (divisions < 3) {\n    throw Error('divisions must be at least 3');\n  }\n\n  var stacks = opt_stacks ? opt_stacks : 1;\n  var stackPower = opt_stackPower ? opt_stackPower : 1;\n  var innerRadius = opt_innerRadius ? opt_innerRadius : 0;\n\n  // Note: We don't share the center vertex because that would\n  // mess up texture coordinates.\n  var numVertices = (divisions) * (stacks + 1);\n\n  var positions = new tdl.primitives.AttribBuffer(3, numVertices);\n  var normals = new tdl.primitives.AttribBuffer(3, numVertices);\n  var texCoords = new tdl.primitives.AttribBuffer(2, numVertices);\n  var indices = new tdl.primitives.AttribBuffer(\n      3, stacks * divisions * 2, 'Uint16Array');\n\n  var firstIndex = 0;\n  var radiusSpan = radius - innerRadius;\n\n  // Build the disk one stack at a time.\n  for (var stack = 0; stack <= stacks; ++stack) {\n    var stackRadius = innerRadius + radiusSpan * Math.pow(stack / stacks, stackPower);\n\n    for (var i = 0; i < divisions; ++i) {\n      var theta = 2.0 * Math.PI * i / divisions;\n      var x = stackRadius * Math.cos(theta);\n      var z = stackRadius * Math.sin(theta);\n\n      positions.push([x, 0, z]);\n      normals.push([0, 1, 0]);\n      texCoords.push([1 - (i / divisions), stack / stacks]);\n      if (stack > 0) {\n        // a, b, c and d are the indices of the vertices of a quad.  unless\n        // the current stack is the one closest to the center, in which case\n        // the vertices a and b connect to the center vertex.\n        var a = firstIndex + (i + 1) % divisions;\n        var b = firstIndex + i;\n        var c = firstIndex + i - divisions;\n        var d = firstIndex + (i + 1) % divisions - divisions;\n\n        // Make a quad of the vertices a, b, c, d.\n        indices.push([a, b, c]);\n        indices.push([a, c, d]);\n      }\n    }\n\n    firstIndex += divisions;\n  }\n\n  return {\n    position: positions,\n    normal: normals,\n    texCoord: texCoords,\n    indices: indices};\n};\n\n/**\n * Interleaves vertex information into one large buffer\n * @param {Array.<string, tdl.primitives.AttribBuffer>}\n * @param {Object.<string, tdl.primitives.AttribBuffer>}\n */\ntdl.primitives.interleaveVertexData = function(vertexDataArray) {\n  var stride = 0;\n  for (var s = 0; s < vertexDataArray.length; s++) {\n    stride += vertexDataArray[s].numComponents;\n  }\n  // This assumes the first element is always positions.\n  var numVertices = vertexDataArray[0].numElements;\n  var vertexData = new Float32Array(stride * numVertices);\n  var count = 0;\n  for (var v = 0; v< numVertices; v++) {\n    for (var i = 0; i < vertexDataArray.length; i++) {\n      var element  = vertexDataArray[i].getElement(v);\n      for (var d = 0; d < vertexDataArray[i].numComponents; d++) {\n        vertexData[count++] = element[d];\n      }\n    }\n  }\n  return vertexData;\n};\n\nreturn tdl.primitives;\n});\n\n"
  },
  {
    "path": "tdl/programs.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains objects to deal with WebGL\n *               programs.\n */\ndefine(\n    [ './base-rs',\n      './log',\n      './string',\n      './webgl',\n    ], function(\n      BaseRS,\n      Log,\n      Strings,\n      WebGL) {\n\ntdl.provide('tdl.programs');\n/**\n * A module for programs.\n * @namespace\n */\ntdl.programs = tdl.programs || {};\n\n/**\n * Loads a program from script tags.\n * @param {string} vertexShaderId The id of the script tag that contains the\n *     vertex shader source.\n * @param {string} fragmentShaderId The id of the script tag that contains the\n *     fragment shader source.\n * @return {tdl.programs.Program} The created program.\n */\ntdl.programs.loadProgramFromScriptTags = function(\n    vertexShaderId, fragmentShaderId) {\n  var vertElem = document.getElementById(vertexShaderId);\n  var fragElem = document.getElementById(fragmentShaderId);\n  if (!vertElem) {\n    throw(\"Can't find vertex program tag: \" + vertexShaderId);\n  }\n  if (!fragElem) {\n    throw(\"Can't find fragment program tag: \" + fragmentShaderId);\n  }\n  return tdl.programs.loadProgram(\n      document.getElementById(vertexShaderId).text,\n      document.getElementById(fragmentShaderId).text);\n};\n\ntdl.programs.makeProgramId = function(vertexShader, fragmentShader) {\n  return vertexShader + fragmentShader;\n};\n\n/**\n * Loads a program.\n * @param {string} vertexShader The vertex shader source.\n * @param {string} fragmentShader The fragment shader source.\n * @param {function(error)) opt_asyncCallback. Called with\n *        undefined if success or string if failure.\n * @return {tdl.programs.Program} The created program.\n */\ntdl.programs.loadProgram = function(vertexShader, fragmentShader, opt_asyncCallback) {\n  var id = tdl.programs.makeProgramId(vertexShader, fragmentShader);\n  tdl.programs.init_();\n  var program = gl.tdl.programs.programDB[id];\n  if (program) {\n    if (opt_asyncCallback) {\n      setTimeout(function() { opt_asyncCallback(); }, 1);\n    }\n    return program;\n  }\n  try {\n    program = new tdl.programs.Program(vertexShader, fragmentShader, opt_asyncCallback);\n  } catch (e) {\n    tdl.error(e);\n    return null;\n  }\n  if (!opt_asyncCallback) {\n    gl.tdl.programs.programDB[id] = program;\n  }\n  return program;\n};\n\n/**\n * A object to manage a WebGLProgram.\n * @constructor\n * @param {string} vertexShader The vertex shader source.\n * @param {string} fragmentShader The fragment shader source.\n * @param {function(error)) opt_asyncCallback. Called with\n *        undefined if success or string if failure.\n */\ntdl.programs.Program = function(vertexShader, fragmentShader, opt_asyncCallback) {\n  var that = this;\n  this.programId = tdl.programs.makeProgramId(vertexShader, fragmentShader);\n  this.asyncCallback = opt_asyncCallback;\n\n  var shaderId;\n  var program;\n  var vs;\n  var fs;\n\n  /**\n   * Loads a shader.\n   * @private\n   * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.\n   * @param {string} shaderSource The shader source.\n   * @param {number} shaderType The type of shader.\n   * @return {WebGLShader} The created shader.\n   */\n  var loadShader = function(gl, shaderSource, shaderType) {\n    shaderId = shaderSource + shaderType;\n    tdl.programs.init_();\n    var shader = gl.tdl.programs.shaderDB[shaderId];\n    if (shader) {\n      return shader;\n    }\n\n    // Create the shader object\n    var shader = gl.createShader(shaderType);\n\n    // Load the shader source\n    gl.shaderSource(shader, shaderSource);\n\n    // Compile the shader\n    gl.compileShader(shader);\n\n    // Check the compile status\n    if (!that.asyncCallback) {\n      checkShader(shader);\n    }\n    return shader;\n  }\n\n  var checkShader = function(shader) {\n    var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);\n    if (!compiled && !gl.isContextLost()) {\n      // Something went wrong during compilation; get the error\n      tdl.programs.lastError = gl.getShaderInfoLog(shader);\n      gl.deleteShader(shader);\n      throw(\"*** Error compiling shader :\" + tdl.programs.lastError);\n    }\n    gl.tdl.programs.shaderDB[shaderId] = shader;\n  };\n\n  /**\n   * Loads shaders from script tags, creates a program, attaches the shaders and\n   * links.\n   * @private\n   * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.\n   * @param {string} vertexShader The vertex shader.\n   * @param {string} fragmentShader The fragment shader.\n   * @return {WebGLProgram} The created program.\n   */\n  var loadProgram = function(gl, vertexShader, fragmentShader) {\n    var e;\n    try {\n      vs = loadShader(gl, vertexShader, gl.VERTEX_SHADER);\n      fs = loadShader(gl, fragmentShader, gl.FRAGMENT_SHADER);\n      program = gl.createProgram();\n      gl.attachShader(program, vs);\n      gl.attachShader(program, fs);\n      linkProgram(gl, program);\n    } catch (e) {\n      deleteAll(e);\n    }\n    return program;\n  };\n\n  var deleteAll = function(e) {\n    if (vs) { gl.deleteShader(vs) }\n    if (fs) { gl.deleteShader(fs) }\n    if (program) { gl.deleteProgram(program) }\n    throw e;\n  };\n\n  /**\n   * Links a WebGL program, throws if there are errors.\n   * @private\n   * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.\n   * @param {WebGLProgram} program The WebGLProgram to link.\n   */\n  var linkProgram = function(gl, program) {\n    // Link the program\n    gl.linkProgram(program);\n\n    // Check the link status\n    if (!that.asyncCallback) {\n      checkProgram(program);\n    }\n  };\n\n  var checkProgram = function(program) {\n    var linked = gl.getProgramParameter(program, gl.LINK_STATUS);\n    if (!linked && !gl.isContextLost()) {\n      // something went wrong with the link\n      tdl.programs.lastError = gl.getProgramInfoLog (program);\n      throw(\"*** Error in program linking:\" + tdl.programs.lastError);\n    }\n  };\n\n  // Compile shaders\n  var program = loadProgram(gl, vertexShader, fragmentShader);\n  if (!program && !gl.isContextLost()) {\n    throw (\"could not compile program\");\n  }\n\n  // TODO(gman): remove the need for this.\n  function flatten(array){\n    var flat = [];\n    for (var i = 0, l = array.length; i < l; i++) {\n      var type = Object.prototype.toString.call(array[i]).split(' ').pop().split(']').shift().toLowerCase();\n      if (type) { flat = flat.concat(/^(array|collection|arguments|object)$/.test(type) ? flatten(array[i]) : array[i]); }\n    }\n    return flat;\n  }\n\n  function createSetters(program) {\n    // Look up attribs.\n    var attribs = {\n    };\n    // Also make a plain table of the locs.\n    var attribLocs = {\n    };\n\n    function createAttribSetter(info, index) {\n      if (info.size != 1) {\n        throw(\"arrays of attribs not handled\");\n      }\n      return function(b) {\n          gl.bindBuffer(gl.ARRAY_BUFFER, b.buffer());\n          gl.enableVertexAttribArray(index);\n          gl.vertexAttribPointer(\n              index, b.numComponents(), b.type(), b.normalize(), b.stride(), b.offset());\n        };\n    }\n\n    var numAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);\n    for (var ii = 0; ii < numAttribs; ++ii) {\n      var info = gl.getActiveAttrib(program, ii);\n    if (!info) {\n      break;\n    }\n      var name = info.name;\n      if (tdl.string.endsWith(name, \"[0]\")) {\n        name = name.substr(0, name.length - 3);\n      }\n      var index = gl.getAttribLocation(program, info.name);\n      attribs[name] = createAttribSetter(info, index);\n      attribLocs[name] = index\n    }\n\n    // Look up uniforms\n    var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);\n    var uniforms = {\n    };\n    var textureUnit = 0;\n\n    function createUniformSetter(info) {\n      var loc = gl.getUniformLocation(program, info.name);\n      var type = info.type;\n      if (info.size > 1 && tdl.string.endsWith(info.name, \"[0]\")) {\n        // It's an array.\n        if (type == gl.FLOAT)\n          return function(v) { gl.uniform1fv(loc, v); };\n        if (type == gl.FLOAT_VEC2)\n          return function(v) { gl.uniform2fv(loc, v); };\n        if (type == gl.FLOAT_VEC3)\n          return function(v) { gl.uniform3fv(loc, v); };\n        if (type == gl.FLOAT_VEC4)\n          return function(v) { gl.uniform4fv(loc, v); };\n        if (type == gl.INT)\n          return function(v) { gl.uniform1iv(loc, v); };\n        if (type == gl.INT_VEC2)\n          return function(v) { gl.uniform2iv(loc, v); };\n        if (type == gl.INT_VEC3)\n          return function(v) { gl.uniform3iv(loc, v); };\n        if (type == gl.INT_VEC4)\n          return function(v) { gl.uniform4iv(loc, v); };\n        if (type == gl.BOOL)\n          return function(v) { gl.uniform1iv(loc, v); };\n        if (type == gl.BOOL_VEC2)\n          return function(v) { gl.uniform2iv(loc, v); };\n        if (type == gl.BOOL_VEC3)\n          return function(v) { gl.uniform3iv(loc, v); };\n        if (type == gl.BOOL_VEC4)\n          return function(v) { gl.uniform4iv(loc, v); };\n        if (type == gl.FLOAT_MAT2)\n          return function(v) { gl.uniformMatrix2fv(loc, false, v); };\n        if (type == gl.FLOAT_MAT3)\n          return function(v) { gl.uniformMatrix3fv(loc, false, v); };\n        if (type == gl.FLOAT_MAT4)\n          return function(v) { gl.uniformMatrix4fv(loc, false, v); };\n        if (type == gl.SAMPLER_2D || type == gl.SAMPLER_CUBE) {\n          var units = [];\n          for (var ii = 0; ii < info.size; ++ii) {\n            units.push(textureUnit++);\n          }\n          return function(units) {\n            return function(v) {\n              gl.uniform1iv(loc, units);\n              v.forEach(function(t, index) {\n                t.bindToUnit(units[index]);\n              });\n            };\n          }(units);\n        }\n        throw (\"unknown type: 0x\" + type.toString(16));\n      } else {\n        if (type == gl.FLOAT)\n          return function(v) { gl.uniform1f(loc, v); };\n        if (type == gl.FLOAT_VEC2)\n          return function(v) { gl.uniform2fv(loc, v); };\n        if (type == gl.FLOAT_VEC3)\n          return function(v) { gl.uniform3fv(loc, v); };\n        if (type == gl.FLOAT_VEC4)\n          return function(v) { gl.uniform4fv(loc, v); };\n        if (type == gl.INT)\n          return function(v) { gl.uniform1i(loc, v); };\n        if (type == gl.INT_VEC2)\n          return function(v) { gl.uniform2iv(loc, v); };\n        if (type == gl.INT_VEC3)\n          return function(v) { gl.uniform3iv(loc, v); };\n        if (type == gl.INT_VEC4)\n          return function(v) { gl.uniform4iv(loc, v); };\n        if (type == gl.BOOL)\n          return function(v) { gl.uniform1i(loc, v); };\n        if (type == gl.BOOL_VEC2)\n          return function(v) { gl.uniform2iv(loc, v); };\n        if (type == gl.BOOL_VEC3)\n          return function(v) { gl.uniform3iv(loc, v); };\n        if (type == gl.BOOL_VEC4)\n          return function(v) { gl.uniform4iv(loc, v); };\n        if (type == gl.FLOAT_MAT2)\n          return function(v) { gl.uniformMatrix2fv(loc, false, v); };\n        if (type == gl.FLOAT_MAT3)\n          return function(v) { gl.uniformMatrix3fv(loc, false, v); };\n        if (type == gl.FLOAT_MAT4)\n          return function(v) { gl.uniformMatrix4fv(loc, false, v); };\n        if (type == gl.SAMPLER_2D || type == gl.SAMPLER_CUBE) {\n          return function(unit) {\n            return function(v) {\n              gl.uniform1i(loc, unit);\n              v.bindToUnit(unit);\n            };\n          }(textureUnit++);\n        }\n        throw (\"unknown type: 0x\" + type.toString(16));\n      }\n    }\n\n    var textures = {};\n\n    for (var ii = 0; ii < numUniforms; ++ii) {\n      var info = gl.getActiveUniform(program, ii);\n    if (!info) {\n      break;\n    }\n      name = info.name;\n      if (tdl.string.endsWith(name, \"[0]\")) {\n        name = name.substr(0, name.length - 3);\n      }\n      var setter = createUniformSetter(info);\n      uniforms[name] = setter;\n      if (info.type == gl.SAMPLER_2D || info.type == gl.SAMPLER_CUBE) {\n        textures[name] = setter;\n      }\n    }\n\n    that.textures = textures;\n    that.attrib = attribs;\n    that.attribLoc = attribLocs;\n    that.uniform = uniforms;\n  }\n  createSetters(program);\n\n  this.loadNewShaders = function(vertexShaderSource, fragmentShaderSource) {\n    var program = loadProgram(gl, vertexShaderSource, fragmentShaderSource);\n    if (!program && !gl.isContextLost()) {\n      throw (\"could not compile program\");\n    }\n    that.program = program;\n    createSetters();\n  };\n\n  this.program = program;\n  this.good = this.asyncCallback ? false : true;\n\n  var checkLater = function() {\n    var e;\n    try {\n      checkShader(vs);\n      checkShader(fs);\n      checkProgram(program);\n    } catch (e) {\n      that.asyncCallback(e.toString());\n      return;\n    }\n    gl.tdl.programs.programDB[that.programId] = this;\n    that.asyncCallback();\n  };\n  if (this.asyncCallback) {\n    setTimeout(checkLater, 1000);\n  }\n};\n\ntdl.programs.handleContextLost_ = function() {\n  if (gl.tdl && gl.tdl.programs && gl.tdl.programs.shaderDB) {\n    delete gl.tdl.programs.shaderDB;\n    delete gl.tdl.programs.programDB;\n  }\n};\n\ntdl.programs.init_ = function() {\n  if (!gl.tdl.programs) {\n    gl.tdl.programs = { };\n    tdl.webgl.registerContextLostHandler(gl.canvas, tdl.programs.handleContextLost_, true);\n  }\n  if (!gl.tdl.programs.shaderDB) {\n    gl.tdl.programs.shaderDB = { };\n    gl.tdl.programs.programDB = { };\n  }\n};\n\n/**\n * Uses the current program (calls `gl.useProgram`)\n */\ntdl.programs.Program.prototype.use = function() {\n  gl.useProgram(this.program);\n};\n\n//function dumpValue(msg, name, value) {\n//  var str;\n//  if (value.length) {\n//      str = value[0].toString();\n//     for (var ii = 1; ii < value.length; ++ii) {\n//       str += \",\" + value[ii];\n//     }\n//  } else {\n//    str = value.toString();\n//  }\n//  tdl.log(msg + name + \": \" + str);\n//}\n\n/**\n * Sets a uniform to a value\n * @param {string} uniform name of uniform to set\n * @param {*} value value that is compatible with type of\n *        uniform.\n */\ntdl.programs.Program.prototype.setUniform = function(uniform, value) {\n  var func = this.uniform[uniform];\n  if (func) {\n    //dumpValue(\"SET UNI:\", uniform, value);\n    func(value);\n  }\n};\n\n/**\n * Sets a bunch of uniforms\n * @param {Object.<string, *>} uniforms uniform name value\n *        pairs.\n */\ntdl.programs.Program.prototype.applyUniforms = function(uniforms) {\n  for (var uniform in uniforms) {\n    this.setUniform(uniform, uniforms[uniform]);\n  }\n};\n\nreturn tdl.programs;\n});\n\n"
  },
  {
    "path": "tdl/quaternions.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * @fileoverview This file contains various functions for quaternion arithmetic\n * and converting between rotation matrices and quaternions.  It adds them to\n * the \"quaternions\" module on the tdl object.  Javascript arrays with\n * four entries are used to represent quaternions, and functions are provided\n * for doing operations on those.\n *\n * Operations are done assuming quaternions are of the form:\n * `q[0] + q[1]i + q[2]j + q[3]k` and using the hamiltonian\n * rules for multiplication as described on Brougham Bridge:\n * `i^2 = j^2 = k^2 = ijk = -1`.\n *\n */\n\ndefine(['./base-rs'], function(BaseRS) {\n\ntdl.provide('tdl.quaternions');\n/**\n * A Module for quaternion math.\n * @namespace\n */\ntdl.quaternions = tdl.quaternions || {};\n\n/**\n * A Quaternion.\n * @typedef {number[]} tdl.quaternions.Quaternion\n */\n\n/**\n * Quickly determines if the object a is a scalar or a quaternion;\n * assumes that the argument is either a number (scalar), or an array of\n * numbers.\n * @param {(number|tdl.quaternions.Quaternion)} a A number or array the type\n *     of which is in question.\n * @return {string} Either the string 'Scalar' or 'Quaternion'.\n */\ntdl.quaternions.mathType = function(a) {\n  if (typeof(a) === 'number')\n    return 'Scalar';\n  return 'Quaternion';\n};\n\n/**\n * Creates an identity quaternion.\n * @return {tdl.quaternions.Quaternion} The identity quaternion.\n */\ntdl.quaternions.identity = function() {\n  return [ 0, 0, 0, 1 ];\n};\n\n/**\n * Copies a quaternion.\n * @param {tdl.quaternions.Quaternion} q The quaternion.\n * @return {tdl.quaternions.Quaternion} A new quaternion identical to q.\n */\ntdl.quaternions.copy = function(q) {\n  return q.slice();\n};\n\n/**\n * Negates a quaternion.\n * @param {tdl.quaternions.Quaternion} q The quaternion.\n * @return {tdl.quaternions.Quaternion} -q.\n */\ntdl.quaternions.negative = function(q) {\n  return [-q[0], -q[1], -q[2], -q[3]];\n};\n\n/**\n * Adds two Quaternions.\n * @param {tdl.quaternions.Quaternion} a Operand Quaternion.\n * @param {tdl.quaternions.Quaternion} b Operand Quaternion.\n * @return {tdl.quaternions.Quaternion} The sum of a and b.\n */\ntdl.quaternions.addQuaternionQuaternion = function(a, b) {\n  return [a[0] + b[0],\n          a[1] + b[1],\n          a[2] + b[2],\n          a[3] + b[3]];\n};\n\n/**\n * Adds a quaternion to a scalar.\n * @param {tdl.quaternions.Quaternion} a Operand Quaternion.\n * @param {number} b Operand Scalar.\n * @return {tdl.quaternions.Quaternion} The sum of a and b.\n */\ntdl.quaternions.addQuaternionScalar = function(a, b) {\n  return a.slice(0, 3).concat(a[3] + b);\n};\n\n/**\n * Adds a scalar to a quaternion.\n * @param {number} a Operand scalar.\n * @param {tdl.quaternions.Quaternion} b Operand quaternion.\n * @return {tdl.quaternions.Quaternion} The sum of a and b.\n */\ntdl.quaternions.addScalarQuaternion = function(a, b) {\n  return b.slice(0, 3).concat(a + b[3]);\n};\n\n/**\n * Subtracts two quaternions.\n * @param {tdl.quaternions.Quaternion} a Operand quaternion.\n * @param {tdl.quaternions.Quaternion} b Operand quaternion.\n * @return {tdl.quaternions.Quaternion} The difference a - b.\n */\ntdl.quaternions.subQuaternionQuaternion = function(a, b) {\n  return [a[0] - b[0],\n          a[1] - b[1],\n          a[2] - b[2],\n          a[3] - b[3]];\n};\n\n/**\n * Subtracts a scalar from a quaternion.\n * @param {tdl.quaternions.Quaternion} a Operand quaternion.\n * @param {number} b Operand scalar.\n * @return {tdl.quaternions.Quaternion} The difference a - b.\n */\ntdl.quaternions.subQuaternionScalar = function(a, b) {\n  return a.slice(0, 3).concat(a[3] - b);\n};\n\n/**\n * Subtracts a quaternion from a scalar.\n * @param {number} a Operand scalar.\n * @param {tdl.quaternions.Quaternion} b Operand quaternion.\n * @return {tdl.quaternions.Quaternion} The difference a - b.\n */\ntdl.quaternions.subScalarQuaternion = function(a, b) {\n  return [-b[0], -b[1], -b[2], a - b[3]];\n};\n\n/**\n * Multiplies a scalar by a quaternion.\n * @param {number} k The scalar.\n * @param {tdl.quaternions.Quaternion} q The quaternion.\n * @return {tdl.quaternions.Quaternion} The product of k and q.\n */\ntdl.quaternions.mulScalarQuaternion = function(k, q) {\n  return [k * q[0], k * q[1], k * q[2], k * q[3]];\n};\n\n/**\n * Multiplies a quaternion by a scalar.\n * @param {tdl.quaternions.Quaternion} q The Quaternion.\n * @param {number} k The scalar.\n * @return {tdl.quaternions.Quaternion} The product of k and v.\n */\ntdl.quaternions.mulQuaternionScalar = function(q, k) {\n  return [k * q[0], k * q[1], k * q[2], k * q[3]];\n};\n\n/**\n * Multiplies two quaternions.\n * @param {tdl.quaternions.Quaternion} a Operand quaternion.\n * @param {tdl.quaternions.Quaternion} b Operand quaternion.\n * @return {tdl.quaternions.Quaternion} The quaternion product a * b.\n */\ntdl.quaternions.mulQuaternionQuaternion = function(a, b) {\n  var aX = a[0];\n  var aY = a[1];\n  var aZ = a[2];\n  var aW = a[3];\n  var bX = b[0];\n  var bY = b[1];\n  var bZ = b[2];\n  var bW = b[3];\n\n  return [\n      aW * bX + aX * bW + aY * bZ - aZ * bY,\n      aW * bY + aY * bW + aZ * bX - aX * bZ,\n      aW * bZ + aZ * bW + aX * bY - aY * bX,\n      aW * bW - aX * bX - aY * bY - aZ * bZ];\n};\n\n/**\n * Divides two quaternions; assumes the convention that a/b = a*(1/b).\n * @param {tdl.quaternions.Quaternion} a Operand quaternion.\n * @param {tdl.quaternions.Quaternion} b Operand quaternion.\n * @return {tdl.quaternions.Quaternion} The quaternion quotient a / b.\n */\ntdl.quaternions.divQuaternionQuaternion = function(a, b) {\n  var aX = a[0];\n  var aY = a[1];\n  var aZ = a[2];\n  var aW = a[3];\n  var bX = b[0];\n  var bY = b[1];\n  var bZ = b[2];\n  var bW = b[3];\n\n  var d = 1 / (bW * bW + bX * bX + bY * bY + bZ * bZ);\n  return [\n      (aX * bW - aW * bX - aY * bZ + aZ * bY) * d,\n      (aX * bZ - aW * bY + aY * bW - aZ * bX) * d,\n      (aY * bX + aZ * bW - aW * bZ - aX * bY) * d,\n      (aW * bW + aX * bX + aY * bY + aZ * bZ) * d];\n};\n\n/**\n * Divides a Quaternion by a scalar.\n * @param {tdl.quaternions.Quaternion} q The quaternion.\n * @param {number} k The scalar.\n * @return {tdl.quaternions.Quaternion} q The quaternion q divided by k.\n */\ntdl.quaternions.divQuaternionScalar = function(q, k) {\n  return [q[0] / k, q[1] / k, q[2] / k, q[3] / k];\n};\n\n/**\n * Divides a scalar by a quaternion.\n * @param {number} a Operand scalar.\n * @param {tdl.quaternions.Quaternion} b Operand quaternion.\n * @return {tdl.quaternions.Quaternion} The quaternion product.\n */\ntdl.quaternions.divScalarQuaternion = function(a, b) {\n  var b0 = b[0];\n  var b1 = b[1];\n  var b2 = b[2];\n  var b3 = b[3];\n\n  var d = 1 / (b0 * b0 + b1 * b1 + b2 * b2 + b3 * b3);\n  return [-a * b0 * d, -a * b1 * d, -a * b2 * d, a * b3 * d];\n};\n\n/**\n * Computes the multiplicative inverse of a quaternion.\n * @param {tdl.quaternions.Quaternion} q The quaternion.\n * @return {tdl.quaternions.Quaternion} The multiplicative inverse of q.\n */\ntdl.quaternions.inverse = function(q) {\n  var q0 = q[0];\n  var q1 = q[1];\n  var q2 = q[2];\n  var q3 = q[3];\n\n  var d = 1 / (q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);\n  return [-q0 * d, -q1 * d, -q2 * d, q3 * d];\n};\n\n/**\n * Multiplies two objects which are either scalars or quaternions.\n * @param {(tdl.quaternions.Quaternion|number)} a Operand.\n * @param {(tdl.quaternions.Quaternion|number)} b Operand.\n * @return {(tdl.quaternions.Quaternion|number)} The product of a and b.\n */\ntdl.quaternions.mul = function(a, b) {\n  return tdl.quaternions['mul' + tdl.quaternions.mathType(a) +\n      tdl.quaternions.mathType(b)](a, b);\n};\n\n/**\n * Divides two objects which are either scalars or quaternions.\n * @param {(tdl.quaternions.Quaternion|number)} a Operand.\n * @param {(tdl.quaternions.Quaternion|number)} b Operand.\n * @return {(tdl.quaternions.Quaternion|number)} The quotient of a and b.\n */\ntdl.quaternions.div = function(a, b) {\n  return tdl.quaternions['div' + tdl.quaternions.mathType(a) +\n      tdl.quaternions.mathType(b)](a, b);\n};\n\n/**\n * Adds two objects which are either scalars or quaternions.\n * @param {(tdl.quaternions.Quaternion|number)} a Operand.\n * @param {(tdl.quaternions.Quaternion|number)} b Operand.\n * @return {(tdl.quaternions.Quaternion|number)} The sum of a and b.\n */\ntdl.quaternions.add = function(a, b) {\n  return tdl.quaternions['add' + tdl.quaternions.mathType(a) +\n      tdl.quaternions.mathType(b)](a, b);\n};\n\n/**\n * Subtracts two objects which are either scalars or quaternions.\n * @param {(tdl.quaternions.Quaternion|number)} a Operand.\n * @param {(tdl.quaternions.Quaternion|number)} b Operand.\n * @return {(tdl.quaternions.Quaternion|number)} The difference of a and b.\n */\ntdl.quaternions.sub = function(a, b) {\n  return tdl.quaternions['sub' + tdl.quaternions.mathType(a) +\n      tdl.quaternions.mathType(b)](a, b);\n};\n\n/**\n * Computes the length of a Quaternion, i.e. the square root of the\n * sum of the squares of the coefficients.\n * @param {tdl.quaternions.Quaternion} a The Quaternion.\n * @return {number} The length of a.\n */\ntdl.quaternions.length = function(a) {\n  return Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]);\n};\n\n/**\n * Computes the square of the length of a quaternion, i.e. the sum of the\n * squares of the coefficients.\n * @param {tdl.quaternions.Quaternion} a The quaternion.\n * @return {number} The square of the length of a.\n */\ntdl.quaternions.lengthSquared = function(a) {\n  return a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3];\n};\n\n/**\n * Divides a Quaternion by its length and returns the quotient.\n * @param {tdl.quaternions.Quaternion} a The Quaternion.\n * @return {tdl.quaternions.Quaternion} A unit length quaternion pointing in\n *     the same direction as a.\n */\ntdl.quaternions.normalize = function(a) {\n  var d = 1 / Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]);\n  return [a[0] * d, a[1] * d, a[2] * d, a[3] * d];\n};\n\n/**\n * Computes the conjugate of the given quaternion.\n * @param {tdl.quaternions.Quaternion} q The quaternion.\n * @return {tdl.quaternions.Quaternion} The conjugate of q.\n */\ntdl.quaternions.conjugate = function(q) {\n  return [-q[0], -q[1], -q[2], q[3]];\n};\n\n\n/**\n * Creates a quaternion which rotates around the x-axis by the given angle.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.quaternions.Quaternion} The quaternion.\n */\ntdl.quaternions.rotationX = function(angle) {\n  return [Math.sin(angle / 2), 0, 0, Math.cos(angle / 2)];\n};\n\n/**\n * Creates a quaternion which rotates around the y-axis by the given angle.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.quaternions.Quaternion} The quaternion.\n */\ntdl.quaternions.rotationY = function(angle) {\n  return [0, Math.sin(angle / 2), 0, Math.cos(angle / 2)];\n};\n\n/**\n * Creates a quaternion which rotates around the z-axis by the given angle.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.quaternions.Quaternion} The quaternion.\n */\ntdl.quaternions.rotationZ = function(angle) {\n  return [0, 0, Math.sin(angle / 2), Math.cos(angle / 2)];\n};\n\n/**\n * Creates a quaternion which rotates around the given axis by the given\n * angle.\n * @param {tdl.math.Vector3} axis The axis about which to rotate.\n * @param {number} angle The angle by which to rotate (in radians).\n * @return {tdl.quaternions.Quaternion} A quaternion which rotates angle\n *     radians around the axis.\n */\ntdl.quaternions.axisRotation = function(axis, angle) {\n  var d = 1 / Math.sqrt(axis[0] * axis[0] +\n                        axis[1] * axis[1] +\n                        axis[2] * axis[2]);\n  var sin = Math.sin(angle / 2);\n  var cos = Math.cos(angle / 2);\n  return [sin * axis[0] * d, sin * axis[1] * d, sin * axis[2] * d, cos];\n};\n\n/**\n * Computes a 4-by-4 rotation matrix (with trivial translation component)\n * given a quaternion.  We assume the convention that to rotate a vector v by\n * a quaternion r means to express that vector as a quaternion q by letting\n * `q = [v[0], v[1], v[2], 0]` and then obtain the rotated\n * vector by evaluating the expression `(r * q) / r`.\n * @param {tdl.quaternions.Quaternion} q The quaternion.\n * @return {tdl.math.Matrix4} A 4-by-4 rotation matrix.\n */\ntdl.quaternions.quaternionToRotation = function(q) {\n  var qX = q[0];\n  var qY = q[1];\n  var qZ = q[2];\n  var qW = q[3];\n\n  var qWqW = qW * qW;\n  var qWqX = qW * qX;\n  var qWqY = qW * qY;\n  var qWqZ = qW * qZ;\n  var qXqW = qX * qW;\n  var qXqX = qX * qX;\n  var qXqY = qX * qY;\n  var qXqZ = qX * qZ;\n  var qYqW = qY * qW;\n  var qYqX = qY * qX;\n  var qYqY = qY * qY;\n  var qYqZ = qY * qZ;\n  var qZqW = qZ * qW;\n  var qZqX = qZ * qX;\n  var qZqY = qZ * qY;\n  var qZqZ = qZ * qZ;\n\n  var d = qWqW + qXqX + qYqY + qZqZ;\n\n  return [\n    (qWqW + qXqX - qYqY - qZqZ) / d,\n     2 * (qWqZ + qXqY) / d,\n     2 * (qXqZ - qWqY) / d, 0,\n\n     2 * (qXqY - qWqZ) / d,\n     (qWqW - qXqX + qYqY - qZqZ) / d,\n     2 * (qWqX + qYqZ) / d, 0,\n\n     2 * (qWqY + qXqZ) / d,\n     2 * (qYqZ - qWqX) / d,\n     (qWqW - qXqX - qYqY + qZqZ) / d, 0,\n\n     0, 0, 0, 1];\n};\n\n/**\n * Computes a quaternion whose rotation is equivalent to the given matrix.\n * @param {(tdl.math.Matrix4|tdl.math.Matrix3)} m A 3-by-3 or 4-by-4\n *     rotation matrix.\n * @return {tdl.quaternions.Quaternion} A quaternion q such that\n *     quaternions.quaternionToRotation(q) is m.\n */\ntdl.quaternions.rotationToQuaternion = function(m) {\n  var u;\n  var v;\n  var w;\n\n  // Choose u, v, and w such that u is the index of the biggest diagonal entry\n  // of m, and u v w is an even permutation of 0 1 and 2.\n  if (m[0*4+0] > m[1*4+1] && m[0*4+0] > m[2*4+2]) {\n    u = 0;\n    v = 1;\n    w = 2;\n  } else if (m[1*4+1] > m[0*4+0] && m[1*4+1] > m[2*4+2]) {\n    u = 1;\n    v = 2;\n    w = 0;\n  } else {\n    u = 2;\n    v = 0;\n    w = 1;\n  }\n\n  var r = Math.sqrt(1 + m[u*4+u] - m[v*4+v] - m[w*4+w]);\n  var q = [];\n  q[u] = 0.5 * r;\n  q[v] = 0.5 * (m[v*4+u] + m[u*4+v]) / r;\n  q[w] = 0.5 * (m[u*4+w] + m[w*4+u]) / r;\n  q[3] = 0.5 * (m[v*4+w] - m[w*4+v]) / r;\n\n  return q;\n};\n\nreturn tdl.quaternions;\n});\n\n"
  },
  {
    "path": "tdl/screenshot.js",
    "content": "/*\r\n * Copyright 2009, Google Inc.\r\n * All rights reserved.\r\n *\r\n * Redistribution and use in source and binary forms, with or without\r\n * modification, are permitted provided that the following conditions are\r\n * met:\r\n *\r\n *     * Redistributions of source code must retain the above copyright\r\n * notice, this list of conditions and the following disclaimer.\r\n *     * Redistributions in binary form must reproduce the above\r\n * copyright notice, this list of conditions and the following disclaimer\r\n * in the documentation and/or other materials provided with the\r\n * distribution.\r\n *     * Neither the name of Google Inc. nor the names of its\r\n * contributors may be used to endorse or promote products derived from\r\n * this software without specific prior written permission.\r\n *\r\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n */\r\n\r\n\r\n/**\r\n * @fileoverview This file contains various functions for taking a screenshot\r\n */\r\ndefine(['./base-rs', './io'], function(BaseRS, IO) {\r\n\r\ntdl.provide('tdl.screenshot');\r\n\r\n/**\r\n * @namespace\r\n */\r\ntdl.screenshot = tdl.screenshot || {};\r\n\r\n/**\r\n * takes a screenshot of a canvas. Sends it to the server to be saved.\r\n */\r\ntdl.screenshot.takeScreenshot = function(canvas) {\r\n  tdl.io.sendJSON(\r\n      this.url,\r\n      {cmd: 'screenshot', dataURL: canvas.toDataURL()},\r\n      function() {});\r\n};\r\n\r\nreturn tdl.screenshot;\r\n});\r\n\r\n\r\n"
  },
  {
    "path": "tdl/shader.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains a class which assists with the\n * loading of GLSL shaders.\n */\ndefine(['./base-rs'], function(BaseRS) {\n\ntdl.provide('tdl.shader');\n/**\n * A module for shaders.\n * @namespace\n */\ntdl.shader = tdl.shader || {};\n\n/**\n\n * Loads a shader from vertex and fragment programs specified in\n * \"script\" nodes in the HTML page. This provides a convenient\n * mechanism for writing GLSL snippets without the burden of\n * additional syntax like per-line quotation marks.\n * @param {WebGLRenderingContext} gl The WebGLRenderingContext\n *     into which the shader will be loaded.\n * @param {string} vertexScriptName The name of the HTML Script node\n *     containing the vertex program.\n * @param {string} fragmentScriptName The name of the HTML Script node\n *     containing the fragment program.\n */\ntdl.shader.loadFromScriptNodes = function(gl,\n                                          vertexScriptName,\n                                          fragmentScriptName) {\n  var vertexScript = document.getElementById(vertexScriptName);\n  var fragmentScript = document.getElementById(fragmentScriptName);\n  if (!vertexScript || !fragmentScript)\n    return null;\n  return new tdl.shader.Shader(gl,\n                               vertexScript.text,\n                               fragmentScript.text);\n}\n\n/**\n * Helper which convers GLSL names to JavaScript names.\n * @private\n */\ntdl.shader.glslNameToJs_ = function(name) {\n  return name.replace(/_(.)/g, function(_, p1) { return p1.toUpperCase(); });\n}\n\n/**\n * Creates a new Shader object, loading and linking the given vertex\n * and fragment shaders into a program.\n * @param {WebGLRenderingContext} gl The WebGLRenderingContext\n *     into which the shader will be loaded.\n * @param {string} vertex The vertex shader.\n * @param {string} fragment The fragment shader.\n */\ntdl.shader.Shader = function(gl, vertex, fragment) {\n  this.program = gl.createProgram();\n  this.gl = gl;\n\n  var vs = this.loadShader(this.gl.VERTEX_SHADER, vertex);\n  if (!vs) {\n    tdl.log(\"couldn't load shader\")\n  }\n  this.gl.attachShader(this.program, vs);\n  this.gl.deleteShader(vs);\n\n  var fs = this.loadShader(this.gl.FRAGMENT_SHADER, fragment);\n  if (!fs) {\n    tdl.log(\"couldn't load shader\")\n  }\n  this.gl.attachShader(this.program, fs);\n  this.gl.deleteShader(fs);\n\n  this.gl.linkProgram(this.program);\n  this.gl.useProgram(this.program);\n\n  // Check the link status\n  var linked = this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS);\n  if (!linked && !this.gl.isContextLost()) {\n    var infoLog = this.gl.getProgramInfoLog(this.program);\n    tdl.error(\"Error linking program:\\n\" + infoLog);\n    this.gl.deleteProgram(this.program);\n    this.program = null;\n    return;\n  }\n\n  // find uniforms and attributes\n  var re = /(uniform|attribute)\\s+\\S+\\s+(\\S+)\\s*;/g;\n  var match = null;\n  while ((match = re.exec(vertex + '\\n' + fragment)) != null) {\n    var glslName = match[2];\n    var jsName = tdl.shader.glslNameToJs_(glslName);\n    var loc = -1;\n    if (match[1] == \"uniform\") {\n      this[jsName + \"Loc\"] = this.getUniform(glslName);\n    } else if (match[1] == \"attribute\") {\n      this[jsName + \"Loc\"] = this.getAttribute(glslName);\n    }\n    if (loc >= 0) {\n      this[jsName + \"Loc\"] = loc;\n    }\n  }\n}\n\n/**\n * Binds the shader's program.\n */\ntdl.shader.Shader.prototype.bind = function() {\n  this.gl.useProgram(this.program);\n}\n\n/**\n * Helper for loading a shader.\n * @private\n */\ntdl.shader.Shader.prototype.loadShader = function(type, shaderSrc) {\n  var shader = this.gl.createShader(type);\n  if (shader == null) {\n    return null;\n  }\n\n  // Load the shader source\n  this.gl.shaderSource(shader, shaderSrc);\n  // Compile the shader\n  this.gl.compileShader(shader);\n  // Check the compile status\n  if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {\n    var infoLog = this.gl.getShaderInfoLog(shader);\n    tdl.error(\"Error compiling shader:\\n\" + infoLog);\n    this.gl.deleteShader(shader);\n    return null;\n  }\n  return shader;\n}\n\n/**\n * Helper for looking up an attribute's location.\n * @private\n */\ntdl.shader.Shader.prototype.getAttribute = function(name) {\n  return this.gl.getAttribLocation(this.program, name);\n};\n\n/**\n * Helper for looking up an attribute's location.\n * @private\n */\ntdl.shader.Shader.prototype.getUniform = function(name) {\n  return this.gl.getUniformLocation(this.program, name);\n}\n\nreturn tdl.shader;\n});\n"
  },
  {
    "path": "tdl/string.js",
    "content": "/*\r\n * Copyright 2009, Google Inc.\r\n * All rights reserved.\r\n *\r\n * Redistribution and use in source and binary forms, with or without\r\n * modification, are permitted provided that the following conditions are\r\n * met:\r\n *\r\n *     * Redistributions of source code must retain the above copyright\r\n * notice, this list of conditions and the following disclaimer.\r\n *     * Redistributions in binary form must reproduce the above\r\n * copyright notice, this list of conditions and the following disclaimer\r\n * in the documentation and/or other materials provided with the\r\n * distribution.\r\n *     * Neither the name of Google Inc. nor the names of its\r\n * contributors may be used to endorse or promote products derived from\r\n * this software without specific prior written permission.\r\n *\r\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n */\r\n\r\n\r\n/**\r\n * @fileoverview This file contains objects strings.\r\n */\r\ndefine(['./base-rs'], function(BaseRS) {\r\n\r\ntdl.provide('tdl.string');\r\n/**\r\n * A module for string.\r\n * @namespace\r\n */\r\ntdl.string = tdl.string || {};\r\n\r\n/**\r\n * Whether a haystack ends with a needle.\r\n * @param {string} haystack String to search\r\n * @param {string} needle String to search for.\r\n * @return {boolean} True if haystack ends with needle.\r\n */\r\ntdl.string.endsWith = function(haystack, needle) {\r\n  return haystack.substr(haystack.length - needle.length) === needle;\r\n};\r\n\r\n/**\r\n * Whether a haystack starts with a needle.\r\n * @param {string} haystack String to search\r\n * @param {string} needle String to search for.\r\n * @return {boolean} True if haystack starts with needle.\r\n */\r\ntdl.string.startsWith = function(haystack, needle) {\r\n  return haystack.substr(0, needle.length) === needle;\r\n};\r\n\r\n/**\r\n * Converts a non-homogenious array into a string.\r\n * @param {Array.<*>} args Args to turn into a string\r\n * @return {string} string representing args\r\n */\r\ntdl.string.argsToString = function(args) {\r\n  var lastArgWasNumber = false;\r\n  var numArgs = args.length;\r\n  var strs = [];\r\n  for (var ii = 0; ii < numArgs; ++ii) {\r\n    var arg = args[ii];\r\n    if (arg === undefined) {\r\n      strs.push('undefined');\r\n    } else if (typeof arg == 'number') {\r\n      if (lastArgWasNumber) {\r\n        strs.push(\", \");\r\n      }\r\n      if (arg == Math.floor(arg)) {\r\n        strs.push(arg.toFixed(0));\r\n      } else {\r\n      strs.push(arg.toFixed(3));\r\n      }\r\n      lastArgWasNumber = true;\r\n    } else if (window.Float32Array && arg instanceof Float32Array) {\r\n      // TODO(gman): Make this handle other types of arrays.\r\n      strs.push(tdl.string.argsToString(arg));\r\n    } else {\r\n      strs.push(arg.toString());\r\n      lastArgWasNumber = false;\r\n    }\r\n  }\r\n  return strs.join(\"\");\r\n};\r\n\r\n/**\r\n * Converts an object into a string. Similar to JSON.stringify but just used\r\n * for debugging.\r\n * @param {*} obj object to stringify.\r\n * @param {string?} opt_prefix optional prefix\r\n * @return {string} stringified object\r\n */\r\ntdl.string.objToString = function(obj, opt_prefix) {\r\n  var strs = [];\r\n\r\n  function objToString(obj, opt_prefix) {\r\n    opt_prefix = opt_prefix || \"\";\r\n    if (typeof obj == 'object') {\r\n      if (obj.length !== undefined) {\r\n        for (var ii = 0; ii < obj.length; ++ii) {\r\n          objToString(obj[ii], opt_prefix + \"[\" + ii + \"]\");\r\n        }\r\n      } else {\r\n        for (var name in obj) {\r\n          objToString(obj[name], opt_prefix + \".\" + name);\r\n        }\r\n      }\r\n    } else {\r\n      strs.push(tdl.string.argsToString([opt_prefix, \": \", obj]));\r\n    }\r\n  }\r\n\r\n  objToString(obj);\r\n\r\n  return strs.join(\"\\n\");\r\n};\r\n\r\nreturn tdl.string;\r\n});\r\n\r\n"
  },
  {
    "path": "tdl/sync.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains objects to sync app settings across\n * browsers.\n */\ndefine(\n    [ './base-rs',\n      './log',\n      './io',\n      './misc',\n    ], function(\n      BaseRS,\n      Log,\n      IO,\n      Misc) {\n\ntdl.provide('tdl.sync');\n/**\n * A module for sync.\n * @namespace\n */\ntdl.sync = tdl.sync || {};\n\n/**\n * Manages synchronizing settings across browsers. Requires a server\n * running to support it. Note that even if you don't want to sync\n * across browsers you can still use the SyncManager.\n *\n * @constructor\n * @param {!Object} settings The object that contains the settings you\n *     want kept in sync.\n */\ntdl.sync.SyncManager = function(settings, opt_callback) {\n  this.settings = settings;\n  this.putCount = 0;\n  this.getCount = 0;\n  this.callback = opt_callback || function() {};\n\n  // This probably should not be here.\n  tdl.misc.applyUrlSettings(settings);\n}\n\n/**\n * Initialize the sync manager to start syncing settings with a server.\n * @param {string} url Url of server.\n * @param {boolean} slave true if this page is a slave. Slaves only receive\n *     settings from the server. Non slaves send settings the server.\n */\ntdl.sync.SyncManager.prototype.init = function(url, slave) {\n  var that = this;\n  this.sync = true;\n  this.slave = slave;\n  this.socket = new WebSocket(url);\n  this.opened = false;\n  this.queued = [];\n  this.socket.onopen = function(event) {\n    tdl.log(\"SOCKET OPENED!\");\n    that.opened = true;\n    for (var ii = 0; ii < that.queued.length; ++ii) {\n      var settings = that.queued[ii];\n      ++that.putCount;\n      tdl.log(\"--PUT:[\", that.putCount, \"]-------------\");\n      tdl.log(settings);\n      that.socket.send(settings);\n    }\n    that.queued = [];\n  };\n  this.socket.onerror = function(event) {\n    tdl.log(\"SOCKET ERROR!\");\n  };\n  this.socket.onclose = function(event) {\n    tdl.log(\"SOCKET CLOSED!\");\n  };\n  this.socket.onmessage = function(event) {\n    ++that.getCount;\n    tdl.log(\"--GET:[\", that.getCount, \":\", event.type, \"]-------------\");\n    var obj = JSON.parse(event.data);\n    tdl.dumpObj(obj);\n    tdl.misc.copyProperties(obj, that.settings);\n    that.callback(obj);\n  };\n};\n\n/**\n * Sets the settings.\n *\n * If we are synchronizing settings the settings are sent to the server.\n * Otherwise they are applied directy.\n *\n * @param {!Object} settings Object with new settings.\n */\ntdl.sync.SyncManager.prototype.setSettings = function(settings) {\n  if (this.sync) {\n    if (!this.slave) {\n      if (this.socket) {\n        if (!this.opened) {\n          this.queued.push(JSON.stringify(settings));\n        } else {\n          ++this.putCount;\n          tdl.log(\"--PUT:[\", this.putCount, \"]-------------\");\n          tdl.dumpObj(settings);\n          this.socket.send(JSON.stringify(settings));\n        }\n      }\n    }\n  } else {\n    tdl.misc.copyProperties(settings, this.settings);\n    this.callback(settings);\n  }\n};\n\nreturn tdl.sync;\n});\n\n"
  },
  {
    "path": "tdl/textures.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains objects to manage textures.\n */\ndefine(['./base-rs', './webgl'], function(BaseRS, WebGL) {\n\ntdl.provide('tdl.textures');\n/**\n * A module for textures.\n * @namespace\n */\ntdl.textures = tdl.textures || {};\n\n/**\n * Loads a texture\n * @param\n *        {(tdl.math.Vector4|string|string[]|HTMLImageElement|HTMLCanvasElement)}\n *        src Passing a color makes a solid 1pixel 2d texture,\n *        passing a URL makes a 2d texture with that url,\n *        passing an array of urls makes a cubemap, passing an\n *        img or canvas makes a 2d texture with that image.\n * @param {boolean} opt_flipY Flip the texture in Y?\n * @param {function()} opt_callback Function to execute when\n *        texture is loaded.\n * @return {tdl.textures.Texture} the created texture.\n */\ntdl.textures.loadTexture = function(src, opt_flipY, opt_callback) {\n  var id;\n  if (typeof src == 'string') {\n    td = src;\n  } else if (src.length == 4 && typeof src[0] == 'number') {\n    id = src.toString();\n  } else if ((src.length == 1 || src.length == 6) &&\n             typeof src[0] == 'string') {\n    id = src.toString();\n  } else if (src.tagName == 'CANVAS') {\n    id = undefined;\n  } else if (src.tagName == 'IMG') {\n    id = src.src;\n  } else if (src.width) {\n    id = undefined;\n  } else {\n    throw \"bad srcs\";\n  }\n\n  var texture;\n  tdl.textures.init_(gl);\n  if (id !== undefined) {\n    texture = gl.tdl.textures.db[id];\n  }\n  if (texture) {\n    return texture;\n  }\n  if (typeof src == 'string') {\n    texture = new tdl.textures.Texture2D(src, opt_flipY, opt_callback);\n  } else if (src.length == 4 && typeof src[0] == 'number') {\n    texture = new tdl.textures.SolidTexture(src);\n  } else if ((src.length == 1 || src.length == 6) &&\n             typeof src[0] == 'string') {\n    texture = new tdl.textures.CubeMap(src);\n  } else if (src.tagName == 'CANVAS' || src.tagName == 'IMG') {\n    texture = new tdl.textures.Texture2D(src, opt_flipY);\n  } else if (src.width) {\n    texture = new tdl.textures.ColorTexture2D(src);\n  } else {\n    throw \"bad srcs\";\n  }\n  gl.tdl.textures.db[src.toString()] = texture;\n  return texture;\n};\n\ntdl.textures.addLoadingImage_ = function(img) {\n  tdl.textures.init_(gl);\n  gl.tdl.textures.loadingImages.push(img);\n};\n\ntdl.textures.removeLoadingImage_ = function(img) {\n  gl.tdl.textures.loadingImages.splice(gl.tdl.textures.loadingImages.indexOf(img), 1);\n};\n\ntdl.textures.init_ = function(gl) {\n  if (!gl.tdl.textures) {\n    gl.tdl.textures = { };\n    gl.tdl.textures.loadingImages = [];\n    tdl.webgl.registerContextLostHandler(\n        gl.canvas, tdl.textures.handleContextLost_, true);\n  }\n  if (!gl.tdl.textures.maxTextureSize) {\n    gl.tdl.textures.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);\n    gl.tdl.textures.maxCubeMapSize = gl.getParameter(\n        gl.MAX_CUBE_MAP_TEXTURE_SIZE);\n  }\n  if (!gl.tdl.textures.db) {\n    gl.tdl.textures.db = { };\n  }\n};\n\ntdl.textures.handleContextLost_ = function() {\n  if (gl.tdl && gl.tdl.textures) {\n    delete gl.tdl.textures.db;\n    var imgs = gl.tdl.textures.loadingImages;\n    for (var ii = 0; ii < imgs.length; ++ii) {\n      imgs[ii].onload = undefined;\n    }\n    gl.tdl.textures.loadingImages = [];\n  }\n};\n\n/**\n * Base class for all textures.\n * @constructor\n * @private\n * @param {number} target GL target like `gl.TEXTURE_2D`\n */\ntdl.textures.Texture = function(target) {\n  this.target = target;\n  this.texture = gl.createTexture();\n  this.params = { };\n};\n\n/**\n * Deletes a texture\n */\ntdl.textures.Texture.prototype.destroy = function() {\n  gl.deleteTexture(this.texture);\n};\n\n/**\n * Set a texture parameter\n * @param {number} pname eg. `gl.TEXTURE_MAG_FILTER`,\n *        `gl.TEXTURE_WRAP_S`\n * @param {number} value eg. `gl.LINEAR`\n */\ntdl.textures.Texture.prototype.setParameter = function(pname, value) {\n  this.params[pname] = value;\n  gl.bindTexture(this.target, this.texture);\n  gl.texParameteri(this.target, pname, value);\n};\n\ntdl.textures.Texture.prototype.setParameterIfNotSet_ = function(pname, value) {\n  if (!this.params[pname]) {\n    this.setParameter(pname, value);\n  }\n};\n\ntdl.textures.Texture.prototype.setFilteringBasedOnDimensions = function(width, height) {\n  if (tdl.textures.isPowerOf2(width) &&\n      tdl.textures.isPowerOf2(height)) {\n    this.setParameterIfNotSet_(gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);\n    gl.generateMipmap(this.target);\n  } else {\n    this.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n    this.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n    this.setParameterIfNotSet_(gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n  }\n};\n\n/**\n * A solid color texture.\n * @constructor\n * @param {tdl.math.vector4} color color for texture.\n */\ntdl.textures.SolidTexture = function(color) {\n  tdl.textures.Texture.call(this, gl.TEXTURE_2D);\n  this.color = color.slice(0, 4);\n  this.uploadTexture();\n};\n\ntdl.base.inherit(tdl.textures.SolidTexture, tdl.textures.Texture);\n\ntdl.textures.SolidTexture.prototype.uploadTexture = function() {\n  gl.bindTexture(gl.TEXTURE_2D, this.texture);\n  var pixel = new Uint8Array(this.color);\n  gl.texImage2D(\n    gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixel);\n};\n\ntdl.textures.SolidTexture.prototype.bindToUnit = function(unit) {\n  gl.activeTexture(gl.TEXTURE0 + unit);\n  gl.bindTexture(gl.TEXTURE_2D, this.texture);\n};\n\n/**\n * A depth texture.\n * @constructor\n * @param {number} width\n * @param {number} height\n */\ntdl.textures.DepthTexture = function(width, height) {\n  if (!gl.tdl.depthTexture) {\n    throw(\"depth textures not supported\");\n  }\n  tdl.textures.Texture.call(this, gl.TEXTURE_2D);\n  this.width = width;\n  this.height = height;\n  this.uploadTexture();\n};\n\ntdl.base.inherit(tdl.textures.DepthTexture, tdl.textures.Texture);\n\ntdl.textures.DepthTexture.prototype.uploadTexture = function() {\n  gl.bindTexture(gl.TEXTURE_2D, this.texture);\n  gl.texImage2D(\n    gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, this.width, this.height, 0,\n    gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);\n  this.setParameter(gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n  this.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n  this.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n  this.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n};\n\ntdl.textures.DepthTexture.prototype.bindToUnit = function(unit) {\n  gl.activeTexture(gl.TEXTURE0 + unit);\n  gl.bindTexture(gl.TEXTURE_2D, this.texture);\n};\n\n/**\n * @typedef {Object} ColorTextureData\n * @memberOf tdl.textures\n * @property {number} width width in pixels\n * @property {number} height height in pixels\n * @property {number[]|ArrayBufferView} data pixels\n */\n\n/**\n * A color from an array of values texture.\n * @constructor\n * @param {tdl.primitives.ColorTextureData} data pixels\n * @param {number} opt_format Default `gl.RGBA`\n * @param {number} opt_type Default `gl.UNSIGNED_BYTE`\n */\ntdl.textures.ColorTexture = function(data, opt_format, opt_type) {\n  tdl.textures.Texture.call(this, gl.TEXTURE_2D);\n  this.format = opt_format || gl.RGBA;\n  this.type   = opt_type || gl.UNSIGNED_BYTE;\n  if (data.pixels instanceof Array) {\n    data.pixels = new Uint8Array(data.pixels);\n  }\n  this.data = data;\n  this.uploadTexture();\n};\n\ntdl.base.inherit(tdl.textures.ColorTexture, tdl.textures.Texture);\n\ntdl.textures.ColorTexture.prototype.uploadTexture = function() {\n  gl.bindTexture(gl.TEXTURE_2D, this.texture);\n  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n  gl.texImage2D(\n    gl.TEXTURE_2D, 0, this.format, this.data.width, this.data.height,\n    0, this.format, this.type, this.data.pixels);\n  this.setFilteringBasedOnDimensions(this.data.width, this.data.height);\n};\n\ntdl.textures.ColorTexture.prototype.bindToUnit = function(unit) {\n  gl.activeTexture(gl.TEXTURE0 + unit);\n  gl.bindTexture(gl.TEXTURE_2D, this.texture);\n};\n\n/**\n * A 2D texture from an image\n * @constructor\n * @param {(string|HTMLElement)} url URL of image to load into\n *        texture or exiting image/canvas/video element.\n * @param {boolean?} opt_flipY true to flip image vertically\n * @param {function()?} opt_callback Function to execute when\n *        texture is loaded.\n */\ntdl.textures.Texture2D = function(url, opt_flipY, opt_callback) {\n  tdl.textures.Texture.call(this, gl.TEXTURE_2D);\n  this.flipY = opt_flipY || false;\n  var that = this;\n  var img;\n  // Handle dataURLs?\n  if (typeof url !== 'string') {\n    img = url;\n    this.loaded = true;\n    if (opt_callback) {\n      opt_callback();\n    }\n  } else {\n    img = document.createElement('img');\n    tdl.textures.addLoadingImage_(img);\n    img.onload = function() {\n      tdl.textures.removeLoadingImage_(img);\n      //tdl.log(\"loaded image: \", url);\n      that.updateTexture();\n      if (opt_callback) {\n        opt_callback();\n      }\n    };\n    img.onerror = function() {\n      tdl.log(\"could not load image: \", url);\n    };\n  }\n  this.img = img;\n  this.uploadTexture();\n\n  if (!this.loaded) {\n    img.src = url;\n  }\n};\n\ntdl.base.inherit(tdl.textures.Texture2D, tdl.textures.Texture);\n\n/**\n * Check if a number if a power of 2\n * @param {number} value value to check.\n * @returns {boolean} true if value is power of 2.\n */\ntdl.textures.isPowerOf2 = function(value) {\n  return (value & (value - 1)) == 0;\n};\n\ntdl.textures.Texture2D.prototype.uploadTexture = function() {\n  gl.bindTexture(gl.TEXTURE_2D, this.texture);\n  if (this.loaded) {\n    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.flipY);\n    this.setTexture(this.img);\n  } else {\n    var pixel = new Uint8Array([255, 255, 255, 255]);\n    gl.texImage2D(\n        gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixel);\n  }\n};\n\n/**\n * Set a texture to a new image\n * @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement}\n *        element image element to set texture.\n */\ntdl.textures.Texture2D.prototype.setTexture = function(element) {\n  // TODO(gman): use texSubImage2D if the size is the same.\n  gl.bindTexture(gl.TEXTURE_2D, this.texture);\n  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, element);\n  this.setFilteringBasedOnDimensions(element.width, element.height);\n};\n\ntdl.textures.Texture2D.prototype.updateTexture = function() {\n  this.loaded = true;\n  this.uploadTexture();\n};\n\ntdl.textures.Texture2D.prototype.bindToUnit = function(unit) {\n  gl.activeTexture(gl.TEXTURE0 + unit);\n  gl.bindTexture(gl.TEXTURE_2D, this.texture);\n};\n\n/**\n * Create a texture to be managed externally.\n * @constructor\n * @param {number} type GL enum for texture type, eg\n *        `gl.TEXTURE_2D`.\n */\ntdl.textures.ExternalTexture = function(type) {\n  tdl.textures.Texture.call(this, type);\n  this.type = type;\n};\n\ntdl.base.inherit(tdl.textures.ExternalTexture, tdl.textures.Texture);\n\ntdl.textures.ExternalTexture.prototype.bindToUnit = function(unit) {\n  gl.activeTexture(gl.TEXTURE0 + unit);\n  gl.bindTexture(this.type, this.texture);\n}\n\n/**\n * Create a 2D texture to be managed externally.\n * @constructor\n */\ntdl.textures.ExternalTexture2D = function() {\n  tdl.textures.ExternalTexture.call(this, gl.TEXTURE_2D);\n};\n\ntdl.base.inherit(tdl.textures.ExternalTexture2D, tdl.textures.ExternalTexture);\n\n/**\n * Create and load a CubeMap.\n * @constructor\n * @param {string[]} urls The urls of the 6 faces, which\n *     must be in the order positive_x, negative_x positive_y,\n *     negative_y, positive_z, negative_z OR an array with a single url\n *     where the images are arranged as a cross in this order.\n *\n *         +--+--+--+--+\n *         |  |PY|  |  |\n *         +--+--+--+--+\n *         |NX|PZ|PX|NZ|\n *         +--+--+--+--+\n *         |  |NY|  |  |\n *         +--+--+--+--+\n */\ntdl.textures.CubeMap = function(urls) {\n  tdl.textures.init_(gl);\n  tdl.textures.Texture.call(this, gl.TEXTURE_CUBE_MAP);\n  // TODO(gman): make this global.\n  if (!tdl.textures.CubeMap.faceTargets) {\n    tdl.textures.CubeMap.faceTargets = [\n      gl.TEXTURE_CUBE_MAP_POSITIVE_X,\n      gl.TEXTURE_CUBE_MAP_NEGATIVE_X,\n      gl.TEXTURE_CUBE_MAP_POSITIVE_Y,\n      gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,\n      gl.TEXTURE_CUBE_MAP_POSITIVE_Z,\n      gl.TEXTURE_CUBE_MAP_NEGATIVE_Z];\n    tdl.textures.CubeMap.offsets = [\n      [2, 1],\n      [0, 1],\n      [1, 0],\n      [1, 2],\n      [1, 1],\n      [3, 1]];\n  }\n  var faceTargets = tdl.textures.CubeMap.faceTargets;\n  gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.texture);\n  this.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n  this.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n  this.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n  this.faces = [];\n  if (!urls.length) {\n    this.numUrls = 0;\n    this.size = urls;\n  } else {\n    this.numUrls = urls.length;\n    var that = this;\n    for (var ff = 0; ff < urls.length; ++ff) {\n      var face = { };\n      this.faces[ff] = face;\n      var img = document.createElement('img');\n      tdl.textures.addLoadingImage_(img);\n      face.img = img;\n      img.onload = function(faceIndex) {\n        return function() {\n          tdl.textures.removeLoadingImage_(img);\n          tdl.log(\"loaded image: \", urls[faceIndex]);\n          that.updateTexture(faceIndex);\n        }\n      } (ff);\n      img.onerror = function(url) {\n        return function() {\n          tdl.log(\"could not load image: \", url);\n        }\n      }(urls[ff]);\n      img.src = urls[ff];\n    }\n  }\n  this.uploadTextures();\n};\n\ntdl.base.inherit(tdl.textures.CubeMap, tdl.textures.Texture);\n\n/**\n * Check if all faces are loaded.\n * @return {boolean} true if all faces are loaded.\n */\ntdl.textures.CubeMap.prototype.loaded = function() {\n  for (var ff = 0; ff < this.faces.length; ++ff) {\n    if (!this.faces[ff].loaded) {\n      return false;\n    }\n  }\n  return true;\n};\n\ntdl.textures.clampToMaxSize = function(element, maxSize) {\n  if (element.width <= maxSize && element.height <= maxSize) {\n    return element;\n  }\n  var maxDimension = Math.max(element.width, element.height);\n  var newWidth = Math.floor(element.width * maxSize / maxDimension);\n  var newHeight = Math.floor(element.height * maxSize / maxDimension);\n\n  var canvas = document.createElement('canvas');\n  canvas.width = newWidth;\n  canvas.height = newHeight;\n  var ctx = canvas.getContext(\"2d\");\n  ctx.drawImage(\n      element,\n      0, 0, element.width, element.height,\n      0, 0, newWidth, newHeight);\n  return canvas;\n};\n\n/**\n * Uploads the images to the texture.\n */\ntdl.textures.CubeMap.prototype.uploadTextures = function() {\n  var allFacesLoaded = this.loaded();\n  var faceTargets = tdl.textures.CubeMap.faceTargets;\n  for (var faceIndex = 0; faceIndex < 6; ++faceIndex) {\n    var uploaded = false;\n    var target = faceTargets[faceIndex];\n    if (this.faces.length) {\n      var face = this.faces[Math.min(this.faces.length - 1, faceIndex)];\n      gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.texture);\n      if (allFacesLoaded) {\n        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);\n        if (this.faces.length == 6) {\n          gl.texImage2D(\n              target, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,\n              tdl.textures.clampToMaxSize(\n                  face.img, gl.tdl.textures.maxCubeMapSize));\n        } else {\n          var canvas = document.createElement('canvas');\n          var width = face.img.width / 4;\n          var height = face.img.height / 3;\n          canvas.width = width;\n          canvas.height = height;\n          var ctx = canvas.getContext(\"2d\");\n          var sx = tdl.textures.CubeMap.offsets[faceIndex][0] * width;\n          var sy = tdl.textures.CubeMap.offsets[faceIndex][1] * height;\n          ctx.drawImage(face.img, sx, sy, width, height, 0, 0, width, height);\n          gl.texImage2D(\n              target, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,\n              tdl.textures.clampToMaxSize(\n                  canvas, gl.tdl.textures.maxCubeMapSize));\n        }\n        uploaded = true;\n      }\n    }\n    if (!uploaded) {\n      var pixel = new Uint8Array([100,100,255,255]);\n      gl.texImage2D(target, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixel);\n    }\n  }\n  var genMips = false;\n  if (this.faces.length) {\n    var faceImg = this.faces[0].img;\n    if (this.faces.length == 6) {\n      genMips = tdl.textures.isPowerOf2(faceImg.width) &&\n                tdl.textures.isPowerOf2(faceImg.height);\n    } else {\n      genMips = tdl.textures.isPowerOf2(faceImg.width / 4) &&\n                tdl.textures.isPowerOf2(faceImg.height / 3);\n    }\n  }\n  if (genMips) {\n    gl.generateMipmap(gl.TEXTURE_CUBE_MAP);\n    this.setParameterIfNotSet_(gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);\n  } else {\n    this.setParameterIfNotSet_(gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n  }\n};\n\n/**\n * Update a just downloaded loaded texture.\n * @param {number} faceIndex index of face.\n */\ntdl.textures.CubeMap.prototype.updateTexture = function(faceIndex) {\n  // mark the face as loaded\n  var face = this.faces[faceIndex];\n  face.loaded = true;\n  // If all 6 faces are loaded then upload to GPU.\n  var loaded = this.loaded();\n  if (loaded) {\n    this.uploadTextures();\n  }\n};\n\n/**\n * Binds the CubeMap to a texture unit\n * @param {number} unit The texture unit.\n */\ntdl.textures.CubeMap.prototype.bindToUnit = function(unit) {\n  gl.activeTexture(gl.TEXTURE0 + unit);\n  gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.texture);\n};\n\nreturn tdl.textures;\n});\n\n"
  },
  {
    "path": "tdl/webgl.js",
    "content": "/*\n * Copyright 2009, Google Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *     * Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above\n * copyright notice, this list of conditions and the following disclaimer\n * in the documentation and/or other materials provided with the\n * distribution.\n *     * Neither the name of Google Inc. nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n/**\n * @fileoverview This file contains objects to deal with basic webgl stuff.\n */\ndefine(['./base-rs', './log', './misc'], function(BaseRS, Log, Misc) {\n\ntdl.provide('tdl.webgl');\n/**\n * A module for webgl.\n * @namespace\n */\ntdl.webgl = tdl.webgl || {};\n\n/**\n * The current GL context\n * @type {WebGLRenderingContext}\n */\ngl = null;\n\n/**\n * Sets a particle WebGLRenderingContext as the *current*\n * context.\n * @param {WebGLRenderingContext} context context to make\n *        current.\n */\ntdl.webgl.makeCurrent = function(context) {\n  gl = context;\n}\n\n/**\n * Creates the HTLM for a failure message\n * @private\n * @param {string} canvasContainerId id of container of th\n *        canvas.\n * @return {string} The html.\n */\ntdl.webgl.makeFailHTML = function(msg) {\n  return '' +\n    '<table style=\"background-color: #8CE; width: 100%; height: 100%;\"><tr>' +\n    '<td align=\"center\">' +\n    '<div style=\"display: table-cell; vertical-align: middle;\">' +\n    '<div style=\"\">' + msg + '</div>' +\n    '</div>' +\n    '</td></tr></table>';\n};\n\n/**\n * Mesasge for getting a webgl browser\n * @private\n * @type {string}\n */\ntdl.webgl.GET_A_WEBGL_BROWSER = '' +\n  'This page requires a browser that supports WebGL.<br/>' +\n  '<a href=\"http://get.webgl.org\">Click here to upgrade your browser.</a>';\n\n/**\n * Mesasge for need better hardware\n * @private\n * @type {string}\n */\ntdl.webgl.OTHER_PROBLEM = '' +\n  \"It does not appear your browser or computer supports WebGL.<br/>\" +\n  '<a href=\"http://get.webgl.org/troubleshooting/\">Click here for more information.</a>';\n\n/**\n * Creates a webgl context.\n * @param {HTMLCanvasElement} canvas The canvas element to\n *     create a context from.\n * @param {WebGLContextCreationAttirbutes?} opt_attribs Any\n *     creation attributes you want to pass in.\n * @param {function(msg)?} opt_onError An function to call\n *     if there is an error during creation.\n * @return {WebGLRenderingContext} The created context.\n */\ntdl.webgl.setupWebGL = function(canvas, opt_attribs, opt_onError) {\n  function handleCreationError(msg) {\n    var container = canvas.parentNode;\n    if (container) {\n      var str = window.WebGLRenderingContext ?\n           tdl.webgl.OTHER_PROBLEM :\n           tdl.webgl.GET_A_WEBGL_BROWSER;\n      if (msg) {\n        str += \"<br/><br/>Status: \" + msg;\n      }\n      container.innerHTML = tdl.webgl.makeFailHTML(str);\n    }\n  };\n\n  opt_onError = opt_onError || handleCreationError;\n\n  if (canvas.addEventListener) {\n    canvas.addEventListener(\"webglcontextcreationerror\", function(event) {\n          opt_onError(event.statusMessage);\n        }, false);\n  }\n  var context = tdl.webgl.create3DContext(canvas, opt_attribs);\n  if (context) {\n    if (canvas.addEventListener) {\n      canvas.addEventListener(\"webglcontextlost\", function(event) {\n        //tdl.log(\"call tdl.webgl.handleContextLost\");\n        event.preventDefault();\n        tdl.webgl.handleContextLost(canvas);\n      }, false);\n      canvas.addEventListener(\"webglcontextrestored\", function(event) {\n        //tdl.log(\"call tdl.webgl.handleContextRestored\");\n        tdl.webgl.handleContextRestored(canvas);\n      }, false);\n    }\n  } else {\n    opt_onError(\"\");\n  }\n  return context;\n};\n\n/**\n * Creates a webgl context.\n * @param {HTMLCanvasElement?} canvas The canvas tag to get\n *     context from. If one is not passed in one will be\n *     created.\n * @return {WebGLRenderingContext} The created context.\n */\ntdl.webgl.create3DContext = function(canvas, opt_attribs) {\n  if (opt_attribs === undefined) {\n    opt_attribs = {alpha:false};\n    tdl.misc.applyUrlSettings(opt_attribs, 'webgl');\n  }\n  var names = [\"webgl\", \"experimental-webgl\"];\n  var context = null;\n  for (var ii = 0; ii < names.length; ++ii) {\n    try {\n      context = canvas.getContext(names[ii], opt_attribs);\n    } catch(e) {}\n    if (context) {\n      break;\n    }\n  }\n  if (context) {\n    if (!tdl.webgl.glEnums) {\n      tdl.webgl.init(context);\n    }\n    tdl.webgl.makeCurrent(context);\n    tdl.webgl.setupCanvas_(canvas);\n    context.tdl = {};\n    context.tdl.depthTexture = tdl.webgl.getExtensionWithKnownPrefixes(\"WEBGL_depth_texture\");\n\n    // Disallow selection by default. This keeps the cursor from changing to an\n    // I-beam when the user clicks and drags.  It's easier on the eyes.\n    function returnFalse() {\n      return false;\n    }\n\n    canvas.onselectstart = returnFalse;\n    canvas.onmousedown = returnFalse;\n  }\n  return context;\n};\n\ntdl.webgl.setupCanvas_ = function(canvas) {\n  if (!canvas.tdl) {\n    canvas.tdl = {};\n  }\n};\n\n/**\n * Browser prefixes for extensions.\n * @private\n * @type {Array.<string>}\n */\ntdl.webgl.browserPrefixes_ = [\n  \"\",\n  \"MOZ_\",\n  \"OP_\",\n  \"WEBKIT_\"\n];\n\n/**\n * Given an extension name like WEBGL_compressed_texture_s3tc\n * returns the supported version extension, like\n * WEBKIT_WEBGL_compressed_teture_s3tc\n * @param {string} name Name of extension to look for\n * @return {WebGLExtension} The extension or undefined if not\n *     found.\n */\ntdl.webgl.getExtensionWithKnownPrefixes = function(name) {\n  for (var ii = 0; ii < tdl.webgl.browserPrefixes_.length; ++ii) {\n    var prefixedName = tdl.webgl.browserPrefixes_[ii] + name;\n    var ext = gl.getExtension(prefixedName);\n    if (ext) {\n      return ext;\n    }\n  }\n};\n\ntdl.webgl.runHandlers_ = function(handlers) {\n  //tdl.log(\"run handlers: \" + handlers.length);\n  var handlersCopy = handlers.slice();\n  for (var ii = 0; ii < handlersCopy.length; ++ii) {\n    //tdl.log(\"run: \" + ii);\n    handlersCopy[ii]();\n  }\n};\n\ntdl.webgl.registerContextLostHandler = function(\n    canvas, handler, opt_sysHandler) { \n  tdl.webgl.setupCanvas_(canvas);\n  if (!canvas.tdl.contextLostHandlers) {\n    canvas.tdl.contextLostHandlers = [[],[]];\n  }\n  var a = canvas.tdl.contextLostHandlers[opt_sysHandler ? 0 : 1];\n  a.push(handler);\n};\n\ntdl.webgl.registerContextRestoredHandler = function(\n    canvas, handler, opt_sysHandler) {\n  tdl.webgl.setupCanvas_(canvas);\n  if (!canvas.tdl.contextRestoredHandlers) {\n    canvas.tdl.contextRestoredHandlers = [[],[]];\n  }\n  var a = canvas.tdl.contextRestoredHandlers[opt_sysHandler ? 0 : 1];\n  a.push(handler);\n};\n\ntdl.webgl.handleContextLost = function(canvas) {\n  // first run tdl's handlers then the user's\n  //tdl.log(\"tdl.webgl.handleContextLost\");\n  if (canvas.tdl.contextLostHandlers) {\n    tdl.webgl.runHandlers_(canvas.tdl.contextLostHandlers[0]);\n    tdl.webgl.runHandlers_(canvas.tdl.contextLostHandlers[1]);\n  }\n};\n\ntdl.webgl.handleContextRestored = function(canvas) {\n  // first run tdl's handlers then the user's\n  //tdl.log(\"tdl.webgl.handleContextRestored\");\n  if (canvas.tdl.contextRestoredHandlers) {\n    tdl.webgl.runHandlers_(canvas.tdl.contextRestoredHandlers[0]);\n    tdl.webgl.runHandlers_(canvas.tdl.contextRestoredHandlers[1]);\n  }\n};\n\n/**\n * Which arguements are enums.\n * @private\n * @type {Object.<number, string>}\n */\ntdl.webgl.glValidEnumContexts = {\n\n  // Generic setters and getters\n\n  'enable': { 0:true },\n  'disable': { 0:true },\n  'getParameter': { 0:true },\n\n  // Rendering\n\n  'drawArrays': { 0:true },\n  'drawElements': { 0:true, 2:true },\n\n  // Shaders\n\n  'createShader': { 0:true },\n  'getShaderParameter': { 1:true },\n  'getProgramParameter': { 1:true },\n\n  // Vertex attributes\n\n  'getVertexAttrib': { 1:true },\n  'vertexAttribPointer': { 2:true },\n\n  // Textures\n\n  'bindTexture': { 0:true },\n  'activeTexture': { 0:true },\n  'getTexParameter': { 0:true, 1:true },\n  'texParameterf': { 0:true, 1:true },\n  'texParameteri': { 0:true, 1:true, 2:true },\n  'texImage2D': { 0:true, 2:true, 6:true, 7:true },\n  'texSubImage2D': { 0:true, 6:true, 7:true },\n  'copyTexImage2D': { 0:true, 2:true },\n  'copyTexSubImage2D': { 0:true },\n  'generateMipmap': { 0:true },\n\n  // Buffer objects\n\n  'bindBuffer': { 0:true },\n  'bufferData': { 0:true, 2:true },\n  'bufferSubData': { 0:true },\n  'getBufferParameter': { 0:true, 1:true },\n\n  // Renderbuffers and framebuffers\n\n  'pixelStorei': { 0:true, 1:true },\n  'readPixels': { 4:true, 5:true },\n  'bindRenderbuffer': { 0:true },\n  'bindFramebuffer': { 0:true },\n  'checkFramebufferStatus': { 0:true },\n  'framebufferRenderbuffer': { 0:true, 1:true, 2:true },\n  'framebufferTexture2D': { 0:true, 1:true, 2:true },\n  'getFramebufferAttachmentParameter': { 0:true, 1:true, 2:true },\n  'getRenderbufferParameter': { 0:true, 1:true },\n  'renderbufferStorage': { 0:true, 1:true },\n\n  // Frame buffer operations (clear, blend, depth test, stencil)\n\n  'clear': { 0:true },\n  'depthFunc': { 0:true },\n  'blendFunc': { 0:true, 1:true },\n  'blendFuncSeparate': { 0:true, 1:true, 2:true, 3:true },\n  'blendEquation': { 0:true },\n  'blendEquationSeparate': { 0:true, 1:true },\n  'stencilFunc': { 0:true },\n  'stencilFuncSeparate': { 0:true, 1:true },\n  'stencilMaskSeparate': { 0:true },\n  'stencilOp': { 0:true, 1:true, 2:true },\n  'stencilOpSeparate': { 0:true, 1:true, 2:true, 3:true },\n\n  // Culling\n\n  'cullFace': { 0:true },\n  'frontFace': { 0:true }\n};\n\n/**\n * Map of numbers to names.\n * @private\n * @type {Object}\n */\ntdl.webgl.glEnums = null;\n\n/**\n * Initializes this module. Safe to call more than once.\n * @private\n * @param {WebGLRenderingContext} ctx A WebGL context. If\n *    you have more than one context it doesn't matter which one\n *    you pass in, it is only used to pull out constants.\n */\ntdl.webgl.init = function(ctx) {\n  if (tdl.webgl.glEnums == null) {\n    tdl.webgl.glEnums = { };\n    for (var propertyName in ctx) {\n      if (typeof ctx[propertyName] == 'number') {\n        tdl.webgl.glEnums[ctx[propertyName]] = propertyName;\n      }\n    }\n  }\n};\n\n/**\n * Checks the utils have been initialized.\n * @private\n */\ntdl.webgl.checkInit = function() {\n  if (tdl.webgl.glEnums == null) {\n    throw 'tdl.webgl.init(ctx) not called';\n  }\n};\n\n/**\n * Returns true or false if value matches any WebGL enum\n * @private\n * @param {*} value Value to check if it might be an enum.\n * @return {boolean} True if value matches one of the WebGL defined enums\n */\ntdl.webgl.mightBeEnum = function(value) {\n  tdl.webgl.checkInit();\n  return (tdl.webgl.glEnums[value] !== undefined);\n}\n\n/**\n * Gets an string version of an WebGL enum.\n *\n * Example:\n *   var str = WebGLDebugUtil.glEnumToString(ctx.getError());\n *\n * @param {number} value Value to return an enum for\n * @return {string} The string version of the enum.\n */\ntdl.webgl.glEnumToString = function(value) {\n  tdl.webgl.checkInit();\n  if (value === undefined) {\n    return \"undefined\";\n  }\n  var name = tdl.webgl.glEnums[value];\n  return (name !== undefined) ? name :\n      (\"*UNKNOWN WebGL ENUM (0x\" + value.toString(16) + \")\");\n};\n\n/**\n * Returns the string version of a WebGL argument.\n * Attempts to convert enum arguments to strings.\n * @private\n * @param {string} functionName the name of the WebGL function.\n * @param {number} argumentIndx the index of the argument.\n * @param {*} value The value of the argument.\n * @return {string} The value as a string.\n */\ntdl.webgl.glFunctionArgToString = function(functionName, argumentIndex, value) {\n  var funcInfo = tdl.webgl.glValidEnumContexts[functionName];\n  if (funcInfo !== undefined) {\n    if (funcInfo[argumentIndex]) {\n      return tdl.webgl.glEnumToString(value);\n    }\n  }\n  if (value === null) {\n    return \"null\";\n  } else if (value === undefined) {\n    return \"undefined\";\n  } else {\n    return value.toString();\n  }\n};\n\n/**\n * Converts the arguments of a WebGL function to a string.\n * Attempts to convert enum arguments to strings.\n *\n * @param {string} functionName the name of the WebGL function.\n * @param {number} args The arguments.\n * @return {string} The arguments as a string.\n */\ntdl.webgl.glFunctionArgsToString = function(functionName, args) {\n  // apparently we can't do args.join(\",\");\n  var argStr = \"\";\n  for (var ii = 0; ii < args.length; ++ii) {\n    argStr += ((ii == 0) ? '' : ', ') +\n        tdl.webgl.glFunctionArgToString(functionName, ii, args[ii]);\n  }\n  return argStr;\n};\n\n/**\n * Given a WebGL context returns a wrapped context that calls\n * gl.getError after every command and calls a function if the\n * result is not gl.NO_ERROR.\n *\n * @param {WebGLRenderingContext} ctx The webgl context to\n *        wrap.\n * @param {function(err, funcName, args): void} opt_onErrorFunc\n *        The function to call when gl.getError returns an\n *        error. If not specified the default function calls\n *        console.log with a message.\n * @param {function(funcName, args): void} opt_onFunc The\n *        function to call when each webgl function is called.\n *        You can use this to log all calls for example.\n */\ntdl.webgl.makeDebugContext = function(ctx, opt_onErrorFunc, opt_onFunc) {\n  tdl.webgl.init(ctx);\n  opt_onErrorFunc = opt_onErrorFunc || function(err, functionName, args) {\n        tdl.error(\n          \"WebGL error \"+ tdl.webgl.glEnumToString(err) + \" in \" +\n          functionName + \"(\" + tdl.webgl.glFunctionArgsToString(\n              functionName, args) + \")\");\n      };\n\n  // Holds booleans for each GL error so after we get the error ourselves\n  // we can still return it to the client app.\n  var glErrorShadow = { };\n\n  // Makes a function that calls a WebGL function and then calls getError.\n  function makeErrorWrapper(ctx, functionName) {\n    return function() {\n      if (opt_onFunc) {\n        opt_onFunc(functionName, arguments);\n      }\n      try {\n        var result = ctx[functionName].apply(ctx, arguments);\n      } catch (e) {\n        opt_onErrorFunc(ctx.NO_ERROR, functionName, arguments);\n        throw(e);\n      }\n      var err = ctx.getError();\n      if (err != 0) {\n        glErrorShadow[err] = true;\n        opt_onErrorFunc(err, functionName, arguments);\n      }\n      return result;\n    };\n  }\n\n  function makePropertyWrapper(wrapper, original, propertyName) {\n    wrapper.__defineGetter__(propertyName, function() {\n      return original[propertyName];\n    });\n    // TODO(gmane): this needs to handle properties that take more than\n    // one value?\n    wrapper.__defineSetter__(propertyName, function(value) {\n      original[propertyName] = value;\n    });\n  }\n\n  // Make a an object that has a copy of every property of the WebGL context\n  // but wraps all functions.\n  var wrapper = {};\n  for (var propertyName in ctx) {\n    if (typeof ctx[propertyName] == 'function') {\n       wrapper[propertyName] = makeErrorWrapper(ctx, propertyName);\n     } else {\n       makePropertyWrapper(wrapper, ctx, propertyName);\n     }\n  }\n\n  // Override the getError function with one that returns our saved results.\n  wrapper.getError = function() {\n    for (var err in glErrorShadow) {\n      if (glErrorShadow[err]) {\n        glErrorShadow[err] = false;\n        return err;\n      }\n    }\n    return ctx.NO_ERROR;\n  };\n\n  return wrapper;\n};\n\n/**\n * Provides requestAnimationFrame in a cross browser way.\n * @param {function(RequestAnimationEvent): void} callback. Callback that will\n *        be called when a frame is ready.\n * @param {Element} element Element to request an animation frame for.\n * @return {number} request id.\n */\ntdl.webgl.requestAnimationFrame = function(callback, element) {\n  if (!tdl.webgl.requestAnimationFrameImpl_) {\n    tdl.webgl.requestAnimationFrameImpl_ = function() {\n      var functionNames = [\n        \"requestAnimationFrame\",\n        \"webkitRequestAnimationFrame\",\n        \"mozRequestAnimationFrame\",\n        \"oRequestAnimationFrame\",\n        \"msRequestAnimationFrame\"\n      ];\n      for (var jj = 0; jj < functionNames.length; ++jj) {\n        var functionName = functionNames[jj];\n        if (window[functionName]) {\n          return function(name) {\n            return function(callback, element) {\n              return window[name].call(window, callback, element);\n            };\n          }(functionName);\n        }\n      }\n      return function(callback, element) {\n           return window.setTimeout(callback, 1000 / 70);\n        };\n    }();\n  }\n\n  return tdl.webgl.requestAnimationFrameImpl_(callback, element);\n};\n\n\n/**\n * Provides cancelRequestAnimationFrame in a cross browser way.\n * @param {number} requestId.\n */\ntdl.webgl.cancelRequestAnimationFrame = function(requestId) {\n  if (!tdl.webgl.cancelRequestAnimationFrameImpl_) {\n    tdl.webgl.cancelRequestAnimationFrameImpl_ = function() {\n      var functionNames = [\n        \"cancelRequestAnimationFrame\",\n        \"webkitCancelRequestAnimationFrame\",\n        \"mozCancelRequestAnimationFrame\",\n        \"oCancelRequestAnimationFrame\",\n        \"msCancelRequestAnimationFrame\"\n      ];\n      for (var jj = 0; jj < functionNames.length; ++jj) {\n        var functionName = functionNames[jj];\n        if (window[functionName]) {\n          return function(name) {\n            return function(requestId) {\n              window[name].call(window, requestId);\n            };\n          }(functionName);\n        }\n      }\n      return function(requestId) {\n           window.clearTimeout(requestId);\n        };\n    }();\n  }\n\n  tdl.webgl.cancelRequestAnimationFrameImpl_(requestId);\n};\n\nreturn tdl.webgl;\n});\n"
  }
]