├── tdl ├── error.jpg ├── screenshot.js ├── log.js ├── fullscreen.js ├── fps.js ├── clock.js ├── string.js ├── sync.js ├── misc.js ├── loader.js ├── buffers.js ├── shader.js ├── base-rs.js ├── models.js ├── framebuffers.js ├── io.js ├── quaternions.js ├── programs.js └── base.js ├── example ├── assets │ └── sometexture.png ├── example-requirejs.html ├── example-requirejs.js ├── example2.html ├── line.html └── example.html ├── .gitignore ├── package.json ├── bower.json ├── jsdoc.conf.json ├── Gruntfile.js ├── README.md ├── docs.md └── js └── require.js /tdl/error.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/tdl/HEAD/tdl/error.jpg -------------------------------------------------------------------------------- /example/assets/sometexture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/tdl/HEAD/example/assets/sometexture.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | docs 2 | *.Makefile 3 | *.mk 4 | *.ncb 5 | *.ninja 6 | *.props 7 | *.pyc 8 | *.rules 9 | *.scons 10 | *.sdf 11 | *.sln 12 | *.suo 13 | *.targets 14 | *.user 15 | *.vcproj 16 | *.vcxproj 17 | *.vcxproj.filters 18 | *.vpj 19 | *.vpw 20 | *.vpwhistu 21 | *.vtg 22 | *.xcodeproj 23 | *~ 24 | .*.sw? 25 | .DS_Store 26 | .cproject 27 | .gdb_history 28 | .gdbinit 29 | .metadata 30 | .project 31 | tags 32 | Thumbs.db 33 | v8.log 34 | node_modules 35 | 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tdl", 3 | "version": "0.0.8", 4 | "description": "Some WebGL Library", 5 | "main": "tdl/base.js", 6 | "directories": { 7 | "doc": "docs", 8 | "example": "example" 9 | }, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/greggman/tdl.git" 16 | }, 17 | "keywords": [ 18 | "WebGL" 19 | ], 20 | "author": "Greggman", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/greggman/tdl/issues" 24 | }, 25 | "homepage": "https://github.com/greggman/tdl", 26 | "devDependencies": { 27 | "grunt": "^0.4.5", 28 | "grunt-contrib-clean": "^0.6.0", 29 | "grunt-contrib-uglify": "^0.7.0", 30 | "grunt-jsdoc": "^0.5.7" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tdl", 3 | "version": "0.0.8", 4 | "authors": [ 5 | { 6 | "name": "Gregg Tavares", 7 | "email": "github@greggman.com", 8 | "homepage": "http://games.greggman.com" 9 | } 10 | ], 11 | "description": "A JavaScript library for WebGL", 12 | "main": "tdl/base.js", 13 | "moduleType": [ 14 | "amd" 15 | ], 16 | "keywords": [ 17 | "webgl", 18 | "tdl" 19 | ], 20 | "license": "MIT", 21 | "homepage": "https://github.com/greggman/tdl", 22 | "repository": "git://github.com/greggman/tdl.git", 23 | "ignore": [ 24 | "**/.*", 25 | "*.md", 26 | "Gruntfile.js", 27 | "package.json", 28 | "bower.json", 29 | "node_modules", 30 | "docs", 31 | "build", 32 | "example", 33 | "js", 34 | "bower_components", 35 | "test", 36 | "tests" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /jsdoc.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags" : { 3 | "allowUnknownTags" : false 4 | }, 5 | "plugins" : ["plugins/markdown"], 6 | "templates" : { 7 | "cleverLinks" : false, 8 | "monospaceLinks" : false, 9 | "dateFormat" : "ddd MMM Do YYYY", 10 | "outputSourceFiles" : false, 11 | "outputSourcePath" : false, 12 | "systemName" : "ThreeDLibrary", 13 | "footer" : "", 14 | "copyright" : "copyright Google, Greggman", 15 | "navType" : "vertical", 16 | "theme" : "cerulean", 17 | "linenums" : true, 18 | "collapseSymbols" : false, 19 | "inverseNav" : true, 20 | "highlightTutorialCode" : true 21 | }, 22 | "markdown" : { 23 | "parser" : "gfm", 24 | "hardwrap" : true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = function(grunt) { 4 | 5 | grunt.initConfig({ 6 | jsdoc: { 7 | tdl: { 8 | src: ['tdl/*.js'], 9 | options: { 10 | destination: 'docs/gen', 11 | configure: 'jsdoc.conf.json', 12 | template: 'node_modules/ink-docstrap/template', 13 | private: false, 14 | }, 15 | }, 16 | }, 17 | clean: [ 18 | 'docs/gen', 19 | ], 20 | uglify: { 21 | my_target: { 22 | files: { 23 | 'build/tdl.min.js': [ 24 | 'tdl/base.js', 25 | 'tdl/buffers.js', 26 | 'tdl/clock.js', 27 | 'tdl/fast.js', 28 | 'tdl/fps.js', 29 | 'tdl/framebuffers.js', 30 | 'tdl/fullscreen.js', 31 | 'tdl/io.js', 32 | 'tdl/loader.js', 33 | 'tdl/log.js', 34 | 'tdl/math.js', 35 | 'tdl/misc.js', 36 | 'tdl/models.js', 37 | 'tdl/particles.js', 38 | 'tdl/primitives.js', 39 | 'tdl/programs.js', 40 | 'tdl/quaternions.js', 41 | 'tdl/screenshot.js', 42 | 'tdl/shader.js', 43 | 'tdl/string.js', 44 | 'tdl/sync.js', 45 | 'tdl/textures.js', 46 | 'tdl/webgl.js', 47 | ], 48 | }, 49 | }, 50 | }, 51 | }); 52 | 53 | grunt.loadNpmTasks('grunt-contrib-clean'); 54 | grunt.loadNpmTasks('grunt-jsdoc'); 55 | grunt.loadNpmTasks('grunt-contrib-uglify'); 56 | 57 | grunt.registerTask('default', ['clean', 'jsdoc', 'uglify']); 58 | }; 59 | 60 | -------------------------------------------------------------------------------- /tdl/screenshot.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains various functions for taking a screenshot 35 | */ 36 | define(['./base-rs', './io'], function(BaseRS, IO) { 37 | 38 | tdl.provide('tdl.screenshot'); 39 | 40 | /** 41 | * @namespace 42 | */ 43 | tdl.screenshot = tdl.screenshot || {}; 44 | 45 | /** 46 | * takes a screenshot of a canvas. Sends it to the server to be saved. 47 | */ 48 | tdl.screenshot.takeScreenshot = function(canvas) { 49 | tdl.io.sendJSON( 50 | this.url, 51 | {cmd: 'screenshot', dataURL: canvas.toDataURL()}, 52 | function() {}); 53 | }; 54 | 55 | return tdl.screenshot; 56 | }); 57 | 58 | 59 | -------------------------------------------------------------------------------- /tdl/log.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains objects to deal with logging. 35 | */ 36 | define(['./base-rs', './string'], function(BaseRS, Strings) { 37 | 38 | tdl.provide('tdl.log'); 39 | /** 40 | * A module for log. 41 | * @namespace 42 | */ 43 | 44 | /** 45 | * Wrapped logging function. 46 | * @param {*} msg The message to log. 47 | */ 48 | tdl.log = function() { 49 | var str = tdl.string.argsToString(arguments); 50 | if (window.console && window.console.log) { 51 | window.console.log(str); 52 | } else if (window.dump) { 53 | window.dump(str + "\n"); 54 | } 55 | }; 56 | 57 | /** 58 | * Wrapped logging function. 59 | * @param {*} msg The message to log. 60 | */ 61 | tdl.error = function() { 62 | var str = tdl.string.argsToString(arguments); 63 | if (window.console) { 64 | if (window.console.error) { 65 | window.console.error(str); 66 | } else if (window.console.log) { 67 | window.console.log(str); 68 | } 69 | } else if (window.dump) { 70 | window.dump(str + "\n"); 71 | } 72 | }; 73 | 74 | /** 75 | * Dumps an object to the console. 76 | * 77 | * @param {!Object} obj Object to dump. 78 | * @param {string} opt_prefix string to prefix each value with. 79 | */ 80 | tdl.dumpObj = function(obj, opt_prefix) { 81 | tdl.log(tdl.string.objToString(obj, opt_prefix)); 82 | }; 83 | 84 | return tdl; 85 | }); 86 | -------------------------------------------------------------------------------- /tdl/fullscreen.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains misc functions to deal with 35 | * fullscreen. 36 | */ 37 | define(['./base-rs'], function(BaseRS) { 38 | 39 | /** 40 | * A module for misc. 41 | * @namespace 42 | */ 43 | tdl.provide('tdl.fullscreen'); 44 | tdl.fullscreen = tdl.fullscreen || {}; 45 | 46 | tdl.fullscreen.requestFullScreen = function(element) { 47 | if (element.requestFullscreen) { 48 | element.requestFullscreen(); 49 | } else if (element.msRequestFullscreen) { 50 | element.msRequestFullscreen(); 51 | } else if (element.webkitRequestFullScreen) { 52 | element.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT); 53 | } else if (element.mozRequestFullScreen) { 54 | element.mozRequestFullScreen(); 55 | } 56 | }; 57 | 58 | tdl.fullscreen.cancelFullScreen = function(element) { 59 | if (document.exitFullscreen) { 60 | document.exitFullscreen(); 61 | } else if (document.msExitFullscreen) { 62 | document.msExitFullscreen(); 63 | } else if (document.webkitCancelFullScreen) { 64 | document.webkitCancelFullScreen(); 65 | } else if (document.mozCancelFullScreen) { 66 | document.mozCancelFullScreen(); 67 | } 68 | }; 69 | 70 | tdl.fullscreen.onFullScreenChange = function(element, callback) { 71 | var isFullScreen = function() { 72 | return document.fullscreenElement || document.mozFullScreenElement || 73 | document.webkitFullscreenElement || document.msFullscreenElement || 74 | document.mozFullScreen || document.webkitIsFullScreen; 75 | }; 76 | document.addEventListener('fullscreenchange', function(event) { 77 | callback(isFullScreen()); 78 | }); 79 | element.addEventListener('webkitfullscreenchange', function(event) { 80 | callback(isFullScreen()); 81 | }); 82 | document.addEventListener('mozfullscreenchange', function(event) { 83 | callback(isFullScreen()); 84 | }); 85 | }; 86 | 87 | return tdl.fullscreen; 88 | }); 89 | -------------------------------------------------------------------------------- /tdl/fps.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains objects to measure frames 35 | * per second. 36 | */ 37 | define(['./base-rs'], function(BaseRS) { 38 | 39 | tdl.provide('tdl.fps'); 40 | /** 41 | * A module for fps. 42 | * @namespace 43 | */ 44 | tdl.fps = tdl.fps || {}; 45 | 46 | /** 47 | * Number of frames to average over for computing FPS. 48 | * @type {number} 49 | */ 50 | tdl.fps.NUM_FRAMES_TO_AVERAGE = 16; 51 | 52 | /** 53 | * Measures frames per second. 54 | * @constructor 55 | */ 56 | tdl.fps.FPSTimer = function() { 57 | // total time spent for last N frames. 58 | this.totalTime_ = tdl.fps.NUM_FRAMES_TO_AVERAGE; 59 | 60 | // elapsed time for last N frames. 61 | this.timeTable_ = []; 62 | 63 | // where to record next elapsed time. 64 | this.timeTableCursor_ = 0; 65 | 66 | // Initialize the FPS elapsed time history table. 67 | for (var tt = 0; tt < tdl.fps.NUM_FRAMES_TO_AVERAGE; ++tt) { 68 | this.timeTable_[tt] = 1.0; 69 | } 70 | 71 | /** 72 | * The instantaneous FPS 73 | * @type {number} 74 | */ 75 | this.instantaneousFPS = 0; 76 | /** 77 | * The average FPS 78 | * @type {number} 79 | */ 80 | this.averageFPS = 0; 81 | }; 82 | 83 | /** 84 | * Updates the fps measurement. You must call this in your 85 | * render loop. 86 | * 87 | * @param {number} elapsedTime The elasped time in seconds 88 | * since the last frame. 89 | */ 90 | tdl.fps.FPSTimer.prototype.update = function(elapsedTime) { 91 | // Keep the total time and total active time for the last N frames. 92 | this.totalTime_ += elapsedTime - this.timeTable_[this.timeTableCursor_]; 93 | 94 | // Save off the elapsed time for this frame so we can subtract it later. 95 | this.timeTable_[this.timeTableCursor_] = elapsedTime; 96 | 97 | // Wrap the place to store the next time sample. 98 | ++this.timeTableCursor_; 99 | if (this.timeTableCursor_ == tdl.fps.NUM_FRAMES_TO_AVERAGE) { 100 | this.timeTableCursor_ = 0; 101 | } 102 | 103 | this.instantaneousFPS = Math.floor(1.0 / elapsedTime + 0.5); 104 | this.averageFPS = Math.floor( 105 | (1.0 / (this.totalTime_ / tdl.fps.NUM_FRAMES_TO_AVERAGE)) + 0.5); 106 | }; 107 | 108 | return tdl.fps; 109 | }); 110 | 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TDL 2 | === 3 | 4 | Please check out [TWGL](http://twgljs.org). It's arguably the spiritual successor to TDL. 5 | 6 | TDL is a **low-level** library for WebGL apps. It currently focuses on speed of rendering rather than ease of use. 7 | 8 | Some [terse docs can be found at here](docs.md) 9 | 10 | Note: By **low-level** I mean TDL doesn't currently provide any 3D knowledge. 11 | There are almost no built in shaders. There is no scene graph. There are just some objects for wrapping WebGL 12 | shaders and helping to easily associate vertex data with attributes and update uniforms. 13 | 14 | Example: Assuming a shaders like this. 15 | 16 | 30 | 31 | 39 | 40 | In WebGL you'd do this 41 | 42 | // At init time: 43 | var program = UtilToCompileShaders("vshader", "fshader"); 44 | var positionLoc = gl.getAttribLocation(program, "position"); 45 | var texcoordLoc = gl.getAttribLocation(program, "texcoord"); 46 | var worldMatLoc = gl.getUniformLocation(program, "u_worldMatrix"); 47 | var projectionMatLoc = gl.getUniformLocation(program, "u_projectionMatrix"); 48 | var textureLoc = gl.getUniformLocation(program, "u_texture"); 49 | 50 | var positions = gl.createBuffer(); 51 | gl.bindBuffer(gl.ARRAY_BUFFER, positions); 52 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positionData), gl.STATIC_DRAW); 53 | 54 | var tecoords = gl.createBuffer(); 55 | gl.bindBuffer(gl.ARRAY_BUFFER, texcoords); 56 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoordData), gl.STATIC_DRAW); 57 | 58 | var texture = gl.createTexture(); 59 | gl.bindTexture(gl.TEXTURE_2D, texture); 60 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, someImage); 61 | 62 | 63 | // At draw time 64 | gl.bindBuffer(gl.ARRAY_BUFFER, positions); 65 | gl.enableVertexAttribArray(programLoc); 66 | gl.vertexAttribPointer(programLoc, 3, gl.FLOAT, false, 0, 0); 67 | 68 | gl.bindBuffer(gl.ARRAY_BUFFER, positions); 69 | gl.enableVertexAttribArray(texcoordLoc); 70 | gl.vertexAttribPointer(tecoordLoc, 2, gl.FLOAT, false, 0, 0); 71 | 72 | gl.useProgram(program); 73 | gl.uniformMatrix4f(projectionMatLoc, false, projectionMatrix); 74 | 75 | for (var i = 0; i < 3; ++i) 76 | { 77 | gl.uniformMatrix4f(worldMatLoc, false, computeWorldMatrix(i)); 78 | gl.drawArrays(gl.TRIANGLES, 0, num); 79 | } 80 | 81 | In TDL that would be shortened to 82 | 83 | // At init time. 84 | var program = tdl.programs.loadProgramFromScriptTags("vshader", "fshader"); 85 | var arrays = { 86 | position: new tdl.primitives.AttribBuffer(3, positionData), 87 | texcoord: new tdl.primitives.AttribBuffer(2, texcoordData), 88 | }; 89 | var textures = { 90 | u_texture: new tdl.textures.loadTexture(someImage), 91 | } 92 | var model = new tdl.models.Model(program, arrays, textures); 93 | 94 | 95 | // At Draw time 96 | var sharedUniforms = { 97 | u_projectionMatrix: projectionMatrix, 98 | }; 99 | var perObjectUniforms = { 100 | u_worldMatrix: worldMatrix, 101 | }; 102 | 103 | model.drawPrep(sharedUniforms); 104 | 105 | for (var i = 0; i < 3; ++i) 106 | { 107 | perObjectUnifirms.u_worldMatrix = computeWorldMatrix(i); 108 | model.draw(perObjectuniforms); 109 | } 110 | 111 | -------------------------------------------------------------------------------- /docs.md: -------------------------------------------------------------------------------- 1 | TDL Docs 2 | ======== 3 | I hope the code is pretty straight forward. There's some simple examples here 4 | 5 | http://greggman.github.com/tdl/example/example.html 6 | 7 | http://greggman.github.com/tdl/example/example2.html 8 | 9 | http://greggman.github.com/tdl/example/picking.html 10 | 11 | More complex samples can be found at http://webglsamples.googlecode.com 12 | 13 | Briefly... 14 | 15 | Your startup code should look like this 16 | 17 | canvas = document.getElementById("canvas"); 18 | gl = tdl.webgl.setupWebGL(canvas); 19 | if (!gl) { 20 | return; // Do nothing 21 | } 22 | 23 | Where "canvas" is the id of the canvas you want to draw into. 24 | tdl.webgl.setupWebGL will replace the contents of the containing div 25 | with a link to getting a WebGL capable browser if the user's browser 26 | does not support WebGL. 27 | 28 | Otherwise... 29 | 30 | Loading Shaders 31 | --------------- 32 | 33 | var program = tdl.programs.loadProgram(vertexShaderSource, fragmentShaderSource); 34 | 35 | Compiles your shaders and creates a Program object. 36 | 37 | Loading Textures 38 | ---------------- 39 | 40 | var textures = { 41 | name1: tdl.textures.loadTexture(url), 42 | name2: tdl.textures.loadTexture(url) 43 | }; 44 | 45 | Loads your textures. The property names must match whatever you called the samplers 46 | in your shaders. loadTexture can take `[url]` for an image, `[r,g,b,a]` for solid 47 | texture. `[url,url,url,url,url,url]` for a cubemap and also `[url]` for a cubemap 48 | where all 6 faces are in a cross. It can also take an img or canvas tag. 49 | 50 | Create Vertices or a Mesh 51 | ------------------------- 52 | 53 | var arrays = tdl.primitives.createSphere(1, 10, 10); 54 | 55 | Creates vertices 56 | 57 | The tdl.primitives functions return an object like this 58 | 59 | { 60 | position: AttribBuffer, 61 | normal: AttribBuffer, 62 | texCoord: AttribBuffer 63 | }; 64 | 65 | The property names must match the attributes in your vertex shader if you want to 66 | add more. 67 | 68 | A call to tdl.primitives.addTangentsAndBinormals adds the fields "tangent" and 69 | "binormal" 70 | 71 | Create a Model 72 | -------------- 73 | 74 | Once you have a program, a texture object and an arrays object you make a new 75 | model with 76 | 77 | var model = new tdl.models.Model(program, array, textures); 78 | 79 | 80 | Rendering 81 | --------- 82 | 83 | To draw the model there are 2 functions, `model.drawPrep(uniformMap)` and 84 | `model.draw(uniformMap)`. 85 | 86 | Both of them take an object with uniformName/value pairs. 87 | 88 | model.drawPrep binds the program, binds all the textures and attributes and 89 | sets whatever uniforms you pass in. 90 | 91 | model.draw sets any more uniforms you pass in and then calls gl.drawElements. 92 | The idea is you call `model.drawPrep` once and then `model.draw` to draw a 93 | bunch of the same model, changing as few uniforms as possible. This is the 94 | fastest way to use WebGL. 95 | 96 | Your rendering loop should look something like this 97 | 98 | function render() { 99 | var time = tdl.webgl.animationTime(); 100 | model.drawPrep({...}); 101 | model.draw({...}); 102 | tdl.webgl.requestAnimationFrame(render, canvas); 103 | } 104 | render(); // call the first render manually to start it off. 105 | 106 | 107 | Math 108 | ---- 109 | 110 | The math is a little funky. There are 2 math libraries, math.js and fast.js. 111 | math.js comes from O3D and uses `JavaScript` arrays. A Matrix in that 112 | library is a an array of numbers. fast.js uses `Float32Array` for its storage 113 | and most functions take a destination object as the first argument. 114 | Theoretically this is faster because you can avoid a certain number of 115 | allocations. It also means the numbers in the array do not have to be queried 116 | and converted from `JavaScript` Number to floats before calling glUniform. 117 | -------------------------------------------------------------------------------- /tdl/clock.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains various functions for managing a clock 35 | */ 36 | define(['./base-rs', './io', './log'], function(BaseRS, IO, Log) { 37 | 38 | tdl.provide('tdl.clock'); 39 | /** 40 | * Clock related stuff 41 | * @namespace 42 | */ 43 | tdl.clock = tdl.clock || {}; 44 | 45 | /** 46 | * Creates a clock. Optionally synced to a server 47 | * @param {number} opt_syncRate. If passed, this is the number of seconds 48 | * between syncing to the server. If not passed the local clock is used. 49 | * Note: If the client is faster than the server this means it's possible 50 | * the clock will report a certain time and then later a previous time. 51 | */ 52 | tdl.clock.createClock = function(opt_syncRate, opt_url) { 53 | if (opt_syncRate) { 54 | return new tdl.clock.SyncedClock(opt_syncRate, opt_url); 55 | } else { 56 | return new tdl.clock.LocalClock(); 57 | } 58 | }; 59 | 60 | /** 61 | * A clock that gets the local current time in seconds. 62 | * @constructor 63 | * @private 64 | */ 65 | tdl.clock.LocalClock = function() { 66 | } 67 | 68 | /** 69 | * Gets the current time in seconds. 70 | * @return {number} current time in seconds 71 | */ 72 | tdl.clock.LocalClock.prototype.getTime = function() { 73 | return (new Date()).getTime() * 0.001; 74 | } 75 | 76 | /** 77 | * A clock that gets the current time in seconds attempting to eep the clock 78 | * synced to the server. 79 | * @constructor 80 | * @private 81 | */ 82 | tdl.clock.SyncedClock = function(opt_syncRate, opt_url) { 83 | this.url = opt_url || window.location.href; 84 | this.syncRate = opt_syncRate || 10; 85 | this.timeOffset = 0; 86 | this.syncToServer(); 87 | } 88 | 89 | tdl.clock.SyncedClock.prototype.getLocalTime_ = function() { 90 | return (new Date()).getTime() * 0.001; 91 | } 92 | 93 | tdl.clock.SyncedClock.prototype.syncToServer = function() { 94 | var that = this; 95 | var sendTime = this.getLocalTime_(); 96 | tdl.io.sendJSON(this.url, {cmd: 'time'}, function(obj, exception) { 97 | if (exception) { 98 | tdl.log("error: syncToServer: " + exception); 99 | } else { 100 | var receiveTime = that.getLocalTime_(); 101 | var duration = receiveTime - sendTime; 102 | var serverTime = obj.time + duration * 0.5; 103 | that.timeOffset = serverTime - receiveTime; 104 | tdl.log("new timeoffset: " + that.timeOffset); 105 | } 106 | setTimeout(function() { 107 | that.syncToServer(); 108 | }, that.syncRate * 1000); 109 | }); 110 | }; 111 | 112 | /** 113 | * Gets the current time in seconds. 114 | * @return {number} current time in seconds 115 | */ 116 | tdl.clock.SyncedClock.prototype.getTime = function() { 117 | return (new Date()).getTime() * 0.001 + this.timeOffset; 118 | } 119 | 120 | return tdl.clock; 121 | }); 122 | -------------------------------------------------------------------------------- /tdl/string.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains objects strings. 35 | */ 36 | define(['./base-rs'], function(BaseRS) { 37 | 38 | tdl.provide('tdl.string'); 39 | /** 40 | * A module for string. 41 | * @namespace 42 | */ 43 | tdl.string = tdl.string || {}; 44 | 45 | /** 46 | * Whether a haystack ends with a needle. 47 | * @param {string} haystack String to search 48 | * @param {string} needle String to search for. 49 | * @return {boolean} True if haystack ends with needle. 50 | */ 51 | tdl.string.endsWith = function(haystack, needle) { 52 | return haystack.substr(haystack.length - needle.length) === needle; 53 | }; 54 | 55 | /** 56 | * Whether a haystack starts with a needle. 57 | * @param {string} haystack String to search 58 | * @param {string} needle String to search for. 59 | * @return {boolean} True if haystack starts with needle. 60 | */ 61 | tdl.string.startsWith = function(haystack, needle) { 62 | return haystack.substr(0, needle.length) === needle; 63 | }; 64 | 65 | /** 66 | * Converts a non-homogenious array into a string. 67 | * @param {Array.<*>} args Args to turn into a string 68 | * @return {string} string representing args 69 | */ 70 | tdl.string.argsToString = function(args) { 71 | var lastArgWasNumber = false; 72 | var numArgs = args.length; 73 | var strs = []; 74 | for (var ii = 0; ii < numArgs; ++ii) { 75 | var arg = args[ii]; 76 | if (arg === undefined) { 77 | strs.push('undefined'); 78 | } else if (typeof arg == 'number') { 79 | if (lastArgWasNumber) { 80 | strs.push(", "); 81 | } 82 | if (arg == Math.floor(arg)) { 83 | strs.push(arg.toFixed(0)); 84 | } else { 85 | strs.push(arg.toFixed(3)); 86 | } 87 | lastArgWasNumber = true; 88 | } else if (window.Float32Array && arg instanceof Float32Array) { 89 | // TODO(gman): Make this handle other types of arrays. 90 | strs.push(tdl.string.argsToString(arg)); 91 | } else { 92 | strs.push(arg.toString()); 93 | lastArgWasNumber = false; 94 | } 95 | } 96 | return strs.join(""); 97 | }; 98 | 99 | /** 100 | * Converts an object into a string. Similar to JSON.stringify but just used 101 | * for debugging. 102 | * @param {*} obj object to stringify. 103 | * @param {string?} opt_prefix optional prefix 104 | * @return {string} stringified object 105 | */ 106 | tdl.string.objToString = function(obj, opt_prefix) { 107 | var strs = []; 108 | 109 | function objToString(obj, opt_prefix) { 110 | opt_prefix = opt_prefix || ""; 111 | if (typeof obj == 'object') { 112 | if (obj.length !== undefined) { 113 | for (var ii = 0; ii < obj.length; ++ii) { 114 | objToString(obj[ii], opt_prefix + "[" + ii + "]"); 115 | } 116 | } else { 117 | for (var name in obj) { 118 | objToString(obj[name], opt_prefix + "." + name); 119 | } 120 | } 121 | } else { 122 | strs.push(tdl.string.argsToString([opt_prefix, ": ", obj])); 123 | } 124 | } 125 | 126 | objToString(obj); 127 | 128 | return strs.join("\n"); 129 | }; 130 | 131 | return tdl.string; 132 | }); 133 | 134 | -------------------------------------------------------------------------------- /tdl/sync.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains objects to sync app settings across 35 | * browsers. 36 | */ 37 | define( 38 | [ './base-rs', 39 | './log', 40 | './io', 41 | './misc', 42 | ], function( 43 | BaseRS, 44 | Log, 45 | IO, 46 | Misc) { 47 | 48 | tdl.provide('tdl.sync'); 49 | /** 50 | * A module for sync. 51 | * @namespace 52 | */ 53 | tdl.sync = tdl.sync || {}; 54 | 55 | /** 56 | * Manages synchronizing settings across browsers. Requires a server 57 | * running to support it. Note that even if you don't want to sync 58 | * across browsers you can still use the SyncManager. 59 | * 60 | * @constructor 61 | * @param {!Object} settings The object that contains the settings you 62 | * want kept in sync. 63 | */ 64 | tdl.sync.SyncManager = function(settings, opt_callback) { 65 | this.settings = settings; 66 | this.putCount = 0; 67 | this.getCount = 0; 68 | this.callback = opt_callback || function() {}; 69 | 70 | // This probably should not be here. 71 | tdl.misc.applyUrlSettings(settings); 72 | } 73 | 74 | /** 75 | * Initialize the sync manager to start syncing settings with a server. 76 | * @param {string} url Url of server. 77 | * @param {boolean} slave true if this page is a slave. Slaves only receive 78 | * settings from the server. Non slaves send settings the server. 79 | */ 80 | tdl.sync.SyncManager.prototype.init = function(url, slave) { 81 | var that = this; 82 | this.sync = true; 83 | this.slave = slave; 84 | this.socket = new WebSocket(url); 85 | this.opened = false; 86 | this.queued = []; 87 | this.socket.onopen = function(event) { 88 | tdl.log("SOCKET OPENED!"); 89 | that.opened = true; 90 | for (var ii = 0; ii < that.queued.length; ++ii) { 91 | var settings = that.queued[ii]; 92 | ++that.putCount; 93 | tdl.log("--PUT:[", that.putCount, "]-------------"); 94 | tdl.log(settings); 95 | that.socket.send(settings); 96 | } 97 | that.queued = []; 98 | }; 99 | this.socket.onerror = function(event) { 100 | tdl.log("SOCKET ERROR!"); 101 | }; 102 | this.socket.onclose = function(event) { 103 | tdl.log("SOCKET CLOSED!"); 104 | }; 105 | this.socket.onmessage = function(event) { 106 | ++that.getCount; 107 | tdl.log("--GET:[", that.getCount, ":", event.type, "]-------------"); 108 | var obj = JSON.parse(event.data); 109 | tdl.dumpObj(obj); 110 | tdl.misc.copyProperties(obj, that.settings); 111 | that.callback(obj); 112 | }; 113 | }; 114 | 115 | /** 116 | * Sets the settings. 117 | * 118 | * If we are synchronizing settings the settings are sent to the server. 119 | * Otherwise they are applied directy. 120 | * 121 | * @param {!Object} settings Object with new settings. 122 | */ 123 | tdl.sync.SyncManager.prototype.setSettings = function(settings) { 124 | if (this.sync) { 125 | if (!this.slave) { 126 | if (this.socket) { 127 | if (!this.opened) { 128 | this.queued.push(JSON.stringify(settings)); 129 | } else { 130 | ++this.putCount; 131 | tdl.log("--PUT:[", this.putCount, "]-------------"); 132 | tdl.dumpObj(settings); 133 | this.socket.send(JSON.stringify(settings)); 134 | } 135 | } 136 | } 137 | } else { 138 | tdl.misc.copyProperties(settings, this.settings); 139 | this.callback(settings); 140 | } 141 | }; 142 | 143 | return tdl.sync; 144 | }); 145 | 146 | -------------------------------------------------------------------------------- /tdl/misc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains misc functions that don't fit elsewhere. 35 | */ 36 | define(['./base-rs', './log'], function(BaseRS, Log) { 37 | 38 | tdl.provide('tdl.misc'); 39 | /** 40 | * A module for misc. 41 | * @namespace 42 | */ 43 | tdl.misc = tdl.misc || {}; 44 | 45 | /** 46 | * Lets you parse JS object literal has JSON. 47 | * 48 | * JSON requires property names be quoted, js object literal 49 | * does not. 50 | * 51 | * @example 52 | * var o = tdl.misc.parseUnquoteJSObjectString('{a:"b",c:123}'); 53 | * @param {string} str js object literal string. 54 | */ 55 | tdl.misc.parseUnquotedJSObjectString = function(str) { 56 | // NOTE: does not handle strings with : in them. 57 | var quoted = str.replace(/([a-zA-Z0-9_]+):/g,'"$1":') 58 | return JSON.parse(quoted); 59 | }; 60 | 61 | /** 62 | * Applies part of the query string to an object. 63 | * @param {object} object to apply properties to 64 | * @param {string?} name of query parameter that has settings. 65 | * Default = 'settings' 66 | * @example 67 | * // Assuming the current URL is 68 | * // http://foo.com?settings={a:123,b:"hello",c:[1,2]} 69 | * // then 70 | * var o = {}; 71 | * tdl.misc.applyUrlSettings(o); 72 | * console.log(JSON.stringify(o, undefined, " ")); 73 | * 74 | * // should print 75 | * { 76 | * "a": 123, 77 | * "b": "hello", 78 | * "c": [1, 2] 79 | * } 80 | */ 81 | tdl.misc.applyUrlSettings = function(obj, opt_argumentName) { 82 | var argumentName = opt_argumentName || 'settings'; 83 | try { 84 | var s = window.location.href; 85 | var q = s.indexOf("?"); 86 | var e = s.indexOf("#"); 87 | if (e < 0) { 88 | e = s.length; 89 | } 90 | var query = s.substring(q + 1, e); 91 | //tdl.log("query:", query); 92 | var pairs = query.split("&"); 93 | //tdl.log("pairs:", pairs.length); 94 | for (var ii = 0; ii < pairs.length; ++ii) { 95 | var keyValue = pairs[ii].split("="); 96 | var key = keyValue[0]; 97 | var value = decodeURIComponent(keyValue[1]); 98 | //tdl.log(ii, ":", key, "=", value); 99 | switch (key) { 100 | case argumentName: 101 | //tdl.log(value); 102 | var settings = tdl.misc.parseUnquotedJSObjectString(value) 103 | //tdl.log("settings:", settings); 104 | tdl.misc.copyProperties(settings, obj); 105 | break; 106 | } 107 | } 108 | } catch (e) { 109 | tdl.error(e); 110 | tdl.error("settings:", settings); 111 | return; 112 | } 113 | }; 114 | 115 | /** 116 | * Copies properties from obj to dst recursively. 117 | * @private 118 | * @param {!Object} obj Object with new settings. 119 | * @param {!Object} dst Object to receive new settings. 120 | */ 121 | tdl.misc.copyProperties = function(obj, dst) { 122 | for (var name in obj) { 123 | var value = obj[name]; 124 | if (value instanceof Array) { 125 | //tdl.log("apply->: ", name, "[]"); 126 | var newDst = dst[name]; 127 | if (!newDst) { 128 | newDst = []; 129 | dst[name] = newDst; 130 | } 131 | tdl.misc.copyProperties(value, newDst); 132 | } else if (typeof value == 'object') { 133 | //tdl.log("apply->: ", name); 134 | var newDst = dst[name]; 135 | if (!newDst) { 136 | newDst = {}; 137 | dst[name] = newDst; 138 | } 139 | tdl.misc.copyProperties(value, newDst); 140 | } else { 141 | //tdl.log("apply: ", name, "=", value); 142 | dst[name] = value; 143 | } 144 | } 145 | }; 146 | 147 | return tdl.misc; 148 | }); 149 | -------------------------------------------------------------------------------- /example/example-requirejs.html: -------------------------------------------------------------------------------- 1 | 31 | 32 | 33 | 34 | 35 | WebGL TDL Example 36 | 78 | 79 | 80 | 81 |
tdl.js - example
82 |
83 |
fps:
84 |
85 |
86 | 87 |
88 | 89 | 113 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /tdl/loader.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains a loader class for helping to load 35 | * muliple assets in an asynchronous manner. 36 | */ 37 | define(['./base-rs', './io'], function(BaseRS, IO) { 38 | 39 | tdl.provide('tdl.loader'); 40 | /** 41 | * A Module with a loader class for helping to load muliple assets in an 42 | * asynchronous manner. 43 | * @namespace 44 | */ 45 | tdl.loader = tdl.loader || {}; 46 | 47 | /** 48 | * @callback Loader~Finished 49 | * @memberOf tdl.loader 50 | */ 51 | /** 52 | * A simple Loader class to call some callback when everything has loaded. 53 | * @constructor 54 | * @param {tdl.loader.Loader~Finished} onFinished Function to call when 55 | * final item has loaded. 56 | */ 57 | tdl.loader.Loader = function(onFinished) { 58 | this.count_ = 1; 59 | this.onFinished_ = onFinished; 60 | 61 | /** 62 | * The LoadInfo for this loader you can use to track progress. 63 | * @type {!tdl.io.LoadInfo} 64 | */ 65 | this.loadInfo = tdl.io.createLoadInfo(); 66 | }; 67 | 68 | /** 69 | * Creates a Loader for helping to load a bunch of items asychronously. 70 | * 71 | * The way you use this is as follows. 72 | * 73 | * @example 74 | * var loader = tdl.loader.createLoader(myFinishedCallback); 75 | * loader.loadTextFile(text1Url, callbackForText); 76 | * loader.loadTextFile(text2Url, callbackForText); 77 | * loader.loadTextFile(text3Url, callbackForText); 78 | * loader.finish(); 79 | * 80 | * The loader guarantees that myFinishedCallback will be called after 81 | * all the items have been loaded. 82 | * 83 | * @param {tdl.loader.Loader~Finished} onFinished Function to call when 84 | * final item has loaded. 85 | * @return {tdl.loader.Loader} A Loader Object. 86 | */ 87 | tdl.loader.createLoader = function(onFinished) { 88 | return new tdl.loader.Loader(onFinished); 89 | }; 90 | 91 | /** 92 | * @callback Loader~Text 93 | * @param {string?} str contents of file 94 | * @param {error?} error or null of no error. 95 | */ 96 | 97 | /** 98 | * Loads a text file. 99 | * @param {string} url URL of scene to load. 100 | * @param {Loader~Text} onTextLoaded 101 | * Function to call when the file is loaded. It will be 102 | * passed the contents of the file as a string and an 103 | * exception which is null on success. 104 | */ 105 | tdl.loader.Loader.prototype.loadTextFile = function(url, onTextLoaded) { 106 | var that = this; // so the function below can see "this". 107 | ++this.count_; 108 | var loadInfo = tdl.io.loadTextFile(url, function(string, exception) { 109 | onTextLoaded(string, exception); 110 | that.countDown_(); 111 | }); 112 | this.loadInfo.addChild(loadInfo); 113 | }; 114 | 115 | /** 116 | * Creates a loader that is tracked by this loader so that when the new loader 117 | * is finished it will be reported to this loader. 118 | * @param {tdl.loader.Loader~Finished} onFinished Function 119 | * to be called when everything loaded with this loader has 120 | * finished. 121 | * @return {tdl.loader.Loader} The new Loader. 122 | */ 123 | tdl.loader.Loader.prototype.createLoader = function(onFinished) { 124 | var that = this; 125 | ++this.count_; 126 | var loader = tdl.loader.createLoader(function() { 127 | onFinished(); 128 | that.countDown_(); 129 | }); 130 | this.loadInfo.addChild(loader.loadInfo); 131 | return loader; 132 | }; 133 | 134 | /** 135 | * Counts down the internal count and if it gets to zero calls the callback. 136 | * @private 137 | */ 138 | tdl.loader.Loader.prototype.countDown_ = function() { 139 | --this.count_; 140 | if (this.count_ === 0) { 141 | this.onFinished_(); 142 | } 143 | }; 144 | 145 | /** 146 | * Finishes the loading process. 147 | * Actually this just calls countDown_ to account for the count starting at 1. 148 | * @private 149 | */ 150 | tdl.loader.Loader.prototype.finish = function() { 151 | this.countDown_(); 152 | }; 153 | 154 | return tdl.loader; 155 | }); 156 | -------------------------------------------------------------------------------- /tdl/buffers.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains objects to deal with WebGL 35 | * buffers. 36 | */ 37 | define(['./base-rs'], function(BaseRS) { 38 | 39 | tdl.provide('tdl.buffers'); 40 | /** 41 | * A module for buffers. 42 | * @namespace 43 | */ 44 | tdl.buffers = tdl.buffers || {}; 45 | 46 | /** 47 | * A Buffer represnets a WebGL buffer. 48 | * @constructor 49 | * @param {tdl.primitives.AttribBuffer} array AttribBuffer with 50 | * data. 51 | * @param {number?} opt_target Assumes gl.ARRAY_BUFFER 52 | */ 53 | tdl.buffers.Buffer = function(array, opt_target) { 54 | var target = opt_target || gl.ARRAY_BUFFER; 55 | var buf = gl.createBuffer(); 56 | this.target = target; 57 | this.buf = buf; 58 | this.set(array); 59 | }; 60 | 61 | /** 62 | * Sets the contents of this buffer from the contents of the 63 | * given AttribBuffer. 64 | * @param {tdl.primitives.AttribBuffer} array AttribBuffer with 65 | * data. 66 | * @param {number?} opt_usage GL buffer usage. Defaults to 67 | * gl.STATIC_DRAW. 68 | */ 69 | tdl.buffers.Buffer.prototype.set = function(array, opt_usage) { 70 | this.numComponents_ = array.numComponents; 71 | this.numElements_ = array.numElements; 72 | this.totalComponents_ = this.numComponents_ * this.numElements_; 73 | if (array.buffer instanceof Float32Array) { 74 | this.type_ = gl.FLOAT; 75 | this.normalize_ = false; 76 | } else if (array.buffer instanceof Uint8Array) { 77 | this.type_ = gl.UNSIGNED_BYTE; 78 | this.normalize_ = true; 79 | } else if (array.buffer instanceof Int8Array) { 80 | this.type_ = gl.BYTE; 81 | this.normalize_ = true; 82 | } else if (array.buffer instanceof Uint16Array) { 83 | this.type_ = gl.UNSIGNED_SHORT; 84 | this.normalize_ = true; 85 | } else if (array.buffer instanceof Int16Array) { 86 | this.type_ = gl.SHORT; 87 | this.normalize_ = true; 88 | } else { 89 | throw("unhandled type:" + (typeof array.buffer)); 90 | } 91 | gl.bindBuffer(this.target, this.buf); 92 | gl.bufferData(this.target, array.buffer, opt_usage || gl.STATIC_DRAW); 93 | } 94 | 95 | /** 96 | * Sets part of a buffer. 97 | * @param {ArrayBufferView} array some typed array buffer view 98 | * @param {number} the offset in bytes into the buffer to copy 99 | * the array. 100 | */ 101 | tdl.buffers.Buffer.prototype.setRange = function(array, offset) { 102 | gl.bindBuffer(this.target, this.buf); 103 | gl.bufferSubData(this.target, offset, array); 104 | }; 105 | 106 | /** 107 | * Gets the type of the buffer. Eg. `gl.FLOAT` 108 | * @return {number} 109 | */ 110 | tdl.buffers.Buffer.prototype.type = function() { 111 | return this.type_; 112 | }; 113 | 114 | /** 115 | * Gets the number of components per element of buffer. 116 | * @return {number} num components per element in buffer 117 | */ 118 | tdl.buffers.Buffer.prototype.numComponents = function() { 119 | return this.numComponents_; 120 | }; 121 | 122 | /** 123 | * Gets the number of elements in the buffer 124 | * @return {number} num elements in buffer 125 | */ 126 | tdl.buffers.Buffer.prototype.numElements = function() { 127 | return this.numElements_; 128 | }; 129 | 130 | /** 131 | * Gets the total components in the buffer. 132 | * @return {number} Basically this is numComponents * 133 | * numElements 134 | */ 135 | tdl.buffers.Buffer.prototype.totalComponents = function() { 136 | return this.totalComponents_; 137 | }; 138 | 139 | /** 140 | * Get the WebGLBuffer for this buffer. 141 | * @return {WebGLBuffer} the WebGLBuffer for this buffer. 142 | */ 143 | tdl.buffers.Buffer.prototype.buffer = function() { 144 | return this.buf; 145 | }; 146 | 147 | /** 148 | * Get the stride? 149 | * @return {number} stride. 150 | */ 151 | tdl.buffers.Buffer.prototype.stride = function() { 152 | return 0; 153 | }; 154 | 155 | /** 156 | * Gets whether the data in this buffer should be normalized. 157 | * @return {boolean} normalizaiton. 158 | */ 159 | tdl.buffers.Buffer.prototype.normalize = function() { 160 | return this.normalize_; 161 | } 162 | 163 | /** 164 | * Get the offset? 165 | * @return {number} offset. 166 | */ 167 | tdl.buffers.Buffer.prototype.offset = function() { 168 | return 0; 169 | }; 170 | 171 | return tdl.buffers; 172 | }); 173 | -------------------------------------------------------------------------------- /tdl/shader.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains a class which assists with the 35 | * loading of GLSL shaders. 36 | */ 37 | define(['./base-rs'], function(BaseRS) { 38 | 39 | tdl.provide('tdl.shader'); 40 | /** 41 | * A module for shaders. 42 | * @namespace 43 | */ 44 | tdl.shader = tdl.shader || {}; 45 | 46 | /** 47 | 48 | * Loads a shader from vertex and fragment programs specified in 49 | * "script" nodes in the HTML page. This provides a convenient 50 | * mechanism for writing GLSL snippets without the burden of 51 | * additional syntax like per-line quotation marks. 52 | * @param {WebGLRenderingContext} gl The WebGLRenderingContext 53 | * into which the shader will be loaded. 54 | * @param {string} vertexScriptName The name of the HTML Script node 55 | * containing the vertex program. 56 | * @param {string} fragmentScriptName The name of the HTML Script node 57 | * containing the fragment program. 58 | */ 59 | tdl.shader.loadFromScriptNodes = function(gl, 60 | vertexScriptName, 61 | fragmentScriptName) { 62 | var vertexScript = document.getElementById(vertexScriptName); 63 | var fragmentScript = document.getElementById(fragmentScriptName); 64 | if (!vertexScript || !fragmentScript) 65 | return null; 66 | return new tdl.shader.Shader(gl, 67 | vertexScript.text, 68 | fragmentScript.text); 69 | } 70 | 71 | /** 72 | * Helper which convers GLSL names to JavaScript names. 73 | * @private 74 | */ 75 | tdl.shader.glslNameToJs_ = function(name) { 76 | return name.replace(/_(.)/g, function(_, p1) { return p1.toUpperCase(); }); 77 | } 78 | 79 | /** 80 | * Creates a new Shader object, loading and linking the given vertex 81 | * and fragment shaders into a program. 82 | * @param {WebGLRenderingContext} gl The WebGLRenderingContext 83 | * into which the shader will be loaded. 84 | * @param {string} vertex The vertex shader. 85 | * @param {string} fragment The fragment shader. 86 | */ 87 | tdl.shader.Shader = function(gl, vertex, fragment) { 88 | this.program = gl.createProgram(); 89 | this.gl = gl; 90 | 91 | var vs = this.loadShader(this.gl.VERTEX_SHADER, vertex); 92 | if (!vs) { 93 | tdl.log("couldn't load shader") 94 | } 95 | this.gl.attachShader(this.program, vs); 96 | this.gl.deleteShader(vs); 97 | 98 | var fs = this.loadShader(this.gl.FRAGMENT_SHADER, fragment); 99 | if (!fs) { 100 | tdl.log("couldn't load shader") 101 | } 102 | this.gl.attachShader(this.program, fs); 103 | this.gl.deleteShader(fs); 104 | 105 | this.gl.linkProgram(this.program); 106 | this.gl.useProgram(this.program); 107 | 108 | // Check the link status 109 | var linked = this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS); 110 | if (!linked && !this.gl.isContextLost()) { 111 | var infoLog = this.gl.getProgramInfoLog(this.program); 112 | tdl.error("Error linking program:\n" + infoLog); 113 | this.gl.deleteProgram(this.program); 114 | this.program = null; 115 | return; 116 | } 117 | 118 | // find uniforms and attributes 119 | var re = /(uniform|attribute)\s+\S+\s+(\S+)\s*;/g; 120 | var match = null; 121 | while ((match = re.exec(vertex + '\n' + fragment)) != null) { 122 | var glslName = match[2]; 123 | var jsName = tdl.shader.glslNameToJs_(glslName); 124 | var loc = -1; 125 | if (match[1] == "uniform") { 126 | this[jsName + "Loc"] = this.getUniform(glslName); 127 | } else if (match[1] == "attribute") { 128 | this[jsName + "Loc"] = this.getAttribute(glslName); 129 | } 130 | if (loc >= 0) { 131 | this[jsName + "Loc"] = loc; 132 | } 133 | } 134 | } 135 | 136 | /** 137 | * Binds the shader's program. 138 | */ 139 | tdl.shader.Shader.prototype.bind = function() { 140 | this.gl.useProgram(this.program); 141 | } 142 | 143 | /** 144 | * Helper for loading a shader. 145 | * @private 146 | */ 147 | tdl.shader.Shader.prototype.loadShader = function(type, shaderSrc) { 148 | var shader = this.gl.createShader(type); 149 | if (shader == null) { 150 | return null; 151 | } 152 | 153 | // Load the shader source 154 | this.gl.shaderSource(shader, shaderSrc); 155 | // Compile the shader 156 | this.gl.compileShader(shader); 157 | // Check the compile status 158 | if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { 159 | var infoLog = this.gl.getShaderInfoLog(shader); 160 | tdl.error("Error compiling shader:\n" + infoLog); 161 | this.gl.deleteShader(shader); 162 | return null; 163 | } 164 | return shader; 165 | } 166 | 167 | /** 168 | * Helper for looking up an attribute's location. 169 | * @private 170 | */ 171 | tdl.shader.Shader.prototype.getAttribute = function(name) { 172 | return this.gl.getAttribLocation(this.program, name); 173 | }; 174 | 175 | /** 176 | * Helper for looking up an attribute's location. 177 | * @private 178 | */ 179 | tdl.shader.Shader.prototype.getUniform = function(name) { 180 | return this.gl.getUniformLocation(this.program, name); 181 | } 182 | 183 | return tdl.shader; 184 | }); 185 | -------------------------------------------------------------------------------- /tdl/base-rs.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014, Gregg Tavares. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Gregg Tavares. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | // emulate tdl/base.js for require.js 33 | 34 | define(function() { 35 | 36 | // Was base.js already included? 37 | var haveBaseJS = (this.tdl !== undefined); 38 | if (haveBaseJS) { 39 | tdl.provide('tdl.base-rs'); 40 | return; 41 | } 42 | 43 | this.tdl = {base:{}}; 44 | this.goog = {}; 45 | 46 | var noop = function() {}; 47 | 48 | // Let's assume if the user is using require JS they don't need tdl.require 49 | // If that's not the case we'd need provide a version of tdl.require that 50 | // ignores the tdl files but not the user's files. Probably hooked into requirejs 51 | tdl.require = noop; 52 | tdl.provide = noop; 53 | 54 | 55 | /** 56 | * Determine whether a value is an array. Do not use instanceof because that 57 | * will not work for V8 arrays (the browser thinks they are Objects). 58 | * @param {*} value A value. 59 | * @return {boolean} Whether the value is an array. 60 | */ 61 | tdl.base.isArray = function(value) { 62 | var valueAsObject = /** @type {!Object} */ (value); 63 | return typeof(value) === 'object' && value !== null && 64 | 'length' in valueAsObject && 'splice' in valueAsObject; 65 | }; 66 | 67 | /** 68 | * A stub for later optionally converting obfuscated names 69 | * @private 70 | * @param {string} name Name to un-obfuscate. 71 | * @return {string} un-obfuscated name. 72 | */ 73 | tdl.base.maybeDeobfuscateFunctionName_ = function(name) { 74 | return name; 75 | }; 76 | 77 | /** 78 | * Makes one class inherit from another. 79 | * @param {!Object} subClass Class that wants to inherit. 80 | * @param {!Object} superClass Class to inherit from. 81 | */ 82 | tdl.base.inherit = function(subClass, superClass) { 83 | /** 84 | * TmpClass. 85 | * @ignore 86 | * @constructor 87 | */ 88 | var TmpClass = function() { }; 89 | TmpClass.prototype = superClass.prototype; 90 | subClass.prototype = new TmpClass(); 91 | }; 92 | 93 | /** 94 | * Parses an error stack from an exception 95 | * @param {!Exception} excp The exception to get a stack trace from. 96 | * @return {!Array.} An array of strings of the stack trace. 97 | */ 98 | tdl.base.parseErrorStack = function(excp) { 99 | var stack = []; 100 | var name; 101 | var line; 102 | 103 | if (!excp || !excp.stack) { 104 | return stack; 105 | } 106 | 107 | var stacklist = excp.stack.split('\n'); 108 | 109 | for (var i = 0; i < stacklist.length - 1; i++) { 110 | var framedata = stacklist[i]; 111 | 112 | name = framedata.match(/^([a-zA-Z0-9_$]*)/)[1]; 113 | if (name) { 114 | name = tdl.base.maybeDeobfuscateFunctionName_(name); 115 | } else { 116 | name = 'anonymous'; 117 | } 118 | 119 | var result = framedata.match(/(.*:[0-9]+)$/); 120 | line = result && result[1]; 121 | 122 | if (!line) { 123 | line = '(unknown)'; 124 | } 125 | 126 | stack[stack.length] = name + ' : ' + line 127 | } 128 | 129 | // remove top level anonymous functions to match IE 130 | var omitRegexp = /^anonymous :/; 131 | while (stack.length && omitRegexp.exec(stack[stack.length - 1])) { 132 | stack.length = stack.length - 1; 133 | } 134 | 135 | return stack; 136 | }; 137 | 138 | /** 139 | * Gets a function name from a function object. 140 | * @param {!function(...): *} aFunction The function object to try to get a 141 | * name from. 142 | * @return {string} function name or 'anonymous' if not found. 143 | */ 144 | tdl.base.getFunctionName = function(aFunction) { 145 | var regexpResult = aFunction.toString().match(/function(\s*)(\w*)/); 146 | if (regexpResult && regexpResult.length >= 2 && regexpResult[2]) { 147 | return tdl.base.maybeDeobfuscateFunctionName_(regexpResult[2]); 148 | } 149 | return 'anonymous'; 150 | }; 151 | 152 | /** 153 | * Pretty prints an exception's stack, if it has one. 154 | * @param {Array.} stack An array of errors. 155 | * @return {string} The pretty stack. 156 | */ 157 | tdl.base.formatErrorStack = function(stack) { 158 | var result = ''; 159 | for (var i = 0; i < stack.length; i++) { 160 | result += '> ' + stack[i] + '\n'; 161 | } 162 | return result; 163 | }; 164 | 165 | /** 166 | * Gets a stack trace as a string. 167 | * @param {number} stripCount The number of entries to strip from the top of the 168 | * stack. Example: Pass in 1 to remove yourself from the stack trace. 169 | * @return {string} The stack trace. 170 | */ 171 | tdl.base.getStackTrace = function(stripCount) { 172 | var result = ''; 173 | 174 | if (typeof(arguments.caller) != 'undefined') { // IE, not ECMA 175 | for (var a = arguments.caller; a != null; a = a.caller) { 176 | result += '> ' + tdl.base.getFunctionName(a.callee) + '\n'; 177 | if (a.caller == a) { 178 | result += '*'; 179 | break; 180 | } 181 | } 182 | } else { // Mozilla, not ECMA 183 | // fake an exception so we can get Mozilla's error stack 184 | var testExcp; 185 | try { 186 | eval('var var;'); 187 | } catch (testExcp) { 188 | var stack = tdl.base.parseErrorStack(testExcp); 189 | result += tdl.base.formatErrorStack(stack.slice(3 + stripCount, 190 | stack.length)); 191 | } 192 | } 193 | 194 | return result; 195 | }; 196 | 197 | /** 198 | * Returns true if the user's browser is Microsoft IE. 199 | * @return {boolean} true if the user's browser is Microsoft IE. 200 | */ 201 | tdl.base.IsMSIE = function() { 202 | var ua = navigator.userAgent.toLowerCase(); 203 | var msie = /msie/.test(ua) && !/opera/.test(ua); 204 | return msie; 205 | }; 206 | 207 | return {}; 208 | }); 209 | -------------------------------------------------------------------------------- /tdl/models.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains objects to manage models. 35 | */ 36 | define(['./base-rs', './buffers'], function(BaseRS, Buffers) { 37 | 38 | tdl.provide('tdl.models'); 39 | /** 40 | * A module for models. 41 | * @namespace 42 | */ 43 | tdl.models = tdl.models || {}; 44 | 45 | /** 46 | * Manages a program, buffers and textures for easier drawing. 47 | * @constructor 48 | * @param {tdl.programs.Program} program The program to render 49 | * this model with 50 | * @param {Object.} arrays The 51 | * AttribBuffers to bind to draw this model. 52 | * @param {Object.} textures The textures to 53 | * bind to draw this model. 54 | * @param {number} opt_mode Mode to call drawElements with. Default = 55 | * gl.TRIANGLES 56 | */ 57 | tdl.models.Model = function(program, arrays, textures, opt_mode) { 58 | this.buffers = { }; 59 | this.setBuffers(arrays); 60 | 61 | var textureUnits = { } 62 | var unit = 0; 63 | for (var texture in program.textures) { 64 | textureUnits[texture] = unit++; 65 | } 66 | 67 | this.mode = (opt_mode === undefined) ? gl.TRIANGLES : opt_mode; 68 | this.textures = textures; 69 | this.textureUnits = textureUnits; 70 | this.setProgram(program); 71 | } 72 | 73 | /** 74 | * Sets the program for this model 75 | * @param {tdl.programs.Program} program The new program for 76 | * this model. 77 | */ 78 | tdl.models.Model.prototype.setProgram = function(program) { 79 | this.program = program; 80 | } 81 | 82 | /** 83 | * Sets a buffer on this model 84 | * @param {string} name The name of the buffer to set. 85 | * @param {tdl.primitives.AttribBuffer} array The AttribBuffer 86 | * to set on this model. 87 | * @param {boolean?} true if a new WebGLBuffer should be 88 | * created. 89 | */ 90 | tdl.models.Model.prototype.setBuffer = function(name, array, opt_newBuffer) { 91 | var target = (name == 'indices') ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER; 92 | var b = this.buffers[name]; 93 | if (!b || opt_newBuffer) { 94 | b = new tdl.buffers.Buffer(array, target); 95 | } else { 96 | b.set(array); 97 | } 98 | this.buffers[name] = b; 99 | }; 100 | 101 | /** 102 | * Sets the buffers on this model 103 | * @param {Object.} arrays The 104 | * AttribBuffers to bind to draw this model. 105 | * @param {boolean?} true if new WebGLBuffers should be created. 106 | */ 107 | tdl.models.Model.prototype.setBuffers = function(arrays, opt_newBuffers) { 108 | var that = this; 109 | for (var name in arrays) { 110 | this.setBuffer(name, arrays[name], opt_newBuffers); 111 | } 112 | if (this.buffers.indices) { 113 | this.baseBuffer = this.buffers.indices; 114 | this.drawFunc = function(totalComponents, startOffset) { 115 | gl.drawElements(that.mode, totalComponents, gl.UNSIGNED_SHORT, startOffset); 116 | } 117 | } else { 118 | for (var key in this.buffers) { 119 | this.baseBuffer = this.buffers[key]; 120 | break; 121 | } 122 | this.drawFunc = function(totalComponents, startOffset) { 123 | gl.drawArrays(that.mode, startOffset, totalComponents); 124 | } 125 | } 126 | }; 127 | 128 | tdl.models.Model.prototype.applyUniforms_ = function(opt_uniforms) { 129 | if (opt_uniforms) { 130 | this.program.applyUniforms(opt_uniforms); 131 | } 132 | }; 133 | 134 | /** 135 | * Sets up the shared parts of drawing this model. Uses the 136 | * program, binds the buffers, sets the textures. 137 | * 138 | * @param {...Object.} opt_uniforms An object of 139 | * names to values to set on this models uniforms. 140 | */ 141 | tdl.models.Model.prototype.drawPrep = function(opt_uniforms) { 142 | var program = this.program; 143 | var buffers = this.buffers; 144 | var textures = this.textures; 145 | 146 | program.use(); 147 | for (var buffer in buffers) { 148 | var b = buffers[buffer]; 149 | if (buffer == 'indices') { 150 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.buffer()); 151 | } else { 152 | var attrib = program.attrib[buffer]; 153 | if (attrib) { 154 | attrib(b); 155 | } 156 | } 157 | } 158 | 159 | this.applyUniforms_(textures); 160 | for (var ii = 0; ii < arguments.length; ++ii) { 161 | this.applyUniforms_(arguments[ii]); 162 | } 163 | }; 164 | 165 | /** 166 | * Draws this model. 167 | * 168 | * After calling tdl.models.Model.drawPrep you can call this 169 | * function multiple times to draw this model. 170 | * 171 | * @param {Object.} opt_uniforms An object of names to 172 | * values to set on this models uniforms. 173 | * @param {Object.} opt_textures An object of names to 174 | * textures to set on this models uniforms. 175 | */ 176 | tdl.models.Model.prototype.draw = function() { 177 | var buffers = this.buffers; 178 | // if no indices buffer then assume drawFunc is drawArrays and thus 179 | // totalComponents is the number of vertices (not indices). 180 | var totalComponents = buffers.indices? buffers.indices.totalComponents(): buffers.position.numElements(); 181 | var startOffset = 0; 182 | for (var ii = 0; ii < arguments.length; ++ii) { 183 | var arg = arguments[ii]; 184 | if (typeof arg == 'number') { 185 | switch (ii) { 186 | case 0: 187 | totalComponents = arg; 188 | break; 189 | case 1: 190 | startOffset = arg; 191 | break; 192 | default: 193 | throw 'unvalid argument'; 194 | } 195 | } else { 196 | this.applyUniforms_(arg); 197 | } 198 | } 199 | 200 | this.drawFunc(totalComponents, startOffset); 201 | }; 202 | 203 | return tdl.models; 204 | }); 205 | -------------------------------------------------------------------------------- /example/example-requirejs.js: -------------------------------------------------------------------------------- 1 | var main = function( 2 | TDLBuffers, 3 | TDLFast, 4 | TDLFps, 5 | TDLLog, 6 | TDLMath, 7 | TDLModels, 8 | TDLPrimitives, 9 | TDLPrograms, 10 | TDLTextures, 11 | TDLWebGL) { 12 | // globals 13 | var gl; // the gl context. 14 | var canvas; // the canvas 15 | var math; // the math lib. 16 | var fast; // the fast math lib. 17 | var g_fpsTimer; // object to measure frames per second; 18 | var g_logGLCalls = true; // whether or not to log webgl calls 19 | var g_debug = false; // whether or not to debug. 20 | var g_drawOnce = false; // draw just one frame. 21 | 22 | //g_drawOnce = true; 23 | //g_debug = true; 24 | 25 | var g_eyeSpeed = 0.5; 26 | var g_eyeHeight = 2; 27 | var g_eyeRadius = 9; 28 | 29 | function ValidateNoneOfTheArgsAreUndefined(functionName, args) { 30 | for (var ii = 0; ii < args.length; ++ii) { 31 | if (args[ii] === undefined) { 32 | TDLLog.error("undefined passed to gl." + functionName + "(" + 33 | TDLWebGL.glFunctionArgsToString(functionName, args) + ")"); 34 | } 35 | } 36 | } 37 | 38 | function Log(msg) { 39 | if (g_logGLCalls) { 40 | TDLLog.log(msg); 41 | } 42 | } 43 | 44 | function LogGLCall(functionName, args) { 45 | if (g_logGLCalls) { 46 | ValidateNoneOfTheArgsAreUndefined(functionName, args) 47 | TDLLog.log("gl." + functionName + "(" + 48 | TDLWebGL.glFunctionArgsToString(functionName, args) + ")"); 49 | } 50 | } 51 | 52 | function createProgramFromTags(vertexTagId, fragmentTagId) { 53 | return TDLPrograms.loadProgram( 54 | document.getElementById(vertexTagId).text, 55 | document.getElementById(fragmentTagId).text); 56 | } 57 | 58 | /** 59 | * Sets up Planet. 60 | */ 61 | function setupSphere() { 62 | var textures = { 63 | diffuseSampler: TDLTextures.loadTexture('assets/sometexture.png')}; 64 | var program = createProgramFromTags( 65 | 'sphereVertexShader', 66 | 'sphereFragmentShader'); 67 | var arrays = TDLPrimitives.createSphere(0.4, 10, 12); 68 | 69 | return new TDLModels.Model(program, arrays, textures); 70 | } 71 | 72 | function initialize() { 73 | math = TDLMath; 74 | fast = TDLFast; 75 | canvas = document.getElementById("canvas"); 76 | g_fpsTimer = new TDLFps.FPSTimer(); 77 | 78 | gl = TDLWebGL.setupWebGL(canvas); 79 | if (!gl) { 80 | return false; 81 | } 82 | if (g_debug) { 83 | gl = TDLWebGL.makeDebugContext(gl, undefined, LogGLCall); 84 | } 85 | 86 | Log("--Setup Sphere---------------------------------------"); 87 | var sphere = setupSphere(); 88 | 89 | var then = 0.0; 90 | var clock = 0.0; 91 | var fpsElem = document.getElementById("fps"); 92 | 93 | // pre-allocate a bunch of arrays 94 | var projection = new Float32Array(16); 95 | var view = new Float32Array(16); 96 | var world = new Float32Array(16); 97 | var worldInverse = new Float32Array(16); 98 | var worldInverseTranspose = new Float32Array(16); 99 | var viewProjection = new Float32Array(16); 100 | var worldViewProjection = new Float32Array(16); 101 | var viewInverse = new Float32Array(16); 102 | var viewProjectionInverse = new Float32Array(16); 103 | var eyePosition = new Float32Array(3); 104 | var target = new Float32Array(3); 105 | var up = new Float32Array([0,1,0]); 106 | var lightWorldPos = new Float32Array(3); 107 | var v3t0 = new Float32Array(3); 108 | var v3t1 = new Float32Array(3); 109 | var v3t2 = new Float32Array(3); 110 | var v3t3 = new Float32Array(3); 111 | var m4t0 = new Float32Array(16); 112 | var m4t1 = new Float32Array(16); 113 | var m4t2 = new Float32Array(16); 114 | var m4t3 = new Float32Array(16); 115 | var zero4 = new Float32Array(4); 116 | var one4 = new Float32Array([1,1,1,1]); 117 | 118 | // Sphere uniforms. 119 | var sphereConst = { 120 | viewInverse: viewInverse, 121 | lightWorldPos: lightWorldPos, 122 | specular: one4, 123 | shininess: 50, 124 | specularFactor: 0.2}; 125 | var spherePer = { 126 | lightColor: new Float32Array([0,0,0,1]), 127 | world: world, 128 | worldViewProjection: worldViewProjection, 129 | worldInverse: worldInverse, 130 | worldInverseTranspose: worldInverseTranspose}; 131 | 132 | var frameCount = 0; 133 | function render() { 134 | ++frameCount; 135 | if (!g_drawOnce) { 136 | TDLWebGL.requestAnimationFrame(render, canvas); 137 | } 138 | var now = (new Date()).getTime() * 0.001; 139 | var elapsedTime; 140 | if(then == 0.0) { 141 | elapsedTime = 0.0; 142 | } else { 143 | elapsedTime = now - then; 144 | } 145 | then = now; 146 | 147 | g_fpsTimer.update(elapsedTime); 148 | fpsElem.innerHTML = g_fpsTimer.averageFPS; 149 | 150 | clock += elapsedTime; 151 | eyePosition[0] = Math.sin(clock * g_eyeSpeed) * g_eyeRadius; 152 | eyePosition[1] = g_eyeHeight; 153 | eyePosition[2] = Math.cos(clock * g_eyeSpeed) * g_eyeRadius; 154 | 155 | gl.colorMask(true, true, true, true); 156 | gl.depthMask(true); 157 | gl.clearColor(0,0,0,0); 158 | gl.clearDepth(1); 159 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); 160 | 161 | gl.enable(gl.CULL_FACE); 162 | gl.enable(gl.DEPTH_TEST); 163 | 164 | fast.matrix4.perspective( 165 | projection, 166 | math.degToRad(60), 167 | canvas.clientWidth / canvas.clientHeight, 168 | 1, 169 | 5000); 170 | fast.matrix4.lookAt( 171 | view, 172 | eyePosition, 173 | target, 174 | up); 175 | fast.matrix4.mul(viewProjection, view, projection); 176 | fast.matrix4.inverse(viewInverse, view); 177 | fast.matrix4.inverse(viewProjectionInverse, viewProjection); 178 | 179 | fast.matrix4.getAxis(v3t0, viewInverse, 0); // x 180 | fast.matrix4.getAxis(v3t1, viewInverse, 1); // y; 181 | fast.matrix4.getAxis(v3t2, viewInverse, 2); // z; 182 | fast.mulScalarVector(v3t0, 10, v3t0); 183 | fast.mulScalarVector(v3t1, 10, v3t1); 184 | fast.mulScalarVector(v3t2, 10, v3t2); 185 | fast.addVector(lightWorldPos, eyePosition, v3t0); 186 | fast.addVector(lightWorldPos, lightWorldPos, v3t1); 187 | fast.addVector(lightWorldPos, lightWorldPos, v3t2); 188 | 189 | // view: view, 190 | // projection: projection, 191 | // viewProjection: viewProjection, 192 | 193 | Log("--Draw sphere---------------------------------------"); 194 | sphere.drawPrep(sphereConst); 195 | var across = 6; 196 | var lightColor = spherePer.lightColor; 197 | var half = (across - 1) * 0.5; 198 | for (var xx = 0; xx < across; ++xx) { 199 | for (var yy = 0; yy < across; ++yy) { 200 | for (var zz = 0; zz < across; ++zz) { 201 | lightColor[0] = xx / across; 202 | lightColor[1] = yy / across; 203 | lightColor[2] = zz / across; 204 | var scale = (xx + yy + zz) % 4 / 4 + 0.5; 205 | fast.matrix4.scaling(m4t0, [scale, scale, scale]); 206 | fast.matrix4.translation(m4t1, [xx - half, yy - half, zz - half]); 207 | fast.matrix4.mul(world, m4t0, m4t1); 208 | fast.matrix4.mul(worldViewProjection, world, viewProjection); 209 | fast.matrix4.inverse(worldInverse, world); 210 | fast.matrix4.transpose(worldInverseTranspose, worldInverse); 211 | sphere.draw(spherePer); 212 | } 213 | } 214 | } 215 | 216 | // Set the alpha to 255. 217 | gl.colorMask(false, false, false, true); 218 | gl.clearColor(0,0,0,1); 219 | gl.clear(gl.COLOR_BUFFER_BIT); 220 | 221 | // turn off logging after 1 frame. 222 | g_logGLCalls = false; 223 | } 224 | render(); 225 | return true; 226 | } 227 | initialize(); 228 | } 229 | 230 | requirejs( 231 | [ '../tdl/buffers', 232 | '../tdl/fast', 233 | '../tdl/fps', 234 | '../tdl/log', 235 | '../tdl/math', 236 | '../tdl/models', 237 | '../tdl/primitives', 238 | '../tdl/programs', 239 | '../tdl/textures', 240 | '../tdl/webgl', 241 | ], 242 | main); 243 | -------------------------------------------------------------------------------- /tdl/framebuffers.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains objects to manage 35 | * framebuffers. 36 | */ 37 | define(['./base-rs', './textures'], function(BaseRS, Textures) { 38 | 39 | tdl.provide('tdl.framebuffers'); 40 | /** 41 | * A module for textures. 42 | * @namespace 43 | */ 44 | tdl.framebuffers = tdl.framebuffers || {}; 45 | 46 | /** 47 | * Creates a framebuffer 48 | * @param {number} width width of framebuffer. 49 | * @param {number} height height of framebuffer. 50 | * @param {boolean?} opt_depth true = make a depth attachment 51 | * @return {tdl.Framebuffer} the created framebuffer. 52 | */ 53 | tdl.framebuffers.createFramebuffer = function(width, height, opt_depth) { 54 | return new tdl.framebuffers.Framebuffer(width, height, opt_depth); 55 | }; 56 | 57 | /** 58 | * Creates a cubemap framebuffer 59 | * @param {number} size size of edge of cube. 60 | * @param {boolean?} opt_depth true = make a depth attachment 61 | * @return {tdl.CubeFramebuffer} the created framebuffer. 62 | */ 63 | tdl.framebuffers.createCubeFramebuffer = function(size, opt_depth) { 64 | return new tdl.framebuffers.CubeFramebuffer(size, opt_depth); 65 | }; 66 | 67 | /** 68 | * A class to represent the backbuffer (the canvas) 69 | * @constructor 70 | */ 71 | tdl.framebuffers.BackBuffer = function() { 72 | this.depth = true; 73 | this.buffer = null; 74 | }; 75 | 76 | /** 77 | * Binds the backbuffer as the current render target. 78 | */ 79 | tdl.framebuffers.BackBuffer.prototype.bind = function() { 80 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 81 | gl.viewport(0, 0, this.width, this.height); 82 | }; 83 | 84 | if (Object.prototype.__defineSetter__) { 85 | tdl.framebuffers.BackBuffer.prototype.__defineGetter__( 86 | 'width', 87 | function () { 88 | return gl.drawingBufferWidth || gl.canvas.width; 89 | } 90 | ); 91 | 92 | tdl.framebuffers.BackBuffer.prototype.__defineGetter__( 93 | 'height', 94 | function () { 95 | return gl.drawingBufferHeight || gl.canvas.height; 96 | } 97 | ); 98 | } 99 | 100 | /** 101 | * Get a FrameBuffer for the backbuffer. 102 | * Use this where you need to pass in a framebuffer, but you really 103 | * mean the backbuffer, so that binding it works as expected. 104 | * @return {tdl.BackBuffer} the created BackBuffer. 105 | */ 106 | tdl.framebuffers.getBackBuffer = function() { 107 | return new tdl.framebuffers.BackBuffer(); 108 | }; 109 | 110 | /** 111 | * Represnets a WebGLFramebuffer 112 | * @constructor 113 | * @param {number} width width of framebuffer. 114 | * @param {number} height height of framebuffer. 115 | * @param {boolean?} opt_depth true = create a depth attachment 116 | */ 117 | tdl.framebuffers.Framebuffer = function(width, height, opt_depth) { 118 | this.width = width; 119 | this.height = height; 120 | this.depth = opt_depth; 121 | var tex = new tdl.textures.SolidTexture([0,0,0,0]); 122 | this.initializeTexture(tex); 123 | 124 | var fb = gl.createFramebuffer(); 125 | gl.bindFramebuffer(gl.FRAMEBUFFER, fb); 126 | gl.framebufferTexture2D( 127 | gl.FRAMEBUFFER, 128 | gl.COLOR_ATTACHMENT0, 129 | gl.TEXTURE_2D, 130 | tex.texture, 131 | 0); 132 | 133 | if (this.depth) { 134 | if (gl.tdl.depthTexture) { 135 | var dt = new tdl.textures.DepthTexture(this.width, this.height); 136 | gl.framebufferTexture2D( 137 | gl.FRAMEBUFFER, 138 | gl.DEPTH_ATTACHMENT, 139 | gl.TEXTURE_2D, 140 | dt.texture, 141 | 0); 142 | this.depthTexture = dt; 143 | } else { 144 | var db = gl.createRenderbuffer(); 145 | gl.bindRenderbuffer(gl.RENDERBUFFER, db); 146 | gl.renderbufferStorage( 147 | gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height); 148 | gl.framebufferRenderbuffer( 149 | gl.FRAMEBUFFER, 150 | gl.DEPTH_ATTACHMENT, 151 | gl.RENDERBUFFER, 152 | db); 153 | gl.bindRenderbuffer(gl.RENDERBUFFER, null); 154 | this.depthRenderbuffer = db; 155 | } 156 | } 157 | 158 | var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); 159 | if (status != gl.FRAMEBUFFER_COMPLETE && !gl.isContextLost()) { 160 | throw("gl.checkFramebufferStatus() returned " + 161 | tdl.webgl.glEnumToString(status)); 162 | } 163 | this.framebuffer = fb; 164 | this.texture = tex; 165 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 166 | }; 167 | 168 | /** 169 | * Bind this framebuffer as the current render target. 170 | */ 171 | tdl.framebuffers.Framebuffer.prototype.bind = function() { 172 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); 173 | gl.viewport(0, 0, this.width, this.height); 174 | }; 175 | 176 | /** 177 | * Unbinds this framebuffer as the current render target 178 | */ 179 | tdl.framebuffers.Framebuffer.prototype.unbind = function() { 180 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 181 | gl.viewport( 182 | 0, 0, 183 | gl.drawingBufferWidth || gl.canvas.width, 184 | gl.drawingBufferHeight || gl.canvas.height); 185 | }; 186 | 187 | tdl.framebuffers.Framebuffer.prototype.initializeTexture = function(tex) { 188 | gl.bindTexture(gl.TEXTURE_2D, tex.texture); 189 | tex.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR); 190 | tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); 191 | tex.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 192 | tex.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 193 | gl.texImage2D(gl.TEXTURE_2D, 194 | 0, // level 195 | gl.RGBA, // internalFormat 196 | this.width, // width 197 | this.height, // height 198 | 0, // border 199 | gl.RGBA, // format 200 | gl.UNSIGNED_BYTE, // type 201 | null); // data 202 | }; 203 | 204 | /** 205 | * Represnents a Cube Map framebuffer 206 | * @constructor 207 | * @param {number} size size of edge of cube. 208 | * @param {boolean?} opt_depth true = make a depth attachment 209 | */ 210 | tdl.framebuffers.CubeFramebuffer = function(size, opt_depth) { 211 | this.size = size; 212 | this.depth = opt_depth; 213 | var tex = new tdl.textures.CubeMap(this.size); 214 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex.texture); 215 | tex.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR); 216 | tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); 217 | tex.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 218 | tex.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 219 | for (var ff = 0; ff < 6; ++ff) { 220 | gl.texImage2D(tdl.textures.CubeMap.faceTargets[ff], 221 | 0, // level 222 | gl.RGBA, // internalFormat 223 | this.size, // width 224 | this.size, // height 225 | 0, // border 226 | gl.RGBA, // format 227 | gl.UNSIGNED_BYTE, // type 228 | null); // data 229 | } 230 | if (this.depth) { 231 | var db = gl.createRenderbuffer(); 232 | gl.bindRenderbuffer(gl.RENDERBUFFER, db); 233 | gl.renderbufferStorage( 234 | gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.size, this.size); 235 | } 236 | this.framebuffers = []; 237 | for (var ff = 0; ff < 6; ++ff) { 238 | var fb = gl.createFramebuffer(); 239 | gl.bindFramebuffer(gl.FRAMEBUFFER, fb); 240 | gl.framebufferTexture2D( 241 | gl.FRAMEBUFFER, 242 | gl.COLOR_ATTACHMENT0, 243 | tdl.textures.CubeMap.faceTargets[ff], 244 | tex.texture, 245 | 0); 246 | if (this.depth) { 247 | gl.framebufferRenderbuffer( 248 | gl.FRAMEBUFFER, 249 | gl.DEPTH_ATTACHMENT, 250 | gl.RENDERBUFFER, 251 | db); 252 | } 253 | var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); 254 | if (status != gl.FRAMEBUFFER_COMPLETE) { 255 | throw("gl.checkFramebufferStatus() returned " + WebGLDebugUtils.glEnumToString(status)); 256 | } 257 | this.framebuffers.push(fb); 258 | } 259 | gl.bindRenderbuffer(gl.RENDERBUFFER, null); 260 | this.texture = tex; 261 | }; 262 | 263 | /** 264 | * Binds a face as the current render target. 265 | * @param {number} face The face to use as the render target. 266 | */ 267 | tdl.framebuffers.CubeFramebuffer.prototype.bind = function(face) { 268 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[face]); 269 | gl.viewport(0, 0, this.size, this.size); 270 | }; 271 | 272 | /** 273 | * Unbinds this framebuffer as the current render target. 274 | */ 275 | tdl.framebuffers.CubeFramebuffer.prototype.unbind = function() { 276 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 277 | gl.viewport( 278 | 0, 0, 279 | gl.drawingBufferWidth || gl.canvas.width, 280 | gl.drawingBufferHeight || gl.canvas.height); 281 | }; 282 | 283 | /** 284 | * A framebuffer with a Float32RGBA texture. 285 | * @param {number} width width of framebuffer. 286 | * @param {number} height height of framebuffer. 287 | * @param {boolean?} opt_depth true = create a depth attachment 288 | */ 289 | tdl.framebuffers.Float32Framebuffer = function(width, height, opt_depth) { 290 | if (!gl.getExtension("OES_texture_float")) { 291 | throw("Requires OES_texture_float extension"); 292 | } 293 | tdl.framebuffers.Framebuffer.call(this, width, height, opt_depth); 294 | }; 295 | 296 | tdl.base.inherit(tdl.framebuffers.Float32Framebuffer, tdl.framebuffers.Framebuffer); 297 | 298 | tdl.framebuffers.Float32Framebuffer.prototype.initializeTexture = function(tex) { 299 | gl.bindTexture(gl.TEXTURE_2D, tex.texture); 300 | tex.setParameter(gl.TEXTURE_MIN_FILTER, gl.NEAREST); 301 | tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST); 302 | tex.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 303 | tex.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 304 | gl.texImage2D(gl.TEXTURE_2D, 305 | 0, // level 306 | gl.RGBA, // internalFormat 307 | this.width, // width 308 | this.height, // height 309 | 0, // border 310 | gl.RGBA, // format 311 | gl.FLOAT, // type 312 | null); // data 313 | }; 314 | 315 | return tdl.framebuffers; 316 | }); 317 | -------------------------------------------------------------------------------- /example/example2.html: -------------------------------------------------------------------------------- 1 | 31 | 32 | 33 | 34 | 35 | WebGL TDL Example 36 | 52 | 53 | 286 | 287 | 288 |

WebGL Spheres

289 |
290 |
291 |
fps:
292 |
293 |
294 | 295 |
296 |
297 | 298 | 322 | 356 | 357 | 358 | 359 | -------------------------------------------------------------------------------- /example/line.html: -------------------------------------------------------------------------------- 1 | 31 | 32 | 33 | 34 | 35 | WebGL TDL Example 36 | 78 | 79 | 313 | 314 | 315 |
tdl.js - example
316 |
317 |
fps:
318 |
319 |
320 | 321 |
322 | 323 | 344 | 377 | 378 | 379 | 380 | -------------------------------------------------------------------------------- /example/example.html: -------------------------------------------------------------------------------- 1 | 31 | 32 | 33 | 34 | 35 | WebGL TDL Example 36 | 78 | 79 | 308 | 309 | 310 |
tdl.js - example
311 |
312 |
fps:
313 |
314 |
315 | 316 |
317 | 318 | 342 | 377 | 378 | 379 | 380 | -------------------------------------------------------------------------------- /js/require.js: -------------------------------------------------------------------------------- 1 | /* 2 | RequireJS 2.1.11 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. 3 | Available via the MIT or new BSD license. 4 | see: http://github.com/jrburke/requirejs for details 5 | */ 6 | var requirejs,require,define; 7 | (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;dthis.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&& 19 | (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= 20 | this.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); 21 | if(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", 22 | "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, 23 | a);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, 24 | nextTick: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, 25 | a="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= 26 | !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!== 27 | d&&(!("."===g||".."===g)||1g.attachEvent.toString().indexOf("[native code"))&&!Z?(O=!0,g.attachEvent("onreadystatechange",b.onScriptLoad)): 34 | (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= 35 | Q),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|| 36 | (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); 37 | -------------------------------------------------------------------------------- /tdl/io.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains various functions and class for io. 35 | */ 36 | define(['./base-rs'], function(BaseRS) { 37 | 38 | tdl.provide('tdl.io'); 39 | /** 40 | * A Module with various io functions and classes. 41 | * @namespace 42 | */ 43 | tdl.io = tdl.io || {}; 44 | 45 | /** 46 | * Creates a LoadInfo object. 47 | * @param {XMLHttpRequest} opt_request 48 | * The request to watch. 49 | * @return {!tdl.io.LoadInfo} The new LoadInfo. 50 | * @see tdl.io.LoadInfo 51 | */ 52 | tdl.io.createLoadInfo = function(opt_request) { 53 | return new tdl.io.LoadInfo(opt_request); 54 | }; 55 | 56 | /** 57 | * A class to help with progress reporting for most loading utilities. 58 | * 59 | * @example 60 | * var g_loadInfo = null; 61 | * g_id = window.setInterval(statusUpdate, 500); 62 | * g_loadInfo = tdl.scene.loadScene('http://google.com/somescene.js', 63 | * callback); 64 | * 65 | * function callback(exception) { 66 | * g_loadInfo = null; 67 | * window.clearInterval(g_id); 68 | * if (!exception) { 69 | * // do something with scene just loaded 70 | * } 71 | * } 72 | * 73 | * function statusUpdate() { 74 | * if (g_loadInfo) { 75 | * var progress = g_loadInfo.getKnownProgressInfoSoFar(); 76 | * document.getElementById('loadstatus').innerHTML = progress.percent; 77 | * } 78 | * } 79 | * 80 | * @constructor 81 | * @param {XMLHttpRequest?} opt_request 82 | * The request to watch. 83 | * @see tdl.loader.Loader 84 | */ 85 | tdl.io.LoadInfo = function(opt_request) { 86 | this.request_ = opt_request; 87 | this.streamLength_ = 0; // because the request may have been freed. 88 | this.children_ = []; 89 | }; 90 | 91 | /** 92 | * Adds another LoadInfo as a child of this LoadInfo so they can be 93 | * managed as a group. 94 | * @param {!tdl.io.LoadInfo} loadInfo The child LoadInfo. 95 | */ 96 | tdl.io.LoadInfo.prototype.addChild = function(loadInfo) { 97 | this.children_.push(loadInfo); 98 | }; 99 | 100 | /** 101 | * Marks this LoadInfo as finished. 102 | */ 103 | tdl.io.LoadInfo.prototype.finish = function() { 104 | if (this.request_) { 105 | if (this.hasStatus_) { 106 | this.streamLength_ = this.request_.streamLength; 107 | } 108 | this.request_ = null; 109 | } 110 | }; 111 | 112 | /** 113 | * Gets the total bytes that will be streamed known so far. 114 | * 115 | * If you are only streaming 1 file then this will be the info for that file but 116 | * if you have queued up many files using a `tdl.loader.Loader` 117 | * only a couple of files are streamed at a time meaning that 118 | * the size is not known for files that have yet started to 119 | * download. 120 | * 121 | * If you are downloading many files for your application and you want to 122 | * provide a progress status you have about 4 options 123 | * 124 | * 1. Use `LoadInfo.getTotalBytesDownloaded()` / 125 | * `LoadInfo.getTotalKnownBytesToStreamSoFar()` and just be 126 | * aware the bar will grown and then shrink as new files 127 | * start to download and their lengths become known. 128 | * 129 | * 2. Use `LoadInfo.getTotalRequestsDownloaded()` / 130 | * `LoadInfo.getTotalKnownRequestsToStreamSoFar()` and be 131 | * aware the granularity is not all that great since it only 132 | * reports fully downloaded files. If you are downloading a 133 | * bunch of small files this might be ok. 134 | * 135 | * 3. Put all your files in one archive. Then there will be 136 | * only one file and method 1 will work well. 137 | * 138 | * 4. Figure out the total size in bytes of the files you will 139 | * download and put that number in your application, then use 140 | * `LoadInfo.getTotalBytesDownloaded()` / 141 | * `MY_APPS_TOTAL_BYTES_TO_DOWNLOAD`. 142 | * 143 | * @return {number} The total number of currently known bytes to be streamed. 144 | */ 145 | tdl.io.LoadInfo.prototype.getTotalKnownBytesToStreamSoFar = function() { 146 | //if (!this.streamLength_ && this.request_ && this.hasStatus_) { 147 | // // 148 | // //this.streamLength_ = this.request_.streamLength; 149 | //} 150 | var total = this.streamLength_; 151 | for (var cc = 0; cc < this.children_.length; ++cc) { 152 | total += this.children_[cc].getTotalKnownBytesToStreamSoFar(); 153 | } 154 | return total; 155 | }; 156 | 157 | /** 158 | * Gets the total bytes downloaded so far. 159 | * @return {number} The total number of currently known bytes to be streamed. 160 | */ 161 | tdl.io.LoadInfo.prototype.getTotalBytesDownloaded = function() { 162 | var total = (this.request_ && this.hasStatus_) ? 163 | this.request_.bytesReceived : this.streamLength_; 164 | for (var cc = 0; cc < this.children_.length; ++cc) { 165 | total += this.children_[cc].getTotalBytesDownloaded(); 166 | } 167 | return total; 168 | }; 169 | 170 | /** 171 | * Gets the total streams that will be download known so far. 172 | * We can't know all the streams since you could use an tdl.loader.Loader 173 | * object, request some streams, then call this function, then request some 174 | * more. 175 | * 176 | * See LoadInfo.getTotalKnownBytesToStreamSoFar for details. 177 | * @return {number} The total number of requests currently known to be streamed. 178 | * @see tdl.io.LoadInfo.getTotalKnownBytesToStreamSoFar 179 | */ 180 | tdl.io.LoadInfo.prototype.getTotalKnownRequestsToStreamSoFar = function() { 181 | var total = 1; 182 | for (var cc = 0; cc < this.children_.length; ++cc) { 183 | total += this.children_[cc].getTotalKnownRequestToStreamSoFar(); 184 | } 185 | return total; 186 | }; 187 | 188 | /** 189 | * Gets the total requests downloaded so far. 190 | * @return {number} The total requests downloaded so far. 191 | */ 192 | tdl.io.LoadInfo.prototype.getTotalRequestsDownloaded = function() { 193 | var total = this.request_ ? 0 : 1; 194 | for (var cc = 0; cc < this.children_.length; ++cc) { 195 | total += this.children_[cc].getTotalRequestsDownloaded(); 196 | } 197 | return total; 198 | }; 199 | 200 | /** 201 | * Gets progress info. 202 | * This is commonly formatted version of the information available from a 203 | * LoadInfo. 204 | * 205 | * See LoadInfo.getTotalKnownBytesToStreamSoFar for details. 206 | * @return {{percent: number, downloaded: string, totalBytes: string, 207 | * base: number, suffix: string}} progress info. 208 | * @see tdl.io.LoadInfo.getTotalKnownBytesToStreamSoFar 209 | */ 210 | tdl.io.LoadInfo.prototype.getKnownProgressInfoSoFar = function() { 211 | var percent = 0; 212 | var bytesToDownload = this.getTotalKnownBytesToStreamSoFar(); 213 | var bytesDownloaded = this.getTotalBytesDownloaded(); 214 | if (bytesToDownload > 0) { 215 | percent = Math.floor(bytesDownloaded / bytesToDownload * 100); 216 | } 217 | 218 | var base = (bytesToDownload < 1024 * 1024) ? 1024 : (1024 * 1024); 219 | 220 | return { 221 | percent: percent, 222 | downloaded: (bytesDownloaded / base).toFixed(2), 223 | totalBytes: (bytesToDownload / base).toFixed(2), 224 | base: base, 225 | suffix: (base == 1024 ? 'kb' : 'mb')} 226 | 227 | }; 228 | 229 | /** 230 | * Loads text from an external file. This function is synchronous. 231 | * @param {string} url The url of the external file. 232 | * @return {string} the loaded text if the request is synchronous. 233 | */ 234 | tdl.io.loadTextFileSynchronous = function(url) { 235 | var error = 'loadTextFileSynchronous failed to load url "' + url + '"'; 236 | var request; 237 | if (window.XMLHttpRequest) { 238 | request = new XMLHttpRequest(); 239 | if (request.overrideMimeType) { 240 | request.overrideMimeType('text/plain'); 241 | } 242 | } else if (window.ActiveXObject) { 243 | request = new ActiveXObject('MSXML2.XMLHTTP.3.0'); 244 | } else { 245 | throw 'XMLHttpRequest is disabled'; 246 | } 247 | request.open('GET', url, false); 248 | request.send(null); 249 | if (request.readyState != 4) { 250 | throw error; 251 | } 252 | return request.responseText; 253 | }; 254 | 255 | /** 256 | * Loads text from an external file. This function is asynchronous. 257 | * @param {string} url The url of the external file. 258 | * @param {function(string, *): void} callback A callback passed the loaded 259 | * string and an exception which will be null on success. 260 | * @return {tdl.io.LoadInfo} A LoadInfo to track progress. 261 | */ 262 | tdl.io.loadTextFile = function(url, callback) { 263 | var error = 'loadTextFile failed to load url "' + url + '"'; 264 | var request; 265 | if (window.XMLHttpRequest) { 266 | request = new XMLHttpRequest(); 267 | if (request.overrideMimeType) { 268 | request.overrideMimeType('text/plain; charset=utf-8'); 269 | } 270 | } else if (window.ActiveXObject) { 271 | request = new ActiveXObject('MSXML2.XMLHTTP.3.0'); 272 | } else { 273 | throw 'XMLHttpRequest is disabled'; 274 | } 275 | var loadInfo = tdl.io.createLoadInfo(request, false); 276 | request.open('GET', url, true); 277 | var finish = function() { 278 | if (request.readyState == 4) { 279 | var text = ''; 280 | // HTTP reports success with a 200 status. The file protocol reports 281 | // success with zero. HTTP does not use zero as a status code (they 282 | // start at 100). 283 | // https://developer.mozilla.org/En/Using_XMLHttpRequest 284 | var success = request.status == 200 || request.status == 0; 285 | if (success) { 286 | text = request.responseText; 287 | } 288 | loadInfo.finish(); 289 | callback(text, success ? null : 'could not load: ' + url); 290 | } 291 | }; 292 | request.onreadystatechange = finish; 293 | request.send(null); 294 | return loadInfo; 295 | }; 296 | 297 | /** 298 | * Loads a file from an external file. This function is 299 | * asynchronous. 300 | * @param {string} url The url of the external file. 301 | * @param {function(string, *): void} callback A callback passed the loaded 302 | * ArrayBuffer and an exception which will be null on 303 | * success. 304 | * @return {tdl.io.LoadInfo} A LoadInfo to track progress. 305 | */ 306 | tdl.io.loadArrayBuffer = function(url, callback) { 307 | var error = 'loadArrayBuffer failed to load url "' + url + '"'; 308 | var request; 309 | if (window.XMLHttpRequest) { 310 | request = new XMLHttpRequest(); 311 | } else { 312 | throw 'XMLHttpRequest is disabled'; 313 | } 314 | var loadInfo = tdl.io.createLoadInfo(request, false); 315 | request.open('GET', url, true); 316 | var finish = function() { 317 | if (request.readyState == 4) { 318 | var text = ''; 319 | // HTTP reports success with a 200 status. The file protocol reports 320 | // success with zero. HTTP does not use zero as a status code (they 321 | // start at 100). 322 | // https://developer.mozilla.org/En/Using_XMLHttpRequest 323 | var success = request.status == 200 || request.status == 0; 324 | if (success) { 325 | arrayBuffer = request.response; 326 | } 327 | loadInfo.finish(); 328 | callback(arrayBuffer, success ? null : 'could not load: ' + url); 329 | } 330 | }; 331 | request.onreadystatechange = finish; 332 | if (request.responseType === undefined) { 333 | throw 'no support for binary files'; 334 | } 335 | request.responseType = "arraybuffer"; 336 | request.send(null); 337 | return loadInfo; 338 | }; 339 | 340 | /** 341 | * Loads JSON from an external file. This function is asynchronous. 342 | * @param {string} url The url of the external file. 343 | * @param {function(jsonObject, *): void} callback A callback passed the loaded 344 | * json and an exception which will be null on success. 345 | * @return {tdl.io.LoadInfo} A LoadInfo to track progress. 346 | */ 347 | tdl.io.loadJSON = function(url, callback) { 348 | var error = 'loadJSON failed to load url "' + url + '"'; 349 | var request; 350 | if (window.XMLHttpRequest) { 351 | request = new XMLHttpRequest(); 352 | if (request.overrideMimeType) { 353 | request.overrideMimeType('text/plain'); 354 | } 355 | } else if (window.ActiveXObject) { 356 | request = new ActiveXObject('MSXML2.XMLHTTP.3.0'); 357 | } else { 358 | throw 'XMLHttpRequest is disabled'; 359 | } 360 | var loadInfo = tdl.io.createLoadInfo(request, false); 361 | request.open('GET', url, true); 362 | var finish = function() { 363 | if (request.readyState == 4) { 364 | var json = undefined; 365 | // HTTP reports success with a 200 status. The file protocol reports 366 | // success with zero. HTTP does not use zero as a status code (they 367 | // start at 100). 368 | // https://developer.mozilla.org/En/Using_XMLHttpRequest 369 | var success = request.status == 200 || request.status == 0; 370 | if (success) { 371 | try { 372 | json = JSON.parse(request.responseText); 373 | } catch (e) { 374 | success = false; 375 | } 376 | } 377 | loadInfo.finish(); 378 | callback(json, success ? null : 'could not load: ' + url); 379 | } 380 | }; 381 | try { 382 | request.onreadystatechange = finish; 383 | request.send(null); 384 | } catch (e) { 385 | callback(null, 'could not load: ' + url); 386 | } 387 | return loadInfo; 388 | }; 389 | 390 | /** 391 | * Sends an object. This function is asynchronous. 392 | * @param {string} url The url of the external file. 393 | * @param {function(jsonObject, *): void} callback A callback passed the loaded 394 | * json and an exception which will be null on success. 395 | * @return {tdl.io.LoadInfo} A LoadInfo to track progress. 396 | */ 397 | tdl.io.sendJSON = function(url, jsonObject, callback) { 398 | var error = 'sendJSON failed to load url "' + url + '"'; 399 | var request; 400 | if (window.XMLHttpRequest) { 401 | request = new XMLHttpRequest(); 402 | if (request.overrideMimeType) { 403 | request.overrideMimeType('text/plain'); 404 | } 405 | } else if (window.ActiveXObject) { 406 | request = new ActiveXObject('MSXML2.XMLHTTP.3.0'); 407 | } else { 408 | throw 'XMLHttpRequest is disabled'; 409 | } 410 | var loadInfo = tdl.io.createLoadInfo(request, false); 411 | request.open('POST', url, true); 412 | var js = JSON.stringify(jsonObject); 413 | var finish = function() { 414 | if (request.readyState == 4) { 415 | var json = undefined; 416 | // HTTP reports success with a 200 status. The file protocol reports 417 | // success with zero. HTTP does not use zero as a status code (they 418 | // start at 100). 419 | // https://developer.mozilla.org/En/Using_XMLHttpRequest 420 | var success = request.status == 200 || request.status == 0; 421 | if (success) { 422 | try { 423 | json = JSON.parse(request.responseText); 424 | } catch (e) { 425 | success = false; 426 | } 427 | } 428 | loadInfo.finish(); 429 | callback(json, success ? null : 'could not load: ' + url); 430 | } 431 | }; 432 | try { 433 | request.onreadystatechange = finish; 434 | request.setRequestHeader("Content-type", "application/json"); 435 | request.send(js); 436 | } catch (e) { 437 | callback(null, 'could not load: ' + url); 438 | } 439 | return loadInfo; 440 | }; 441 | 442 | return tdl.io; 443 | }); 444 | -------------------------------------------------------------------------------- /tdl/quaternions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /** 33 | * @fileoverview This file contains various functions for quaternion arithmetic 34 | * and converting between rotation matrices and quaternions. It adds them to 35 | * the "quaternions" module on the tdl object. Javascript arrays with 36 | * four entries are used to represent quaternions, and functions are provided 37 | * for doing operations on those. 38 | * 39 | * Operations are done assuming quaternions are of the form: 40 | * `q[0] + q[1]i + q[2]j + q[3]k` and using the hamiltonian 41 | * rules for multiplication as described on Brougham Bridge: 42 | * `i^2 = j^2 = k^2 = ijk = -1`. 43 | * 44 | */ 45 | 46 | define(['./base-rs'], function(BaseRS) { 47 | 48 | tdl.provide('tdl.quaternions'); 49 | /** 50 | * A Module for quaternion math. 51 | * @namespace 52 | */ 53 | tdl.quaternions = tdl.quaternions || {}; 54 | 55 | /** 56 | * A Quaternion. 57 | * @typedef {number[]} tdl.quaternions.Quaternion 58 | */ 59 | 60 | /** 61 | * Quickly determines if the object a is a scalar or a quaternion; 62 | * assumes that the argument is either a number (scalar), or an array of 63 | * numbers. 64 | * @param {(number|tdl.quaternions.Quaternion)} a A number or array the type 65 | * of which is in question. 66 | * @return {string} Either the string 'Scalar' or 'Quaternion'. 67 | */ 68 | tdl.quaternions.mathType = function(a) { 69 | if (typeof(a) === 'number') 70 | return 'Scalar'; 71 | return 'Quaternion'; 72 | }; 73 | 74 | /** 75 | * Creates an identity quaternion. 76 | * @return {tdl.quaternions.Quaternion} The identity quaternion. 77 | */ 78 | tdl.quaternions.identity = function() { 79 | return [ 0, 0, 0, 1 ]; 80 | }; 81 | 82 | /** 83 | * Copies a quaternion. 84 | * @param {tdl.quaternions.Quaternion} q The quaternion. 85 | * @return {tdl.quaternions.Quaternion} A new quaternion identical to q. 86 | */ 87 | tdl.quaternions.copy = function(q) { 88 | return q.slice(); 89 | }; 90 | 91 | /** 92 | * Negates a quaternion. 93 | * @param {tdl.quaternions.Quaternion} q The quaternion. 94 | * @return {tdl.quaternions.Quaternion} -q. 95 | */ 96 | tdl.quaternions.negative = function(q) { 97 | return [-q[0], -q[1], -q[2], -q[3]]; 98 | }; 99 | 100 | /** 101 | * Adds two Quaternions. 102 | * @param {tdl.quaternions.Quaternion} a Operand Quaternion. 103 | * @param {tdl.quaternions.Quaternion} b Operand Quaternion. 104 | * @return {tdl.quaternions.Quaternion} The sum of a and b. 105 | */ 106 | tdl.quaternions.addQuaternionQuaternion = function(a, b) { 107 | return [a[0] + b[0], 108 | a[1] + b[1], 109 | a[2] + b[2], 110 | a[3] + b[3]]; 111 | }; 112 | 113 | /** 114 | * Adds a quaternion to a scalar. 115 | * @param {tdl.quaternions.Quaternion} a Operand Quaternion. 116 | * @param {number} b Operand Scalar. 117 | * @return {tdl.quaternions.Quaternion} The sum of a and b. 118 | */ 119 | tdl.quaternions.addQuaternionScalar = function(a, b) { 120 | return a.slice(0, 3).concat(a[3] + b); 121 | }; 122 | 123 | /** 124 | * Adds a scalar to a quaternion. 125 | * @param {number} a Operand scalar. 126 | * @param {tdl.quaternions.Quaternion} b Operand quaternion. 127 | * @return {tdl.quaternions.Quaternion} The sum of a and b. 128 | */ 129 | tdl.quaternions.addScalarQuaternion = function(a, b) { 130 | return b.slice(0, 3).concat(a + b[3]); 131 | }; 132 | 133 | /** 134 | * Subtracts two quaternions. 135 | * @param {tdl.quaternions.Quaternion} a Operand quaternion. 136 | * @param {tdl.quaternions.Quaternion} b Operand quaternion. 137 | * @return {tdl.quaternions.Quaternion} The difference a - b. 138 | */ 139 | tdl.quaternions.subQuaternionQuaternion = function(a, b) { 140 | return [a[0] - b[0], 141 | a[1] - b[1], 142 | a[2] - b[2], 143 | a[3] - b[3]]; 144 | }; 145 | 146 | /** 147 | * Subtracts a scalar from a quaternion. 148 | * @param {tdl.quaternions.Quaternion} a Operand quaternion. 149 | * @param {number} b Operand scalar. 150 | * @return {tdl.quaternions.Quaternion} The difference a - b. 151 | */ 152 | tdl.quaternions.subQuaternionScalar = function(a, b) { 153 | return a.slice(0, 3).concat(a[3] - b); 154 | }; 155 | 156 | /** 157 | * Subtracts a quaternion from a scalar. 158 | * @param {number} a Operand scalar. 159 | * @param {tdl.quaternions.Quaternion} b Operand quaternion. 160 | * @return {tdl.quaternions.Quaternion} The difference a - b. 161 | */ 162 | tdl.quaternions.subScalarQuaternion = function(a, b) { 163 | return [-b[0], -b[1], -b[2], a - b[3]]; 164 | }; 165 | 166 | /** 167 | * Multiplies a scalar by a quaternion. 168 | * @param {number} k The scalar. 169 | * @param {tdl.quaternions.Quaternion} q The quaternion. 170 | * @return {tdl.quaternions.Quaternion} The product of k and q. 171 | */ 172 | tdl.quaternions.mulScalarQuaternion = function(k, q) { 173 | return [k * q[0], k * q[1], k * q[2], k * q[3]]; 174 | }; 175 | 176 | /** 177 | * Multiplies a quaternion by a scalar. 178 | * @param {tdl.quaternions.Quaternion} q The Quaternion. 179 | * @param {number} k The scalar. 180 | * @return {tdl.quaternions.Quaternion} The product of k and v. 181 | */ 182 | tdl.quaternions.mulQuaternionScalar = function(q, k) { 183 | return [k * q[0], k * q[1], k * q[2], k * q[3]]; 184 | }; 185 | 186 | /** 187 | * Multiplies two quaternions. 188 | * @param {tdl.quaternions.Quaternion} a Operand quaternion. 189 | * @param {tdl.quaternions.Quaternion} b Operand quaternion. 190 | * @return {tdl.quaternions.Quaternion} The quaternion product a * b. 191 | */ 192 | tdl.quaternions.mulQuaternionQuaternion = function(a, b) { 193 | var aX = a[0]; 194 | var aY = a[1]; 195 | var aZ = a[2]; 196 | var aW = a[3]; 197 | var bX = b[0]; 198 | var bY = b[1]; 199 | var bZ = b[2]; 200 | var bW = b[3]; 201 | 202 | return [ 203 | aW * bX + aX * bW + aY * bZ - aZ * bY, 204 | aW * bY + aY * bW + aZ * bX - aX * bZ, 205 | aW * bZ + aZ * bW + aX * bY - aY * bX, 206 | aW * bW - aX * bX - aY * bY - aZ * bZ]; 207 | }; 208 | 209 | /** 210 | * Divides two quaternions; assumes the convention that a/b = a*(1/b). 211 | * @param {tdl.quaternions.Quaternion} a Operand quaternion. 212 | * @param {tdl.quaternions.Quaternion} b Operand quaternion. 213 | * @return {tdl.quaternions.Quaternion} The quaternion quotient a / b. 214 | */ 215 | tdl.quaternions.divQuaternionQuaternion = function(a, b) { 216 | var aX = a[0]; 217 | var aY = a[1]; 218 | var aZ = a[2]; 219 | var aW = a[3]; 220 | var bX = b[0]; 221 | var bY = b[1]; 222 | var bZ = b[2]; 223 | var bW = b[3]; 224 | 225 | var d = 1 / (bW * bW + bX * bX + bY * bY + bZ * bZ); 226 | return [ 227 | (aX * bW - aW * bX - aY * bZ + aZ * bY) * d, 228 | (aX * bZ - aW * bY + aY * bW - aZ * bX) * d, 229 | (aY * bX + aZ * bW - aW * bZ - aX * bY) * d, 230 | (aW * bW + aX * bX + aY * bY + aZ * bZ) * d]; 231 | }; 232 | 233 | /** 234 | * Divides a Quaternion by a scalar. 235 | * @param {tdl.quaternions.Quaternion} q The quaternion. 236 | * @param {number} k The scalar. 237 | * @return {tdl.quaternions.Quaternion} q The quaternion q divided by k. 238 | */ 239 | tdl.quaternions.divQuaternionScalar = function(q, k) { 240 | return [q[0] / k, q[1] / k, q[2] / k, q[3] / k]; 241 | }; 242 | 243 | /** 244 | * Divides a scalar by a quaternion. 245 | * @param {number} a Operand scalar. 246 | * @param {tdl.quaternions.Quaternion} b Operand quaternion. 247 | * @return {tdl.quaternions.Quaternion} The quaternion product. 248 | */ 249 | tdl.quaternions.divScalarQuaternion = function(a, b) { 250 | var b0 = b[0]; 251 | var b1 = b[1]; 252 | var b2 = b[2]; 253 | var b3 = b[3]; 254 | 255 | var d = 1 / (b0 * b0 + b1 * b1 + b2 * b2 + b3 * b3); 256 | return [-a * b0 * d, -a * b1 * d, -a * b2 * d, a * b3 * d]; 257 | }; 258 | 259 | /** 260 | * Computes the multiplicative inverse of a quaternion. 261 | * @param {tdl.quaternions.Quaternion} q The quaternion. 262 | * @return {tdl.quaternions.Quaternion} The multiplicative inverse of q. 263 | */ 264 | tdl.quaternions.inverse = function(q) { 265 | var q0 = q[0]; 266 | var q1 = q[1]; 267 | var q2 = q[2]; 268 | var q3 = q[3]; 269 | 270 | var d = 1 / (q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); 271 | return [-q0 * d, -q1 * d, -q2 * d, q3 * d]; 272 | }; 273 | 274 | /** 275 | * Multiplies two objects which are either scalars or quaternions. 276 | * @param {(tdl.quaternions.Quaternion|number)} a Operand. 277 | * @param {(tdl.quaternions.Quaternion|number)} b Operand. 278 | * @return {(tdl.quaternions.Quaternion|number)} The product of a and b. 279 | */ 280 | tdl.quaternions.mul = function(a, b) { 281 | return tdl.quaternions['mul' + tdl.quaternions.mathType(a) + 282 | tdl.quaternions.mathType(b)](a, b); 283 | }; 284 | 285 | /** 286 | * Divides two objects which are either scalars or quaternions. 287 | * @param {(tdl.quaternions.Quaternion|number)} a Operand. 288 | * @param {(tdl.quaternions.Quaternion|number)} b Operand. 289 | * @return {(tdl.quaternions.Quaternion|number)} The quotient of a and b. 290 | */ 291 | tdl.quaternions.div = function(a, b) { 292 | return tdl.quaternions['div' + tdl.quaternions.mathType(a) + 293 | tdl.quaternions.mathType(b)](a, b); 294 | }; 295 | 296 | /** 297 | * Adds two objects which are either scalars or quaternions. 298 | * @param {(tdl.quaternions.Quaternion|number)} a Operand. 299 | * @param {(tdl.quaternions.Quaternion|number)} b Operand. 300 | * @return {(tdl.quaternions.Quaternion|number)} The sum of a and b. 301 | */ 302 | tdl.quaternions.add = function(a, b) { 303 | return tdl.quaternions['add' + tdl.quaternions.mathType(a) + 304 | tdl.quaternions.mathType(b)](a, b); 305 | }; 306 | 307 | /** 308 | * Subtracts two objects which are either scalars or quaternions. 309 | * @param {(tdl.quaternions.Quaternion|number)} a Operand. 310 | * @param {(tdl.quaternions.Quaternion|number)} b Operand. 311 | * @return {(tdl.quaternions.Quaternion|number)} The difference of a and b. 312 | */ 313 | tdl.quaternions.sub = function(a, b) { 314 | return tdl.quaternions['sub' + tdl.quaternions.mathType(a) + 315 | tdl.quaternions.mathType(b)](a, b); 316 | }; 317 | 318 | /** 319 | * Computes the length of a Quaternion, i.e. the square root of the 320 | * sum of the squares of the coefficients. 321 | * @param {tdl.quaternions.Quaternion} a The Quaternion. 322 | * @return {number} The length of a. 323 | */ 324 | tdl.quaternions.length = function(a) { 325 | return Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]); 326 | }; 327 | 328 | /** 329 | * Computes the square of the length of a quaternion, i.e. the sum of the 330 | * squares of the coefficients. 331 | * @param {tdl.quaternions.Quaternion} a The quaternion. 332 | * @return {number} The square of the length of a. 333 | */ 334 | tdl.quaternions.lengthSquared = function(a) { 335 | return a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]; 336 | }; 337 | 338 | /** 339 | * Divides a Quaternion by its length and returns the quotient. 340 | * @param {tdl.quaternions.Quaternion} a The Quaternion. 341 | * @return {tdl.quaternions.Quaternion} A unit length quaternion pointing in 342 | * the same direction as a. 343 | */ 344 | tdl.quaternions.normalize = function(a) { 345 | var d = 1 / Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]); 346 | return [a[0] * d, a[1] * d, a[2] * d, a[3] * d]; 347 | }; 348 | 349 | /** 350 | * Computes the conjugate of the given quaternion. 351 | * @param {tdl.quaternions.Quaternion} q The quaternion. 352 | * @return {tdl.quaternions.Quaternion} The conjugate of q. 353 | */ 354 | tdl.quaternions.conjugate = function(q) { 355 | return [-q[0], -q[1], -q[2], q[3]]; 356 | }; 357 | 358 | 359 | /** 360 | * Creates a quaternion which rotates around the x-axis by the given angle. 361 | * @param {number} angle The angle by which to rotate (in radians). 362 | * @return {tdl.quaternions.Quaternion} The quaternion. 363 | */ 364 | tdl.quaternions.rotationX = function(angle) { 365 | return [Math.sin(angle / 2), 0, 0, Math.cos(angle / 2)]; 366 | }; 367 | 368 | /** 369 | * Creates a quaternion which rotates around the y-axis by the given angle. 370 | * @param {number} angle The angle by which to rotate (in radians). 371 | * @return {tdl.quaternions.Quaternion} The quaternion. 372 | */ 373 | tdl.quaternions.rotationY = function(angle) { 374 | return [0, Math.sin(angle / 2), 0, Math.cos(angle / 2)]; 375 | }; 376 | 377 | /** 378 | * Creates a quaternion which rotates around the z-axis by the given angle. 379 | * @param {number} angle The angle by which to rotate (in radians). 380 | * @return {tdl.quaternions.Quaternion} The quaternion. 381 | */ 382 | tdl.quaternions.rotationZ = function(angle) { 383 | return [0, 0, Math.sin(angle / 2), Math.cos(angle / 2)]; 384 | }; 385 | 386 | /** 387 | * Creates a quaternion which rotates around the given axis by the given 388 | * angle. 389 | * @param {tdl.math.Vector3} axis The axis about which to rotate. 390 | * @param {number} angle The angle by which to rotate (in radians). 391 | * @return {tdl.quaternions.Quaternion} A quaternion which rotates angle 392 | * radians around the axis. 393 | */ 394 | tdl.quaternions.axisRotation = function(axis, angle) { 395 | var d = 1 / Math.sqrt(axis[0] * axis[0] + 396 | axis[1] * axis[1] + 397 | axis[2] * axis[2]); 398 | var sin = Math.sin(angle / 2); 399 | var cos = Math.cos(angle / 2); 400 | return [sin * axis[0] * d, sin * axis[1] * d, sin * axis[2] * d, cos]; 401 | }; 402 | 403 | /** 404 | * Computes a 4-by-4 rotation matrix (with trivial translation component) 405 | * given a quaternion. We assume the convention that to rotate a vector v by 406 | * a quaternion r means to express that vector as a quaternion q by letting 407 | * `q = [v[0], v[1], v[2], 0]` and then obtain the rotated 408 | * vector by evaluating the expression `(r * q) / r`. 409 | * @param {tdl.quaternions.Quaternion} q The quaternion. 410 | * @return {tdl.math.Matrix4} A 4-by-4 rotation matrix. 411 | */ 412 | tdl.quaternions.quaternionToRotation = function(q) { 413 | var qX = q[0]; 414 | var qY = q[1]; 415 | var qZ = q[2]; 416 | var qW = q[3]; 417 | 418 | var qWqW = qW * qW; 419 | var qWqX = qW * qX; 420 | var qWqY = qW * qY; 421 | var qWqZ = qW * qZ; 422 | var qXqW = qX * qW; 423 | var qXqX = qX * qX; 424 | var qXqY = qX * qY; 425 | var qXqZ = qX * qZ; 426 | var qYqW = qY * qW; 427 | var qYqX = qY * qX; 428 | var qYqY = qY * qY; 429 | var qYqZ = qY * qZ; 430 | var qZqW = qZ * qW; 431 | var qZqX = qZ * qX; 432 | var qZqY = qZ * qY; 433 | var qZqZ = qZ * qZ; 434 | 435 | var d = qWqW + qXqX + qYqY + qZqZ; 436 | 437 | return [ 438 | (qWqW + qXqX - qYqY - qZqZ) / d, 439 | 2 * (qWqZ + qXqY) / d, 440 | 2 * (qXqZ - qWqY) / d, 0, 441 | 442 | 2 * (qXqY - qWqZ) / d, 443 | (qWqW - qXqX + qYqY - qZqZ) / d, 444 | 2 * (qWqX + qYqZ) / d, 0, 445 | 446 | 2 * (qWqY + qXqZ) / d, 447 | 2 * (qYqZ - qWqX) / d, 448 | (qWqW - qXqX - qYqY + qZqZ) / d, 0, 449 | 450 | 0, 0, 0, 1]; 451 | }; 452 | 453 | /** 454 | * Computes a quaternion whose rotation is equivalent to the given matrix. 455 | * @param {(tdl.math.Matrix4|tdl.math.Matrix3)} m A 3-by-3 or 4-by-4 456 | * rotation matrix. 457 | * @return {tdl.quaternions.Quaternion} A quaternion q such that 458 | * quaternions.quaternionToRotation(q) is m. 459 | */ 460 | tdl.quaternions.rotationToQuaternion = function(m) { 461 | var u; 462 | var v; 463 | var w; 464 | 465 | // Choose u, v, and w such that u is the index of the biggest diagonal entry 466 | // of m, and u v w is an even permutation of 0 1 and 2. 467 | if (m[0*4+0] > m[1*4+1] && m[0*4+0] > m[2*4+2]) { 468 | u = 0; 469 | v = 1; 470 | w = 2; 471 | } else if (m[1*4+1] > m[0*4+0] && m[1*4+1] > m[2*4+2]) { 472 | u = 1; 473 | v = 2; 474 | w = 0; 475 | } else { 476 | u = 2; 477 | v = 0; 478 | w = 1; 479 | } 480 | 481 | var r = Math.sqrt(1 + m[u*4+u] - m[v*4+v] - m[w*4+w]); 482 | var q = []; 483 | q[u] = 0.5 * r; 484 | q[v] = 0.5 * (m[v*4+u] + m[u*4+v]) / r; 485 | q[w] = 0.5 * (m[u*4+w] + m[w*4+u]) / r; 486 | q[3] = 0.5 * (m[v*4+w] - m[w*4+v]) / r; 487 | 488 | return q; 489 | }; 490 | 491 | return tdl.quaternions; 492 | }); 493 | 494 | -------------------------------------------------------------------------------- /tdl/programs.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains objects to deal with WebGL 35 | * programs. 36 | */ 37 | define( 38 | [ './base-rs', 39 | './log', 40 | './string', 41 | './webgl', 42 | ], function( 43 | BaseRS, 44 | Log, 45 | Strings, 46 | WebGL) { 47 | 48 | tdl.provide('tdl.programs'); 49 | /** 50 | * A module for programs. 51 | * @namespace 52 | */ 53 | tdl.programs = tdl.programs || {}; 54 | 55 | /** 56 | * Loads a program from script tags. 57 | * @param {string} vertexShaderId The id of the script tag that contains the 58 | * vertex shader source. 59 | * @param {string} fragmentShaderId The id of the script tag that contains the 60 | * fragment shader source. 61 | * @return {tdl.programs.Program} The created program. 62 | */ 63 | tdl.programs.loadProgramFromScriptTags = function( 64 | vertexShaderId, fragmentShaderId) { 65 | var vertElem = document.getElementById(vertexShaderId); 66 | var fragElem = document.getElementById(fragmentShaderId); 67 | if (!vertElem) { 68 | throw("Can't find vertex program tag: " + vertexShaderId); 69 | } 70 | if (!fragElem) { 71 | throw("Can't find fragment program tag: " + fragmentShaderId); 72 | } 73 | return tdl.programs.loadProgram( 74 | document.getElementById(vertexShaderId).text, 75 | document.getElementById(fragmentShaderId).text); 76 | }; 77 | 78 | tdl.programs.makeProgramId = function(vertexShader, fragmentShader) { 79 | return vertexShader + fragmentShader; 80 | }; 81 | 82 | /** 83 | * Loads a program. 84 | * @param {string} vertexShader The vertex shader source. 85 | * @param {string} fragmentShader The fragment shader source. 86 | * @param {function(error)) opt_asyncCallback. Called with 87 | * undefined if success or string if failure. 88 | * @return {tdl.programs.Program} The created program. 89 | */ 90 | tdl.programs.loadProgram = function(vertexShader, fragmentShader, opt_asyncCallback) { 91 | var id = tdl.programs.makeProgramId(vertexShader, fragmentShader); 92 | tdl.programs.init_(); 93 | var program = gl.tdl.programs.programDB[id]; 94 | if (program) { 95 | if (opt_asyncCallback) { 96 | setTimeout(function() { opt_asyncCallback(); }, 1); 97 | } 98 | return program; 99 | } 100 | try { 101 | program = new tdl.programs.Program(vertexShader, fragmentShader, opt_asyncCallback); 102 | } catch (e) { 103 | tdl.error(e); 104 | return null; 105 | } 106 | if (!opt_asyncCallback) { 107 | gl.tdl.programs.programDB[id] = program; 108 | } 109 | return program; 110 | }; 111 | 112 | /** 113 | * A object to manage a WebGLProgram. 114 | * @constructor 115 | * @param {string} vertexShader The vertex shader source. 116 | * @param {string} fragmentShader The fragment shader source. 117 | * @param {function(error)) opt_asyncCallback. Called with 118 | * undefined if success or string if failure. 119 | */ 120 | tdl.programs.Program = function(vertexShader, fragmentShader, opt_asyncCallback) { 121 | var that = this; 122 | this.programId = tdl.programs.makeProgramId(vertexShader, fragmentShader); 123 | this.asyncCallback = opt_asyncCallback; 124 | 125 | var shaderId; 126 | var program; 127 | var vs; 128 | var fs; 129 | 130 | /** 131 | * Loads a shader. 132 | * @private 133 | * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. 134 | * @param {string} shaderSource The shader source. 135 | * @param {number} shaderType The type of shader. 136 | * @return {WebGLShader} The created shader. 137 | */ 138 | var loadShader = function(gl, shaderSource, shaderType) { 139 | shaderId = shaderSource + shaderType; 140 | tdl.programs.init_(); 141 | var shader = gl.tdl.programs.shaderDB[shaderId]; 142 | if (shader) { 143 | return shader; 144 | } 145 | 146 | // Create the shader object 147 | var shader = gl.createShader(shaderType); 148 | 149 | // Load the shader source 150 | gl.shaderSource(shader, shaderSource); 151 | 152 | // Compile the shader 153 | gl.compileShader(shader); 154 | 155 | // Check the compile status 156 | if (!that.asyncCallback) { 157 | checkShader(shader); 158 | } 159 | return shader; 160 | } 161 | 162 | var checkShader = function(shader) { 163 | var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); 164 | if (!compiled && !gl.isContextLost()) { 165 | // Something went wrong during compilation; get the error 166 | tdl.programs.lastError = gl.getShaderInfoLog(shader); 167 | gl.deleteShader(shader); 168 | throw("*** Error compiling shader :" + tdl.programs.lastError); 169 | } 170 | gl.tdl.programs.shaderDB[shaderId] = shader; 171 | }; 172 | 173 | /** 174 | * Loads shaders from script tags, creates a program, attaches the shaders and 175 | * links. 176 | * @private 177 | * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. 178 | * @param {string} vertexShader The vertex shader. 179 | * @param {string} fragmentShader The fragment shader. 180 | * @return {WebGLProgram} The created program. 181 | */ 182 | var loadProgram = function(gl, vertexShader, fragmentShader) { 183 | var e; 184 | try { 185 | vs = loadShader(gl, vertexShader, gl.VERTEX_SHADER); 186 | fs = loadShader(gl, fragmentShader, gl.FRAGMENT_SHADER); 187 | program = gl.createProgram(); 188 | gl.attachShader(program, vs); 189 | gl.attachShader(program, fs); 190 | linkProgram(gl, program); 191 | } catch (e) { 192 | deleteAll(e); 193 | } 194 | return program; 195 | }; 196 | 197 | var deleteAll = function(e) { 198 | if (vs) { gl.deleteShader(vs) } 199 | if (fs) { gl.deleteShader(fs) } 200 | if (program) { gl.deleteProgram(program) } 201 | throw e; 202 | }; 203 | 204 | /** 205 | * Links a WebGL program, throws if there are errors. 206 | * @private 207 | * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. 208 | * @param {WebGLProgram} program The WebGLProgram to link. 209 | */ 210 | var linkProgram = function(gl, program) { 211 | // Link the program 212 | gl.linkProgram(program); 213 | 214 | // Check the link status 215 | if (!that.asyncCallback) { 216 | checkProgram(program); 217 | } 218 | }; 219 | 220 | var checkProgram = function(program) { 221 | var linked = gl.getProgramParameter(program, gl.LINK_STATUS); 222 | if (!linked && !gl.isContextLost()) { 223 | // something went wrong with the link 224 | tdl.programs.lastError = gl.getProgramInfoLog (program); 225 | throw("*** Error in program linking:" + tdl.programs.lastError); 226 | } 227 | }; 228 | 229 | // Compile shaders 230 | var program = loadProgram(gl, vertexShader, fragmentShader); 231 | if (!program && !gl.isContextLost()) { 232 | throw ("could not compile program"); 233 | } 234 | 235 | // TODO(gman): remove the need for this. 236 | function flatten(array){ 237 | var flat = []; 238 | for (var i = 0, l = array.length; i < l; i++) { 239 | var type = Object.prototype.toString.call(array[i]).split(' ').pop().split(']').shift().toLowerCase(); 240 | if (type) { flat = flat.concat(/^(array|collection|arguments|object)$/.test(type) ? flatten(array[i]) : array[i]); } 241 | } 242 | return flat; 243 | } 244 | 245 | function createSetters(program) { 246 | // Look up attribs. 247 | var attribs = { 248 | }; 249 | // Also make a plain table of the locs. 250 | var attribLocs = { 251 | }; 252 | 253 | function createAttribSetter(info, index) { 254 | if (info.size != 1) { 255 | throw("arrays of attribs not handled"); 256 | } 257 | return function(b) { 258 | gl.bindBuffer(gl.ARRAY_BUFFER, b.buffer()); 259 | gl.enableVertexAttribArray(index); 260 | gl.vertexAttribPointer( 261 | index, b.numComponents(), b.type(), b.normalize(), b.stride(), b.offset()); 262 | }; 263 | } 264 | 265 | var numAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); 266 | for (var ii = 0; ii < numAttribs; ++ii) { 267 | var info = gl.getActiveAttrib(program, ii); 268 | if (!info) { 269 | break; 270 | } 271 | var name = info.name; 272 | if (tdl.string.endsWith(name, "[0]")) { 273 | name = name.substr(0, name.length - 3); 274 | } 275 | var index = gl.getAttribLocation(program, info.name); 276 | attribs[name] = createAttribSetter(info, index); 277 | attribLocs[name] = index 278 | } 279 | 280 | // Look up uniforms 281 | var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); 282 | var uniforms = { 283 | }; 284 | var textureUnit = 0; 285 | 286 | function createUniformSetter(info) { 287 | var loc = gl.getUniformLocation(program, info.name); 288 | var type = info.type; 289 | if (info.size > 1 && tdl.string.endsWith(info.name, "[0]")) { 290 | // It's an array. 291 | if (type == gl.FLOAT) 292 | return function(v) { gl.uniform1fv(loc, v); }; 293 | if (type == gl.FLOAT_VEC2) 294 | return function(v) { gl.uniform2fv(loc, v); }; 295 | if (type == gl.FLOAT_VEC3) 296 | return function(v) { gl.uniform3fv(loc, v); }; 297 | if (type == gl.FLOAT_VEC4) 298 | return function(v) { gl.uniform4fv(loc, v); }; 299 | if (type == gl.INT) 300 | return function(v) { gl.uniform1iv(loc, v); }; 301 | if (type == gl.INT_VEC2) 302 | return function(v) { gl.uniform2iv(loc, v); }; 303 | if (type == gl.INT_VEC3) 304 | return function(v) { gl.uniform3iv(loc, v); }; 305 | if (type == gl.INT_VEC4) 306 | return function(v) { gl.uniform4iv(loc, v); }; 307 | if (type == gl.BOOL) 308 | return function(v) { gl.uniform1iv(loc, v); }; 309 | if (type == gl.BOOL_VEC2) 310 | return function(v) { gl.uniform2iv(loc, v); }; 311 | if (type == gl.BOOL_VEC3) 312 | return function(v) { gl.uniform3iv(loc, v); }; 313 | if (type == gl.BOOL_VEC4) 314 | return function(v) { gl.uniform4iv(loc, v); }; 315 | if (type == gl.FLOAT_MAT2) 316 | return function(v) { gl.uniformMatrix2fv(loc, false, v); }; 317 | if (type == gl.FLOAT_MAT3) 318 | return function(v) { gl.uniformMatrix3fv(loc, false, v); }; 319 | if (type == gl.FLOAT_MAT4) 320 | return function(v) { gl.uniformMatrix4fv(loc, false, v); }; 321 | if (type == gl.SAMPLER_2D || type == gl.SAMPLER_CUBE) { 322 | var units = []; 323 | for (var ii = 0; ii < info.size; ++ii) { 324 | units.push(textureUnit++); 325 | } 326 | return function(units) { 327 | return function(v) { 328 | gl.uniform1iv(loc, units); 329 | v.forEach(function(t, index) { 330 | t.bindToUnit(units[index]); 331 | }); 332 | }; 333 | }(units); 334 | } 335 | throw ("unknown type: 0x" + type.toString(16)); 336 | } else { 337 | if (type == gl.FLOAT) 338 | return function(v) { gl.uniform1f(loc, v); }; 339 | if (type == gl.FLOAT_VEC2) 340 | return function(v) { gl.uniform2fv(loc, v); }; 341 | if (type == gl.FLOAT_VEC3) 342 | return function(v) { gl.uniform3fv(loc, v); }; 343 | if (type == gl.FLOAT_VEC4) 344 | return function(v) { gl.uniform4fv(loc, v); }; 345 | if (type == gl.INT) 346 | return function(v) { gl.uniform1i(loc, v); }; 347 | if (type == gl.INT_VEC2) 348 | return function(v) { gl.uniform2iv(loc, v); }; 349 | if (type == gl.INT_VEC3) 350 | return function(v) { gl.uniform3iv(loc, v); }; 351 | if (type == gl.INT_VEC4) 352 | return function(v) { gl.uniform4iv(loc, v); }; 353 | if (type == gl.BOOL) 354 | return function(v) { gl.uniform1i(loc, v); }; 355 | if (type == gl.BOOL_VEC2) 356 | return function(v) { gl.uniform2iv(loc, v); }; 357 | if (type == gl.BOOL_VEC3) 358 | return function(v) { gl.uniform3iv(loc, v); }; 359 | if (type == gl.BOOL_VEC4) 360 | return function(v) { gl.uniform4iv(loc, v); }; 361 | if (type == gl.FLOAT_MAT2) 362 | return function(v) { gl.uniformMatrix2fv(loc, false, v); }; 363 | if (type == gl.FLOAT_MAT3) 364 | return function(v) { gl.uniformMatrix3fv(loc, false, v); }; 365 | if (type == gl.FLOAT_MAT4) 366 | return function(v) { gl.uniformMatrix4fv(loc, false, v); }; 367 | if (type == gl.SAMPLER_2D || type == gl.SAMPLER_CUBE) { 368 | return function(unit) { 369 | return function(v) { 370 | gl.uniform1i(loc, unit); 371 | v.bindToUnit(unit); 372 | }; 373 | }(textureUnit++); 374 | } 375 | throw ("unknown type: 0x" + type.toString(16)); 376 | } 377 | } 378 | 379 | var textures = {}; 380 | 381 | for (var ii = 0; ii < numUniforms; ++ii) { 382 | var info = gl.getActiveUniform(program, ii); 383 | if (!info) { 384 | break; 385 | } 386 | name = info.name; 387 | if (tdl.string.endsWith(name, "[0]")) { 388 | name = name.substr(0, name.length - 3); 389 | } 390 | var setter = createUniformSetter(info); 391 | uniforms[name] = setter; 392 | if (info.type == gl.SAMPLER_2D || info.type == gl.SAMPLER_CUBE) { 393 | textures[name] = setter; 394 | } 395 | } 396 | 397 | that.textures = textures; 398 | that.attrib = attribs; 399 | that.attribLoc = attribLocs; 400 | that.uniform = uniforms; 401 | } 402 | createSetters(program); 403 | 404 | this.loadNewShaders = function(vertexShaderSource, fragmentShaderSource) { 405 | var program = loadProgram(gl, vertexShaderSource, fragmentShaderSource); 406 | if (!program && !gl.isContextLost()) { 407 | throw ("could not compile program"); 408 | } 409 | that.program = program; 410 | createSetters(); 411 | }; 412 | 413 | this.program = program; 414 | this.good = this.asyncCallback ? false : true; 415 | 416 | var checkLater = function() { 417 | var e; 418 | try { 419 | checkShader(vs); 420 | checkShader(fs); 421 | checkProgram(program); 422 | } catch (e) { 423 | that.asyncCallback(e.toString()); 424 | return; 425 | } 426 | gl.tdl.programs.programDB[that.programId] = this; 427 | that.asyncCallback(); 428 | }; 429 | if (this.asyncCallback) { 430 | setTimeout(checkLater, 1000); 431 | } 432 | }; 433 | 434 | tdl.programs.handleContextLost_ = function() { 435 | if (gl.tdl && gl.tdl.programs && gl.tdl.programs.shaderDB) { 436 | delete gl.tdl.programs.shaderDB; 437 | delete gl.tdl.programs.programDB; 438 | } 439 | }; 440 | 441 | tdl.programs.init_ = function() { 442 | if (!gl.tdl.programs) { 443 | gl.tdl.programs = { }; 444 | tdl.webgl.registerContextLostHandler(gl.canvas, tdl.programs.handleContextLost_, true); 445 | } 446 | if (!gl.tdl.programs.shaderDB) { 447 | gl.tdl.programs.shaderDB = { }; 448 | gl.tdl.programs.programDB = { }; 449 | } 450 | }; 451 | 452 | /** 453 | * Uses the current program (calls `gl.useProgram`) 454 | */ 455 | tdl.programs.Program.prototype.use = function() { 456 | gl.useProgram(this.program); 457 | }; 458 | 459 | //function dumpValue(msg, name, value) { 460 | // var str; 461 | // if (value.length) { 462 | // str = value[0].toString(); 463 | // for (var ii = 1; ii < value.length; ++ii) { 464 | // str += "," + value[ii]; 465 | // } 466 | // } else { 467 | // str = value.toString(); 468 | // } 469 | // tdl.log(msg + name + ": " + str); 470 | //} 471 | 472 | /** 473 | * Sets a uniform to a value 474 | * @param {string} uniform name of uniform to set 475 | * @param {*} value value that is compatible with type of 476 | * uniform. 477 | */ 478 | tdl.programs.Program.prototype.setUniform = function(uniform, value) { 479 | var func = this.uniform[uniform]; 480 | if (func) { 481 | //dumpValue("SET UNI:", uniform, value); 482 | func(value); 483 | } 484 | }; 485 | 486 | /** 487 | * Sets a bunch of uniforms 488 | * @param {Object.} uniforms uniform name value 489 | * pairs. 490 | */ 491 | tdl.programs.Program.prototype.applyUniforms = function(uniforms) { 492 | for (var uniform in uniforms) { 493 | this.setUniform(uniform, uniforms[uniform]); 494 | } 495 | }; 496 | 497 | return tdl.programs; 498 | }); 499 | 500 | -------------------------------------------------------------------------------- /tdl/base.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview Base for all tdl sample utilties. 35 | * 36 | * The main point of this module is to provide a central place to 37 | * have an init function to register an tdl namespace object because many other 38 | * modules need access to it. 39 | */ 40 | 41 | /** 42 | * A namespace for all the tdl utility libraries. 43 | * @namespace 44 | */ 45 | var tdl = tdl || {}; 46 | 47 | /** 48 | * Define this because the Google internal JSCompiler needs goog.typedef below. 49 | */ 50 | var goog = goog || {}; 51 | 52 | 53 | if (!window.Int32Array) { 54 | window.Int32Array = function() { }; 55 | window.Float32Array = function() { }; 56 | window.Uint16Array = function() { }; 57 | } 58 | 59 | /** 60 | * A macro for defining composite types. 61 | * 62 | * By assigning goog.typedef to a name, this tells Google internal JSCompiler 63 | * that this is not the name of a class, but rather it's the name of a composite 64 | * type. 65 | * 66 | * For example, 67 | * /** @type {Array|NodeList} / goog.ArrayLike = goog.typedef; 68 | * will tell JSCompiler to replace all appearances of goog.ArrayLike in type 69 | * definitions with the union of Array and NodeList. 70 | * 71 | * Does nothing in uncompiled code. 72 | */ 73 | goog.typedef = true; 74 | 75 | /** 76 | * Reference to the global context. In most cases this will be 'window'. 77 | */ 78 | tdl.global = this; 79 | 80 | /** 81 | * Some javascripts don't support __defineGetter__ or __defineSetter__ 82 | * so we define some here so at least we don't get compile errors. 83 | * We expect the initialzation code will check and complain. This stubs 84 | * are just here to make sure we can actually get to the initialization code. 85 | */ 86 | //if (!Object.prototype.__defineSetter__) { 87 | // Object.prototype.__defineSetter__ = function() {} 88 | // Object.prototype.__defineGetter__ = function() {} 89 | //} 90 | // 91 | /** 92 | * Flag used to force a function to run in the browser when it is called 93 | * from V8. 94 | * @type {boolean} 95 | */ 96 | tdl.BROWSER_ONLY = true; 97 | 98 | /** 99 | * Array of namespaces that have been provided. 100 | * @private 101 | * @type {!Array.} 102 | */ 103 | tdl.provided_ = []; 104 | 105 | /** 106 | * Creates object stubs for a namespace. When present in a file, 107 | * tdl.provide also indicates that the file defines the indicated 108 | * object. 109 | * @param {string} name name of the object that this file defines. 110 | */ 111 | tdl.provide = function(name) { 112 | // Ensure that the same namespace isn't provided twice. 113 | if (tdl.getObjectByName(name) && 114 | !tdl.implicitNamespaces_[name]) { 115 | throw 'Namespace "' + name + '" already declared.'; 116 | } 117 | 118 | var namespace = name; 119 | while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { 120 | tdl.implicitNamespaces_[namespace] = true; 121 | } 122 | 123 | tdl.exportPath_(name); 124 | tdl.provided_.push(name); 125 | }; 126 | 127 | 128 | /** 129 | * Namespaces implicitly defined by tdl.provide. For example, 130 | * tdl.provide('tdl.events.Event') implicitly declares 131 | * that 'tdl' and 'tdl.events' must be namespaces. 132 | * 133 | * @type {Object} 134 | * @private 135 | */ 136 | tdl.implicitNamespaces_ = {}; 137 | 138 | /** 139 | * Builds an object structure for the provided namespace path, 140 | * ensuring that names that already exist are not overwritten. For 141 | * example: 142 | * "a.b.c" -> a = {};a.b={};a.b.c={}; 143 | * Used by tdl.provide and tdl.exportSymbol. 144 | * @param {string} name name of the object that this file defines. 145 | * @param {Object} opt_object the object to expose at the end of the path. 146 | * @param {Object} opt_objectToExportTo The object to add the path to; default 147 | * is |tdl.global|. 148 | * @private 149 | */ 150 | tdl.exportPath_ = function(name, opt_object, opt_objectToExportTo) { 151 | var parts = name.split('.'); 152 | var cur = opt_objectToExportTo || tdl.global; 153 | var part; 154 | 155 | // Internet Explorer exhibits strange behavior when throwing errors from 156 | // methods externed in this manner. See the testExportSymbolExceptions in 157 | // base_test.html for an example. 158 | if (!(parts[0] in cur) && cur.execScript) { 159 | cur.execScript('var ' + parts[0]); 160 | } 161 | 162 | // Parentheses added to eliminate strict JS warning in Firefox. 163 | while (parts.length && (part = parts.shift())) { 164 | if (!parts.length && tdl.isDef(opt_object)) { 165 | // last part and we have an object; use it. 166 | cur[part] = opt_object; 167 | } else if (cur[part]) { 168 | cur = cur[part]; 169 | } else { 170 | cur = cur[part] = {}; 171 | } 172 | } 173 | }; 174 | 175 | 176 | /** 177 | * Returns an object based on its fully qualified external name. If you are 178 | * using a compilation pass that renames property names beware that using this 179 | * function will not find renamed properties. 180 | * 181 | * @param {string} name The fully qualified name. 182 | * @param {Object} opt_obj The object within which to look; default is 183 | * |tdl.global|. 184 | * @return {Object} The object or, if not found, null. 185 | */ 186 | tdl.getObjectByName = function(name, opt_obj) { 187 | var parts = name.split('.'); 188 | var cur = opt_obj || tdl.global; 189 | for (var pp = 0; pp < parts.length; ++pp) { 190 | var part = parts[pp]; 191 | if (cur[part]) { 192 | cur = cur[part]; 193 | } else { 194 | return null; 195 | } 196 | } 197 | return cur; 198 | }; 199 | 200 | 201 | /** 202 | * Implements a system for the dynamic resolution of dependencies. 203 | * @param {string} rule Rule to include, in the form tdl.package.part. 204 | */ 205 | tdl.require = function(rule) { 206 | // TODO(gman): For some unknown reason, when we call 207 | // tdl.util.getScriptTagText_ it calls 208 | // document.getElementsByTagName('script') and for some reason the scripts do 209 | // not always show up. Calling it here seems to fix that as long as we 210 | // actually ask for the length, at least in FF 3.5.1 It would be nice to 211 | // figure out why. 212 | var dummy = document.getElementsByTagName('script').length; 213 | // if the object already exists we do not need do do anything 214 | if (tdl.getObjectByName(rule)) { 215 | return; 216 | } 217 | var path = tdl.getPathFromRule_(rule); 218 | if (path) { 219 | tdl.included_[path] = true; 220 | tdl.writeScripts_(); 221 | } else { 222 | throw new Error('tdl.require could not find: ' + rule); 223 | } 224 | }; 225 | 226 | 227 | /** 228 | * Path for included scripts. 229 | * @type {string} 230 | */ 231 | tdl.basePath = ''; 232 | 233 | 234 | /** 235 | * Object used to keep track of urls that have already been added. This 236 | * record allows the prevention of circular dependencies. 237 | * @type {Object} 238 | * @private 239 | */ 240 | tdl.included_ = {}; 241 | 242 | 243 | /** 244 | * This object is used to keep track of dependencies and other data that is 245 | * used for loading scripts. 246 | * @private 247 | * @type {Object} 248 | */ 249 | tdl.dependencies_ = { 250 | visited: {}, // used when resolving dependencies to prevent us from 251 | // visiting the file twice. 252 | written: {} // used to keep track of script files we have written. 253 | }; 254 | 255 | 256 | /** 257 | * Tries to detect the base path of the tdl-base.js script that 258 | * bootstraps the tdl libraries. 259 | * @private 260 | */ 261 | tdl.findBasePath_ = function() { 262 | var doc = tdl.global.document; 263 | if (typeof doc == 'undefined') { 264 | return; 265 | } 266 | if (tdl.global.BASE_PATH) { 267 | tdl.basePath = tdl.global.BASE_PATH; 268 | return; 269 | } else { 270 | // HACKHACK to hide compiler warnings :( 271 | tdl.global.BASE_PATH = null; 272 | } 273 | var expectedBase = 'tdl/base.js'; 274 | var scripts = doc.getElementsByTagName('script'); 275 | for (var script, i = 0; script = scripts[i]; i++) { 276 | var src = script.src; 277 | var l = src.length; 278 | if (src.substr(l - expectedBase.length) == expectedBase) { 279 | tdl.basePath = src.substr(0, l - expectedBase.length); 280 | return; 281 | } 282 | } 283 | }; 284 | 285 | 286 | /** 287 | * Writes a script tag if, and only if, that script hasn't already been added 288 | * to the document. (Must be called at execution time.) 289 | * @param {string} src Script source. 290 | * @private 291 | */ 292 | tdl.writeScriptTag_ = function(src) { 293 | var doc = tdl.global.document; 294 | if (typeof doc != 'undefined' && 295 | !tdl.dependencies_.written[src]) { 296 | tdl.dependencies_.written[src] = true; 297 | var html = '