├── .gitignore ├── LICENSE.md ├── README.md ├── cli ├── Cargo.toml └── src │ └── main.rs ├── example_project ├── basic_sample │ ├── index.html │ ├── logo.png │ ├── test.css │ ├── test.glsl │ └── test.js ├── codestage.toml ├── electricflower │ ├── electricflower.jpg │ ├── flower.js │ ├── index.html │ └── postprocess.js ├── halo │ ├── halo.jpg │ ├── halo.js │ ├── index.html │ └── voronoi.js ├── khronos │ └── webgl-debug.js ├── logo.png ├── logo4.png ├── meta.png ├── readme │ └── index.html ├── spacerocks │ ├── assets │ │ ├── f.png │ │ ├── height-map.png │ │ ├── rock-color.png │ │ ├── rock-nmap.png │ │ ├── rocks.png │ │ ├── shield-gradient.png │ │ ├── shield-noise.png │ │ ├── space_bk.jpg │ │ ├── space_dn.jpg │ │ ├── space_fr.jpg │ │ ├── space_lf.jpg │ │ ├── space_rt.jpg │ │ ├── space_up.jpg │ │ └── white-square-outline.png │ ├── index.html │ └── spacerocks.jpg └── tdl │ ├── base.js │ ├── buffers.js │ ├── clock.js │ ├── error.jpg │ ├── fast.js │ ├── fps.js │ ├── framebuffers.js │ ├── fullscreen.js │ ├── io.js │ ├── loader.js │ ├── log.js │ ├── math.js │ ├── misc.js │ ├── models.js │ ├── particles.js │ ├── primitives.js │ ├── programs.js │ ├── quaternions.js │ ├── screenshot.js │ ├── shader.js │ ├── string.js │ ├── sync.js │ ├── textures.js │ └── webgl.js ├── frontend ├── .gitignore ├── build.sh ├── code.html ├── package.json ├── package_monaco.py ├── public │ └── logo.svg ├── src │ ├── App.jsx │ ├── App.module.css │ ├── MenuItem.jsx │ ├── MenuItem.module.css │ ├── assets │ │ ├── CodeStage.json │ │ └── logo2.png │ ├── index.css │ └── index.jsx └── vite.config.js ├── logo.png ├── logo.psd ├── logo.svg ├── logo2.psd ├── logo3.psd └── meta.psd /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | frontend/dist 4 | cli/target 5 | cli/dist 6 | example_project/dist 7 | Cargo.lock 8 | package-lock.json 9 | # local env files 10 | .env.local 11 | .env.*.local 12 | 13 | # Log files 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | pnpm-debug.log* 18 | 19 | # Editor directories and files 20 | .idea 21 | .vscode 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Shi Yan 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | https://user-images.githubusercontent.com/326807/201398785-5eaf402e-ee41-46c4-bb7b-5ccc6a9861db.mp4 2 | 3 | [![crates.io](https://img.shields.io/crates/v/codestage.svg)](https://crates.io/crates/codestage) 4 | 5 | ![logo](https://raw.githubusercontent.com/shi-yan/codestage/master/logo.png) 6 | 7 | CodeStage is a static site generator to create javascript playgrounds. I implemented this to generate code samples for [my WebGPU tutorial](https://shi-yan.github.io/WebGPUUnleashed/) project. CodeStage was inspired by the following sites: 8 | 9 | [Monaco](https://microsoft.github.io/monaco-editor/playground.html) | [WebGPU samples](https://austin-eng.com/webgpu-samples) | [Bauble](https://bauble.studio) | [Goplay](https://goplay.space) 10 | 11 | All these sites seem to build their own solution. CodeStage, on the other hand, is a free and reusable solution. 12 | 13 | ### Key features 14 | 15 | * Mutable code samples, easy to conduct experiments on 16 | * Samples can be navigated by a menu supporting nested items 17 | * No backend is needed 18 | 19 | To see a demo of a deployed CodeStage site: [Demo](https://shi-yan.github.io/codestage/). Some samples used in this demo come from [webglsamples](https://github.com/webglsamples/webglsamples.github.io). 20 | 21 | ## Installation 22 | ``` 23 | cargo install codestage --version 0.1.1-alpha.2 24 | ``` 25 | 26 | ## Usage 27 | 28 | Create a project folder and a project file [codestage.toml](https://github.com/shi-yan/codestage/blob/master/example_project/codestage.toml) 29 | 30 | ```toml 31 | # Title of the project (must have). 32 | title = "CodeStage example" 33 | # Link to the repository (optional). 34 | repo = "xxx" 35 | # If not deployed under the root directory, this will be needed. The first slash is required (optional). 36 | prefix = "/codestage" 37 | # Specify the output folder (optional). 38 | target = "dist" 39 | # Link to the deployed site, this will be used for meta tags (optional). 40 | url = "https://shi-yan.github.io/codestage/" 41 | # Image used for meta tags (optional). 42 | meta_image = "meta.png" 43 | # Description used for meta tags (optional). 44 | description = """ 45 | CodeStage is a static site generator to build JS playground demos.""" 46 | # Utility folders are shared by all samples in the project (optional). 47 | utilities = [ "utility_folder_1", "utility_folder_2" ] 48 | # An optional folder to put a readme, the index.html inside the readme folder will be displayed when code is not running 49 | readme_folder = "readme" 50 | 51 | # The following is the table of content, which will be rendered in the menu area. 52 | # The content field is an array of chapters. 53 | # Each chapter must have a title 54 | # A chapter can have a folder. When a folder is provided and when the menu item is clicked, we will load the sample in the folder. If no folder is provided, this menu item will not be clickable. 55 | [[content]] 56 | title = "chapter 1" 57 | folder = "test_base" 58 | 59 | # A list of files we want to load into the editor. All files in the above folder will be deployed, but only these files in that folder will be loaded into the editor. 60 | [[content.files]] 61 | # Each folder must have an index.html, this file is the entrypoint. 62 | filename = "index.html" 63 | # is_readonly will make a file immutable (optional). 64 | is_readonly = true 65 | 66 | # Chapters can be nested by using the sub_chapters field. This field is an array, its format is the same as the content field. 67 | [[content.sub_chapters]] 68 | title = "chapter 1.1" 69 | folder = "test_base" 70 | 71 | [[content.sub_chapters.files]] 72 | filename = "index.html" 73 | 74 | # Another level of nested chapter 75 | [[content.sub_chapters.sub_chapters]] 76 | title = "chapter 1.1.1" 77 | folder = "test_base" 78 | 79 | [[content.sub_chapters.sub_chapters.files]] 80 | filename = "index.html" 81 | 82 | [[content]] 83 | title = "chapter 2" 84 | folder = "test_base" 85 | 86 | [[content.files]] 87 | filename = "index.html" 88 | is_readonly = true 89 | 90 | ``` 91 | 92 | Each individual sample should be in a separate folder. Under each folder, there must be an `index.html` file. This will be the entrypoint for the sample. When a user clicks the run button, we will load and display this `index.html` file. 93 | 94 | There can be a utility folder housing the common files that are shared by all samples. 95 | 96 | A typical project's folder structure should look like this: 97 | 98 | ```bash 99 | my-codestage-project/ 100 | ├─ sample_1/ 101 | │ ├─ favicon.ico 102 | │ ├─ index.html 103 | │ ├─ style.css 104 | ├─ sample_2/ 105 | │ ├─ index.html 106 | ├─ sample_3/ 107 | │ ├─ index.html 108 | │ ├─ index.css 109 | │ ├─ index.js 110 | ├─ utility_folder/ 111 | │ ├─ utility_script.js 112 | ├─ utility_folder_2/ 113 | │ ├─ test.png 114 | ├─ codestage.toml 115 | ├─ meta_image.png 116 | ├─ README.md 117 | ``` 118 | 119 | It is not necessary to develop the samples using the CodeStage editor. They can be developed using a more familiar and advanced editor. 120 | 121 | Once development is done, run this command to build your project: 122 | 123 | ```bash 124 | codestage --target 125 | ``` 126 | 127 | The static site is generated under 128 | 129 | If the site will be deployed to a subpath of a domain, indead of the root, for example: `https://example.com/my_samples`, We need to specify the path prefix (`/my_sample`). This can be done with either the commandline argument `--prefix` or the `codestage.toml` file. 130 | 131 | The commandline options have higher priority than the toml file. If you want to do any adhoc config changes, you can use the commandline. 132 | 133 | ## Example 134 | 135 | The [example_project](https://github.com/shi-yan/codestage/tree/master/example_project) folder contains an example project. To build it: 136 | 137 | ```bash 138 | cd example_project 139 | 140 | codestage 141 | ``` 142 | 143 | The generated site will be under `example_project/dist` 144 | 145 | ## Build 146 | ``` 147 | cd frontend 148 | npm i --save 149 | ./build 150 | cd cli 151 | cargo build --release 152 | ``` 153 | 154 | ## Implementation details 155 | When we build a CodeStage project, we first validate the `codestage.toml` file, copy all sample and utility folders to the target folder. We then generate a json file called `manifest.json`, which contains the menu structure for the project. We also output the frontend code into the target folder. When the project is loaded into browser, we fetch the manifest file first to populate the menu structure. When a menu item is clicked, we load the corresponding `files` as defined in the `codestage.toml` file into the editor. A user can freely change the sample code using the in-browser editor. When the `run` button is clicked, we use the following mechanism to assemble the program: 156 | 157 | 1. We first create a dom tree using the content of the index.html file. 158 | 2. We scan for all link tags. For all link tags that have the `href` attribute matching a modified css file, we will replace their `textContent` with the modified code. 159 | 3. We scan for all script tags. For all script tags that have the `src` attribute matching a modified js file, we will replace their `textContent` with the modified code. 160 | 4. Finally we inject a `base` tag into the document, so that we can use the sample's folder as the root. 161 | 5. The dom tree assembled above will be stuffed into an iframe for execution. 162 | 163 | The in-browser editor is built using [Monaco](https://microsoft.github.io/monaco-editor/). 164 | 165 | -------------------------------------------------------------------------------- /cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "codestage" 3 | version = "0.1.1-alpha.2" 4 | edition = "2021" 5 | authors = ["Shi Yan "] 6 | license = "MIT OR Apache-2.0" 7 | description = "A static site generator to create javascript playground demo" 8 | readme = "../README.md" 9 | homepage = "https://github.com/shi-yan/codestage" 10 | repository = "https://github.com/shi-yan/codestage" 11 | keywords = ["cli", "static-site", "demo", "code-playground", "javascript"] 12 | categories = ["command-line-utilities"] 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | toml = "0.5" 18 | serde = { version = "1.0", features = ["derive"] } 19 | serde_json = "1.0" 20 | rust-embed = "6.4" 21 | clap = { version = "4.0", features = ["derive"]} 22 | fs_extra = "1.2" 23 | regex = "1.10" -------------------------------------------------------------------------------- /example_project/basic_sample/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

This is the most basic Test

7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example_project/basic_sample/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/basic_sample/logo.png -------------------------------------------------------------------------------- /example_project/basic_sample/test.css: -------------------------------------------------------------------------------- 1 | #test { 2 | background-color: red; 3 | } -------------------------------------------------------------------------------- /example_project/basic_sample/test.glsl: -------------------------------------------------------------------------------- 1 | vec3 main() { 2 | gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 3 | } -------------------------------------------------------------------------------- /example_project/basic_sample/test.js: -------------------------------------------------------------------------------- 1 | console.log('debug print'); 2 | 3 | const shader = document.getElementById('shader'); 4 | 5 | console.log('print shader', shader.innerText); -------------------------------------------------------------------------------- /example_project/codestage.toml: -------------------------------------------------------------------------------- 1 | title = "CodeStage example" 2 | repo = "https://github.com/shi-yan/codestage" 3 | # need to have the slash 4 | prefix = "" 5 | target = "dist" 6 | url = "http://localhost:8000" 7 | meta_image = "meta.png" 8 | description = """ 9 | CodeStage is a static site generator to build JS playground demos.""" 10 | utilities = [ "khronos", "tdl" ] 11 | readme_folder = "readme" 12 | 13 | [[content]] 14 | title = "1. Basic Sample" 15 | folder = "basic_sample" 16 | 17 | [[content.files]] 18 | filename = "index.html" 19 | 20 | [[content.files]] 21 | filename = "test.css" 22 | 23 | [[content.files]] 24 | filename = "test.js" 25 | 26 | [[content.files]] 27 | filename = "test.glsl" 28 | 29 | [[content.sub_chapters]] 30 | title = "1.1 Nested Basic Sample" 31 | folder = "basic_sample" 32 | [[content.sub_chapters.files]] 33 | filename = "index.html" 34 | is_readonly = true 35 | 36 | [[content.sub_chapters.sub_chapters]] 37 | title = "1.1.1 Nested Basic Sample" 38 | folder = "basic_sample" 39 | [[content.sub_chapters.sub_chapters.files]] 40 | filename = "index.html" 41 | is_readonly = true 42 | 43 | [[content]] 44 | title = "2. Electric Flower" 45 | folder = "electricflower" 46 | 47 | [[content.files]] 48 | filename = "index.html" 49 | 50 | [[content.files]] 51 | filename = "flower.js" 52 | 53 | [[content.files]] 54 | filename = "postprocess.js" 55 | is_readonly = true 56 | 57 | [[content]] 58 | title = "3. Halo" 59 | folder = "halo" 60 | 61 | [[content.files]] 62 | filename = "index.html" 63 | 64 | [[content.files]] 65 | filename = "halo.js" 66 | 67 | [[content.files]] 68 | filename = "voronoi.js" 69 | is_readonly = true 70 | 71 | [[content]] 72 | title = "4. Space Rocks" 73 | folder = "spacerocks" 74 | 75 | [[content.files]] 76 | filename = "index.html" 77 | -------------------------------------------------------------------------------- /example_project/electricflower/electricflower.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/electricflower/electricflower.jpg -------------------------------------------------------------------------------- /example_project/electricflower/flower.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010, 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 | function FlowerEffect() { 33 | var arrays = tdl.primitives.createFlaredCube(0.01, 3.0, 1400) 34 | var program = tdl.programs.loadProgramFromScriptTags("flower_vs", "flower_fs") 35 | var textures = [] 36 | 37 | var proj = new Float32Array(16) 38 | var view = new Float32Array(16) 39 | var world = new Float32Array(16) 40 | 41 | var viewproj = new Float32Array(16) 42 | var worldviewproj = new Float32Array(16) 43 | 44 | var model = new tdl.models.Model(program, arrays, textures); 45 | 46 | var eyePosition = new Float32Array([0, 0, 3]) 47 | var target = new Float32Array([-0.3, 0, 0]) 48 | 49 | var m4 = tdl.fast.matrix4 50 | 51 | m4.lookAt(view, eyePosition, target, up); 52 | 53 | // Returns RGBA quad as array. 54 | function hsv2rgb(h, s, v, a) { 55 | h *= 6 56 | var i = Math.floor(h); 57 | var f = h - i; 58 | if (!(i & 1)) f = 1 - f; // if i is even 59 | var m = v * (1 - s); 60 | var n = v * (1 - s * f); 61 | switch (i) { 62 | case 6: 63 | case 0: return [v, n, m, a] 64 | case 1: return [n, v, m, a] 65 | case 2: return [m, v, n, a] 66 | case 3: return [m, n, v, a] 67 | case 4: return [n, m, v, a] 68 | case 5: return [v, m, n, a] 69 | } 70 | } 71 | 72 | this.render = function(framebuffer, time, postproc) { 73 | m4.perspective(proj, tdl.math.degToRad(60), aspect, 0.1, 500); 74 | m4.rotationY(world, time*0.2) 75 | m4.mul(viewproj, view, proj) 76 | m4.mul(worldviewproj, world, viewproj) 77 | 78 | if (postproc != 0) post.begin() 79 | 80 | gl.clearColor(0.1, 0.2, 0.3, 1) 81 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) 82 | gl.disable(gl.CULL_FACE); 83 | gl.disable(gl.DEPTH_TEST); 84 | gl.enable(gl.BLEND); 85 | gl.blendFunc(gl.ONE, gl.ONE); 86 | var boom = 0.0 //0.5 + Math.sin(time)*0.5 87 | var uniformsConst = { 88 | u_time: time, 89 | u_color: hsv2rgb((time * 0.1) % 1.0, 0.8, 0.1, 1), 90 | u_color2: hsv2rgb((time * 0.22124) % 1.0, 0.7, 0.1, 0), 91 | } 92 | var uniformsPer = { 93 | u_worldviewproj: worldviewproj 94 | } 95 | model.drawPrep(uniformsConst) 96 | model.draw(uniformsPer) 97 | gl.disable(gl.BLEND); 98 | 99 | switch (postproc) { 100 | case 1: 101 | post.end(framebuffer, post.hypnoGlow, {x: 3, y: 3, sub: 0.2}); 102 | break; 103 | case 2: 104 | post.end(framebuffer, post.focusBlur, {x: 2, y: 2}); 105 | break; 106 | case 3: 107 | post.end(framebuffer, post.radialBlur, {strength: 0.3, glow: 1.0}); 108 | break; 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /example_project/electricflower/index.html: -------------------------------------------------------------------------------- 1 | 31 | 32 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | WebGL Electric Flower 51 | 83 | 84 | 85 | 86 | 87 | 225 | 226 | 227 | 228 | 229 |
230 |
Postprocessing
231 |
None
232 |
Glow
233 |
Blur
234 |
Radial Blur
235 |
236 |
237 | 238 |
239 | 240 | 241 | 248 | 249 | 287 | 288 | 298 | 299 | 319 | 320 | 336 | 337 | 351 | 352 | 360 | 361 | 371 | 372 | 373 | -------------------------------------------------------------------------------- /example_project/electricflower/postprocess.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010, 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 | // requires shaders: quad_vs radial_fs blur_fs copy_fs add_fs 33 | 34 | // Bind quad_vs or similar together with any fragment program you want, 35 | // and draw(program). 36 | function QuadDrawer() { 37 | var quadVerts = new Float32Array([-1.0, -1.0, 0.0, 38 | 1.0, -1.0, 0.0, 39 | -1.0, 1.0, 0.0, 40 | 1.0, 1.0, 0.0]) 41 | var quadPosBuf = gl.createBuffer() 42 | gl.bindBuffer(gl.ARRAY_BUFFER, quadPosBuf) 43 | gl.bufferData(gl.ARRAY_BUFFER, quadVerts, gl.STATIC_DRAW) 44 | this.draw = function(program) { 45 | gl.bindBuffer(gl.ARRAY_BUFFER, quadPosBuf); 46 | gl.enableVertexAttribArray(program.attribLoc["position"]) 47 | gl.vertexAttribPointer(program.attribLoc["position"], 3, gl.FLOAT, false, 0, 0); 48 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4) 49 | } 50 | } 51 | 52 | function PostProcessor(w, h) { 53 | // Create a set of render targets, useful for various post processing 54 | // effects. 55 | var render_fb = tdl.framebuffers.createFramebuffer(w, h, true) 56 | var bounce_fb = tdl.framebuffers.createFramebuffer(w, h, true) 57 | 58 | var qw_fb = tdl.framebuffers.createFramebuffer(w / 4, h, true) 59 | var qw_qh_fb = tdl.framebuffers.createFramebuffer(w / 4, h / 4, true) 60 | 61 | // Re-bind the backbuffer. 62 | backbuffer.bind() 63 | 64 | var blurQuadProgram = tdl.programs.loadProgramFromScriptTags("quad_vs", "blur_fs") 65 | var copyQuadProgram = tdl.programs.loadProgramFromScriptTags("quad_vs", "copy_fs") 66 | var addQuadProgram = tdl.programs.loadProgramFromScriptTags("quad_vs", "add_fs") 67 | var radialQuadProgram = tdl.programs.loadProgramFromScriptTags("radial_vs", "radial_fs") 68 | 69 | this.focusBlur = function(framebuffer, params) { 70 | blurQuadProgram.use() 71 | blurQuadProgram.setUniform("mainSampler", render_fb.texture) 72 | 73 | qw_fb.bind() 74 | blurQuadProgram.setUniform("blurSize", [params.x / w, 0.0 / h]) 75 | blurQuadProgram.setUniform("subtract", [0,0,0,0]) 76 | quad.draw(blurQuadProgram) 77 | 78 | qw_qh_fb.bind() 79 | blurQuadProgram.setUniform("mainSampler", qw_fb.texture) 80 | blurQuadProgram.setUniform("blurSize", [0.0 / w, params.y / h]) 81 | blurQuadProgram.setUniform("subtract", [0,0,0,0]) 82 | quad.draw(blurQuadProgram) 83 | 84 | copyQuadProgram.use() 85 | copyQuadProgram.setUniform("mainSampler", qw_qh_fb.texture) 86 | framebuffer.bind() 87 | quad.draw(copyQuadProgram) 88 | } 89 | 90 | this.hypnoGlow = function(framebuffer, params) { 91 | blurQuadProgram.use() 92 | blurQuadProgram.setUniform("mainSampler", render_fb.texture) 93 | 94 | qw_fb.bind() 95 | blurQuadProgram.setUniform("blurSize", [params.x / w, 0.0 / h]) 96 | blurQuadProgram.setUniform("subtract", [params.sub,params.sub,params.sub,0]) 97 | quad.draw(blurQuadProgram) 98 | 99 | qw_qh_fb.bind() 100 | blurQuadProgram.setUniform("mainSampler", qw_fb.texture) 101 | blurQuadProgram.setUniform("blurSize", [0.0 / w, params.y / h]) 102 | blurQuadProgram.setUniform("subtract", [0,0,0,0]) 103 | quad.draw(blurQuadProgram) 104 | 105 | addQuadProgram.use() 106 | addQuadProgram.setUniform("mainSampler", qw_qh_fb.texture) 107 | addQuadProgram.setUniform("secondSampler", render_fb.texture) 108 | framebuffer.bind() 109 | quad.draw(addQuadProgram) 110 | } 111 | 112 | // Params: strength, glow(1.0=neutral) 113 | this.radialBlur = function(framebuffer, params) { 114 | if (params.strength <= 0.002) { 115 | framebuffer.bind() 116 | copyQuadProgram.use() 117 | copyQuadProgram.setUniform("mainSampler", render_fb.texture) 118 | quad.draw(copyQuadProgram) 119 | return 120 | } 121 | pingpong = [render_fb, bounce_fb] 122 | to = 1 123 | from = 0 124 | 125 | var passes = 3 126 | var amount = params.strength 127 | radialQuadProgram.use() 128 | radialQuadProgram.setUniform("mainSampler", pingpong[from].texture) 129 | for (var i = 0; i < passes; i++) { 130 | pingpong[to].bind() 131 | radialQuadProgram.setUniform("mainSampler", pingpong[from].texture) 132 | radialQuadProgram.setUniform("amount", amount) 133 | radialQuadProgram.setUniform("glow", params.glow) 134 | quad.draw(radialQuadProgram) 135 | amount /= 4.0 136 | to ^= 1 137 | from ^= 1 138 | } 139 | framebuffer.bind() 140 | copyQuadProgram.use() 141 | copyQuadProgram.setUniform("mainSampler", pingpong[from].texture) 142 | quad.draw(copyQuadProgram) 143 | } 144 | 145 | // You can not call begin "recursively" unless your two effects use completely 146 | // independent buffers. 147 | this.begin = function() { 148 | render_fb.bind() 149 | } 150 | 151 | this.end = function(framebuffer, func, params) { 152 | gl.disable(gl.DEPTH_TEST) 153 | gl.disable(gl.CULL_FACE) 154 | gl.disable(gl.BLEND) 155 | gl.activeTexture(gl.TEXTURE0) 156 | func(framebuffer, params) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /example_project/halo/halo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/halo/halo.jpg -------------------------------------------------------------------------------- /example_project/halo/index.html: -------------------------------------------------------------------------------- 1 | 31 | 34 | 35 | 36 | 37 | Halo 38 | 39 | 40 | 41 | 42 | 53 | 54 | 55 | 56 | 66 | 99 | 114 | 136 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /example_project/halo/voronoi.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010, 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 | (function(window) { 33 | 34 | var Vertex = function(x, y, artificial) { 35 | this.x = x; 36 | this.y = y; 37 | this.edge = null; 38 | this.artificial = Boolean(artificial); 39 | }; 40 | 41 | var HalfEdge = function() { 42 | this.next = null; 43 | this.face = null; 44 | }; 45 | HalfEdge.prototype = { 46 | coefs: function() { 47 | A = this.twin.origin.y - this.origin.y; 48 | B = this.origin.x - this.twin.origin.x; 49 | C = -(A * this.origin.x + B * this.origin.y); 50 | return [A, B, C]; 51 | }, 52 | perpendicular: function() { 53 | origin = this.origin; 54 | target = this.twin.origin; 55 | midx = (origin.x + target.x) / 2; 56 | midy = (origin.y + target.y) / 2; 57 | 58 | A = origin.x - target.x; 59 | B = origin.y - target.y; 60 | C = -(A * midx + B * midy); 61 | return [A, B, C]; 62 | } 63 | }; 64 | 65 | var make_edge_pair = function(v0, v1) { 66 | var e0 = new HalfEdge(), 67 | e1 = new HalfEdge(); 68 | e0.twin = e1; 69 | e0.origin = v0; 70 | e0.origin.edge = e0; 71 | e1.twin = e0; 72 | e1.origin = v1; 73 | e1.origin.edge = e1; 74 | return e0; 75 | }; 76 | 77 | var Face = function(edge) { 78 | this.edge = edge; 79 | this.data = null; 80 | }; 81 | 82 | var Triangle = function(face) { 83 | this.face = face; 84 | this.face.data = this; 85 | this.children = []; 86 | 87 | this.coefs = []; 88 | for (var i = 0, e = this.face.edge; i < 3; ++i) { 89 | this.coefs.push(e.coefs()); 90 | e = e.next; 91 | } 92 | }; 93 | Triangle.prototype = { 94 | get_face: function() { 95 | var triangle; 96 | for (triangle = this; 97 | triangle.children.length > 0; 98 | triangle = triangle.children[0]) 99 | ; 100 | return triangle.face; 101 | }, 102 | inside: function(v) { 103 | for (var i = 0, l = this.coefs.length; i < l; ++i) { 104 | var A = this.coefs[i][0], 105 | B = this.coefs[i][1], 106 | C = this.coefs[i][2]; 107 | if (A * v.x + B * v.y + C > 0.) 108 | return false; 109 | } 110 | return true; 111 | }, 112 | incircle: function(d) { 113 | var a = this.face.edge.origin, 114 | b = this.face.edge.next.origin, 115 | c = this.face.edge.next.next.origin, 116 | norm_a = a.x * a.x + a.y * a.y, 117 | norm_b = b.x * b.x + b.y * b.y, 118 | norm_c = c.x * c.x + c.y * c.y, 119 | norm_d = d.x * d.x + d.y * d.y, 120 | A = a.x - d.x, 121 | B = a.y - d.y, 122 | C = norm_a - norm_d, 123 | D = b.x - d.x, 124 | E = b.y - d.y, 125 | F = norm_b - norm_d, 126 | G = c.x - d.x, 127 | H = c.y - d.y, 128 | I = norm_c - norm_d, 129 | det = A * (E * I - F * H) - D * (B * I - C * H) + 130 | G * (B * F - C * E); 131 | return det > 0.; 132 | }, 133 | circumcenter: function() { 134 | var x, y, 135 | L1 = this.face.edge.perpendicular(), 136 | L2 = this.face.edge.next.perpendicular(); 137 | x = (L2[1] * L1[2] - L1[1] * L2[2]) / (L2[0] * L1[1] - L1[0] * L2[1]); 138 | if (L1[1] != 0) 139 | y = (-L1[0] * x - L1[2]) / L1[1]; 140 | else 141 | y = (-L2[0] * x - L2[2]) / L2[1]; 142 | return [x, y]; 143 | }, 144 | child: function(v) { 145 | for (var i = 0, l = this.children.length; i < l; ++i) { 146 | var child = this.children[i]; 147 | if (child.inside(v)) 148 | return child; 149 | } 150 | throw "OutsideTriangleError"; 151 | }, 152 | find_leaf: function(v) { 153 | var triangle; 154 | for (triangle = this; 155 | triangle.children.length > 0; 156 | triangle = triangle.child(v)) 157 | ; 158 | return triangle; 159 | }, 160 | deep_split: function(v) { 161 | var leaf = this.find_leaf(v); 162 | leaf.split(v); 163 | return leaf; 164 | }, 165 | split: function(v) { 166 | var side0 = this.face.edge, 167 | side1 = side0.next, 168 | side2 = side1.next, 169 | e0 = make_edge_pair(v, side0.origin), 170 | e1 = make_edge_pair(v, side1.origin), 171 | e2 = make_edge_pair(v, side2.origin); 172 | 173 | e0.next = side0; 174 | e1.next = side1; 175 | e2.next = side2; 176 | 177 | e0.twin.next = e2; 178 | e1.twin.next = e0; 179 | e2.twin.next = e1; 180 | 181 | side0.next = e1.twin; 182 | side1.next = e2.twin; 183 | side2.next = e0.twin; 184 | 185 | side0.face = new Face(side0); 186 | side1.face = new Face(side1); 187 | side2.face = new Face(side2); 188 | e0.face = side0.face; 189 | e1.twin.face = side0.face; 190 | e1.face = side1.face; 191 | e2.twin.face = side1.face; 192 | e2.face = side2.face; 193 | e0.twin.face = side2.face; 194 | 195 | this.face = null; 196 | this.children = [ 197 | new Triangle(side0.face), 198 | new Triangle(side1.face), 199 | new Triangle(side2.face)]; 200 | }, 201 | far_edge: function(v) { 202 | var edge; 203 | for (edge = this.face.edge; edge.origin !== v; edge = edge.next) 204 | ; 205 | return edge.next; 206 | }, 207 | flip: function(v) { 208 | var children, 209 | edge = this.far_edge(v), 210 | neighbor = edge.twin.face.data, 211 | target = edge.twin.next.next.origin, 212 | v_cw = edge.next, 213 | v_ccw = v_cw.next, 214 | t_cw = edge.twin.next, 215 | t_ccw = t_cw.next; 216 | 217 | edge.origin = v; 218 | edge.twin.origin = target; 219 | v_cw.next = edge; 220 | v_ccw.next = t_cw; 221 | t_cw.next = edge.twin; 222 | t_ccw.next = v_cw; 223 | edge.next = t_ccw; 224 | edge.twin.next = v_ccw; 225 | 226 | t_ccw.face = this.face; 227 | v_ccw.face = neighbor.face; 228 | 229 | this.face.edge = edge; 230 | neighbor.face.edge = edge.twin; 231 | 232 | v_cw.origin.edge = v_cw; 233 | t_cw.origin.edge = t_cw; 234 | 235 | children = [new Triangle(this.face), new Triangle(neighbor.face)] 236 | this.children = children; 237 | neighbor.children = children; 238 | this.face = null; 239 | neighbor.face = null; 240 | } 241 | }; 242 | 243 | var make_triangle = function(v0, v1, v2) { 244 | var e0 = make_edge_pair(v0, v1), 245 | e1 = make_edge_pair(v1, v2), 246 | e2 = make_edge_pair(v2, v0), 247 | f = new Face(e0); 248 | e0.next = e1; 249 | e1.next = e2; 250 | e2.next = e0; 251 | e0.face = f; 252 | e1.face = f; 253 | e2.face = f; 254 | return new Triangle(f); 255 | }; 256 | 257 | var legalize = function(triangle, v) { 258 | var adjacent, 259 | face = triangle.far_edge(v).twin.face; 260 | if (face !== null) { 261 | adjacent = face.data; 262 | if (adjacent.incircle(v)) { 263 | triangle.flip(v); 264 | legalize(triangle.children[0], v); 265 | legalize(triangle.children[1], v); 266 | } 267 | } 268 | }; 269 | 270 | var triangulate = function(verticies, max_coord) { 271 | var i, j, l, l2, vertex, M, triangle, leaf, child; 272 | 273 | if (max_coord === undefined) { 274 | max_coord = 0; 275 | for (i = 0, l = verticies.length; i < l; ++i) { 276 | vertex = verticies[i]; 277 | max_coord = Math.max(max_coord, 278 | Math.abs(vertex.x), Math.abs(vertex.y)); 279 | } 280 | } 281 | 282 | M = 3 * max_coord; 283 | triangle = make_triangle(new Vertex(M, 0, true), new Vertex(0, M, true), 284 | new Vertex(-M, -M, true)); 285 | 286 | for (i = 0, l = verticies.length; i < l; ++i) { 287 | vertex = verticies[i]; 288 | leaf = triangle.deep_split(vertex); 289 | for (j = 0; l2 = leaf.children.length, j < l2; ++j) 290 | legalize(leaf.children[j], vertex); 291 | } 292 | 293 | return triangle; 294 | }; 295 | 296 | window.Vertex = Vertex; 297 | window.triangulate = triangulate; 298 | 299 | })(window); 300 | 301 | -------------------------------------------------------------------------------- /example_project/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/logo.png -------------------------------------------------------------------------------- /example_project/logo4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/logo4.png -------------------------------------------------------------------------------- /example_project/meta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/meta.png -------------------------------------------------------------------------------- /example_project/readme/index.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | Halo 8 | 9 | 10 | 11 | readme test 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example_project/spacerocks/assets/f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/f.png -------------------------------------------------------------------------------- /example_project/spacerocks/assets/height-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/height-map.png -------------------------------------------------------------------------------- /example_project/spacerocks/assets/rock-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/rock-color.png -------------------------------------------------------------------------------- /example_project/spacerocks/assets/rock-nmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/rock-nmap.png -------------------------------------------------------------------------------- /example_project/spacerocks/assets/rocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/rocks.png -------------------------------------------------------------------------------- /example_project/spacerocks/assets/shield-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/shield-gradient.png -------------------------------------------------------------------------------- /example_project/spacerocks/assets/shield-noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/shield-noise.png -------------------------------------------------------------------------------- /example_project/spacerocks/assets/space_bk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/space_bk.jpg -------------------------------------------------------------------------------- /example_project/spacerocks/assets/space_dn.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/space_dn.jpg -------------------------------------------------------------------------------- /example_project/spacerocks/assets/space_fr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/space_fr.jpg -------------------------------------------------------------------------------- /example_project/spacerocks/assets/space_lf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/space_lf.jpg -------------------------------------------------------------------------------- /example_project/spacerocks/assets/space_rt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/space_rt.jpg -------------------------------------------------------------------------------- /example_project/spacerocks/assets/space_up.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/space_up.jpg -------------------------------------------------------------------------------- /example_project/spacerocks/assets/white-square-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/assets/white-square-outline.png -------------------------------------------------------------------------------- /example_project/spacerocks/spacerocks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/spacerocks/spacerocks.jpg -------------------------------------------------------------------------------- /example_project/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 | 38 | tdl.provide('tdl.buffers'); 39 | 40 | /** 41 | * A module for buffers. 42 | * @namespace 43 | */ 44 | tdl.buffers = tdl.buffers || {}; 45 | 46 | tdl.buffers.Buffer = function(array, opt_target) { 47 | var target = opt_target || gl.ARRAY_BUFFER; 48 | var buf = gl.createBuffer(); 49 | this.target = target; 50 | this.buf = buf; 51 | this.set(array); 52 | this.numComponents_ = array.numComponents; 53 | this.numElements_ = array.numElements; 54 | this.totalComponents_ = this.numComponents_ * this.numElements_; 55 | if (array.buffer instanceof Float32Array) { 56 | this.type_ = gl.FLOAT; 57 | this.normalize_ = false; 58 | } else if (array.buffer instanceof Uint8Array) { 59 | this.type_ = gl.UNSIGNED_BYTE; 60 | this.normalize_ = true; 61 | } else if (array.buffer instanceof Int8Array) { 62 | this.type_ = gl.BYTE; 63 | this.normalize_ = true; 64 | } else if (array.buffer instanceof Uint16Array) { 65 | this.type_ = gl.UNSIGNED_SHORT; 66 | this.normalize_ = true; 67 | } else if (array.buffer instanceof Int16Array) { 68 | this.type_ = gl.SHORT; 69 | this.normalize_ = true; 70 | } else { 71 | throw("unhandled type:" + (typeof array.buffer)); 72 | } 73 | }; 74 | 75 | tdl.buffers.Buffer.prototype.set = function(array, opt_usage) { 76 | gl.bindBuffer(this.target, this.buf); 77 | gl.bufferData(this.target, array.buffer, opt_usage || gl.STATIC_DRAW); 78 | } 79 | 80 | tdl.buffers.Buffer.prototype.setRange = function(array, offset) { 81 | gl.bindBuffer(this.target, this.buf); 82 | gl.bufferSubData(this.target, offset, array); 83 | } 84 | 85 | tdl.buffers.Buffer.prototype.type = function() { 86 | return this.type_; 87 | }; 88 | 89 | tdl.buffers.Buffer.prototype.numComponents = function() { 90 | return this.numComponents_; 91 | }; 92 | 93 | tdl.buffers.Buffer.prototype.numElements = function() { 94 | return this.numElements_; 95 | }; 96 | 97 | tdl.buffers.Buffer.prototype.totalComponents = function() { 98 | return this.totalComponents_; 99 | }; 100 | 101 | tdl.buffers.Buffer.prototype.buffer = function() { 102 | return this.buf; 103 | }; 104 | 105 | tdl.buffers.Buffer.prototype.stride = function() { 106 | return 0; 107 | }; 108 | 109 | tdl.buffers.Buffer.prototype.normalize = function() { 110 | return this.normalize_; 111 | } 112 | 113 | tdl.buffers.Buffer.prototype.offset = function() { 114 | return 0; 115 | }; 116 | 117 | 118 | -------------------------------------------------------------------------------- /example_project/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 | 37 | tdl.provide('tdl.clock'); 38 | 39 | tdl.require('tdl.io'); 40 | tdl.require('tdl.log'); 41 | 42 | /** 43 | * Creates a clock. Optionally synced to a server 44 | * @param {number} opt_syncRate. If passed, this is the number of seconds 45 | * between syncing to the server. If not passed the local clock is used. 46 | * Note: If the client is faster than the server this means it's possible 47 | * the clock will report a certain time and then later a previous time. 48 | */ 49 | tdl.clock.createClock = function(opt_syncRate, opt_url) { 50 | if (opt_syncRate) { 51 | return new tdl.clock.SyncedClock(opt_syncRate, opt_url); 52 | } else { 53 | return new tdl.clock.LocalClock(); 54 | } 55 | }; 56 | 57 | 58 | /** 59 | * A clock that gets the local current time in seconds. 60 | * @private 61 | */ 62 | tdl.clock.LocalClock = function() { 63 | } 64 | 65 | /** 66 | * Gets the current time in seconds. 67 | * @private 68 | */ 69 | tdl.clock.LocalClock.prototype.getTime = function() { 70 | return (new Date()).getTime() * 0.001; 71 | } 72 | 73 | /** 74 | * A clock that gets the current time in seconds attempting to eep the clock 75 | * synced to the server. 76 | * @private 77 | */ 78 | tdl.clock.SyncedClock = function(opt_syncRate, opt_url) { 79 | this.url = opt_url || window.location.href; 80 | this.syncRate = opt_syncRate || 10; 81 | this.timeOffset = 0; 82 | this.syncToServer(); 83 | } 84 | 85 | tdl.clock.SyncedClock.prototype.getLocalTime_ = function() { 86 | return (new Date()).getTime() * 0.001; 87 | } 88 | 89 | tdl.clock.SyncedClock.prototype.syncToServer = function() { 90 | var that = this; 91 | var sendTime = this.getLocalTime_(); 92 | tdl.io.sendJSON(this.url, {cmd: 'time'}, function(obj, exception) { 93 | if (exception) { 94 | tdl.log("error: syncToServer: " + exception); 95 | } else { 96 | var receiveTime = that.getLocalTime_(); 97 | var duration = receiveTime - sendTime; 98 | var serverTime = obj.time + duration * 0.5; 99 | that.timeOffset = serverTime - receiveTime; 100 | tdl.log("new timeoffset: " + that.timeOffset); 101 | } 102 | setTimeout(function() { 103 | that.syncToServer(); 104 | }, that.syncRate * 1000); 105 | }); 106 | }; 107 | 108 | /** 109 | * Gets the current time in seconds. 110 | * @private 111 | */ 112 | tdl.clock.SyncedClock.prototype.getTime = function() { 113 | return (new Date()).getTime() * 0.001 + this.timeOffset; 114 | } 115 | 116 | -------------------------------------------------------------------------------- /example_project/tdl/error.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/example_project/tdl/error.jpg -------------------------------------------------------------------------------- /example_project/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 | 38 | tdl.provide('tdl.fps'); 39 | 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 | /** 73 | * Updates the fps measurement. You must call this in your 74 | * render loop. 75 | * 76 | * @param {number} elapsedTime The elasped time in seconds 77 | * since the last frame. 78 | */ 79 | tdl.fps.FPSTimer.prototype.update = function(elapsedTime) { 80 | // Keep the total time and total active time for the last N frames. 81 | this.totalTime_ += elapsedTime - this.timeTable_[this.timeTableCursor_]; 82 | 83 | // Save off the elapsed time for this frame so we can subtract it later. 84 | this.timeTable_[this.timeTableCursor_] = elapsedTime; 85 | 86 | // Wrap the place to store the next time sample. 87 | ++this.timeTableCursor_; 88 | if (this.timeTableCursor_ == tdl.fps.NUM_FRAMES_TO_AVERAGE) { 89 | this.timeTableCursor_ = 0; 90 | } 91 | 92 | this.instantaneousFPS = Math.floor(1.0 / elapsedTime + 0.5); 93 | this.averageFPS = Math.floor( 94 | (1.0 / (this.totalTime_ / tdl.fps.NUM_FRAMES_TO_AVERAGE)) + 0.5); 95 | }; 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example_project/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 | 38 | tdl.provide('tdl.framebuffers'); 39 | 40 | tdl.require('tdl.textures'); 41 | 42 | /** 43 | * A module for textures. 44 | * @namespace 45 | */ 46 | tdl.framebuffers = tdl.framebuffers || {}; 47 | 48 | tdl.framebuffers.createFramebuffer = function(width, height, opt_depth) { 49 | return new tdl.framebuffers.Framebuffer(width, height, opt_depth); 50 | }; 51 | 52 | tdl.framebuffers.createCubeFramebuffer = function(size, opt_depth) { 53 | return new tdl.framebuffers.CubeFramebuffer(size, opt_depth); 54 | }; 55 | 56 | tdl.framebuffers.BackBuffer = function(canvas) { 57 | this.depth = true; 58 | this.buffer = null; 59 | }; 60 | 61 | tdl.framebuffers.BackBuffer.prototype.bind = function() { 62 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 63 | gl.viewport(0, 0, this.width, this.height); 64 | }; 65 | 66 | if (Object.prototype.__defineSetter__) { 67 | tdl.framebuffers.BackBuffer.prototype.__defineGetter__( 68 | 'width', 69 | function () { 70 | return gl.drawingBufferWidth || gl.canvas.width; 71 | } 72 | ); 73 | 74 | tdl.framebuffers.BackBuffer.prototype.__defineGetter__( 75 | 'height', 76 | function () { 77 | return gl.drawingBufferHeight || gl.canvas.height; 78 | } 79 | ); 80 | } 81 | 82 | // Use this where you need to pass in a framebuffer, but you really 83 | // mean the backbuffer, so that binding it works as expected. 84 | tdl.framebuffers.getBackBuffer = function(canvas) { 85 | return new tdl.framebuffers.BackBuffer(canvas) 86 | }; 87 | 88 | tdl.framebuffers.Framebuffer = function(width, height, opt_depth) { 89 | this.width = width; 90 | this.height = height; 91 | this.depth = opt_depth; 92 | this.recoverFromLostContext(); 93 | }; 94 | 95 | tdl.framebuffers.Framebuffer.prototype.bind = function() { 96 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); 97 | gl.viewport(0, 0, this.width, this.height); 98 | }; 99 | 100 | tdl.framebuffers.Framebuffer.unbind = function() { 101 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 102 | gl.viewport( 103 | 0, 0, 104 | gl.drawingBufferWidth || gl.canvas.width, 105 | gl.drawingBufferHeight || gl.canvas.height); 106 | }; 107 | 108 | tdl.framebuffers.Framebuffer.prototype.unbind = function() { 109 | tdl.framebuffers.Framebuffer.unbind(); 110 | }; 111 | 112 | tdl.framebuffers.Framebuffer.prototype.recoverFromLostContext = function() { 113 | var tex = new tdl.textures.SolidTexture([0,0,0,0]); 114 | this.initializeTexture(tex); 115 | 116 | var fb = gl.createFramebuffer(); 117 | gl.bindFramebuffer(gl.FRAMEBUFFER, fb); 118 | gl.framebufferTexture2D( 119 | gl.FRAMEBUFFER, 120 | gl.COLOR_ATTACHMENT0, 121 | gl.TEXTURE_2D, 122 | tex.texture, 123 | 0); 124 | 125 | if (this.depth) { 126 | if (gl.tdl.depthTexture) { 127 | var dt = new tdl.textures.DepthTexture(this.width, this.height); 128 | gl.framebufferTexture2D( 129 | gl.FRAMEBUFFER, 130 | gl.DEPTH_ATTACHMENT, 131 | gl.TEXTURE_2D, 132 | dt.texture, 133 | 0); 134 | this.depthTexture = dt; 135 | } else { 136 | var db = gl.createRenderbuffer(); 137 | gl.bindRenderbuffer(gl.RENDERBUFFER, db); 138 | gl.renderbufferStorage( 139 | gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height); 140 | gl.framebufferRenderbuffer( 141 | gl.FRAMEBUFFER, 142 | gl.DEPTH_ATTACHMENT, 143 | gl.RENDERBUFFER, 144 | db); 145 | gl.bindRenderbuffer(gl.RENDERBUFFER, null); 146 | this.depthRenderbuffer = db; 147 | } 148 | } 149 | 150 | var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); 151 | if (status != gl.FRAMEBUFFER_COMPLETE && !gl.isContextLost()) { 152 | throw("gl.checkFramebufferStatus() returned " + 153 | tdl.webgl.glEnumToString(status)); 154 | } 155 | this.framebuffer = fb; 156 | this.texture = tex; 157 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 158 | }; 159 | 160 | tdl.framebuffers.Framebuffer.prototype.initializeTexture = function(tex) { 161 | gl.bindTexture(gl.TEXTURE_2D, tex.texture); 162 | tex.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR); 163 | tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); 164 | tex.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 165 | tex.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 166 | gl.texImage2D(gl.TEXTURE_2D, 167 | 0, // level 168 | gl.RGBA, // internalFormat 169 | this.width, // width 170 | this.height, // height 171 | 0, // border 172 | gl.RGBA, // format 173 | gl.UNSIGNED_BYTE, // type 174 | null); // data 175 | }; 176 | 177 | tdl.framebuffers.CubeFramebuffer = function(size, opt_depth) { 178 | this.size = size; 179 | this.depth = opt_depth; 180 | this.recoverFromLostContext(); 181 | }; 182 | 183 | tdl.framebuffers.CubeFramebuffer.prototype.bind = function(face) { 184 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[face]); 185 | gl.viewport(0, 0, this.size, this.size); 186 | }; 187 | 188 | tdl.framebuffers.CubeFramebuffer.unbind = function() { 189 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 190 | gl.viewport( 191 | 0, 0, 192 | gl.drawingBufferWidth || gl.canvas.width, 193 | gl.drawingBufferHeight || gl.canvas.height); 194 | }; 195 | 196 | tdl.framebuffers.CubeFramebuffer.prototype.unbind = function() { 197 | tdl.framebuffers.CubeFramebuffer.unbind(); 198 | }; 199 | 200 | tdl.framebuffers.CubeFramebuffer.prototype.recoverFromLostContext = function() { 201 | var tex = new tdl.textures.CubeMap(this.size); 202 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex.texture); 203 | tex.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR); 204 | tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); 205 | tex.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 206 | tex.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 207 | for (var ff = 0; ff < 6; ++ff) { 208 | gl.texImage2D(tdl.textures.CubeMap.faceTargets[ff], 209 | 0, // level 210 | gl.RGBA, // internalFormat 211 | this.size, // width 212 | this.size, // height 213 | 0, // border 214 | gl.RGBA, // format 215 | gl.UNSIGNED_BYTE, // type 216 | null); // data 217 | } 218 | if (this.depth) { 219 | var db = gl.createRenderbuffer(); 220 | gl.bindRenderbuffer(gl.RENDERBUFFER, db); 221 | gl.renderbufferStorage( 222 | gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.size, this.size); 223 | } 224 | this.framebuffers = []; 225 | for (var ff = 0; ff < 6; ++ff) { 226 | var fb = gl.createFramebuffer(); 227 | gl.bindFramebuffer(gl.FRAMEBUFFER, fb); 228 | gl.framebufferTexture2D( 229 | gl.FRAMEBUFFER, 230 | gl.COLOR_ATTACHMENT0, 231 | tdl.textures.CubeMap.faceTargets[ff], 232 | tex.texture, 233 | 0); 234 | if (this.depth) { 235 | gl.framebufferRenderbuffer( 236 | gl.FRAMEBUFFER, 237 | gl.DEPTH_ATTACHMENT, 238 | gl.RENDERBUFFER, 239 | db); 240 | } 241 | var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); 242 | if (status != gl.FRAMEBUFFER_COMPLETE) { 243 | throw("gl.checkFramebufferStatus() returned " + WebGLDebugUtils.glEnumToString(status)); 244 | } 245 | this.framebuffers.push(fb); 246 | } 247 | gl.bindRenderbuffer(gl.RENDERBUFFER, null); 248 | this.texture = tex; 249 | }; 250 | 251 | tdl.framebuffers.Float32Framebuffer = function(width, height, opt_depth) { 252 | if (!gl.getExtension("OES_texture_float")) { 253 | throw("Requires OES_texture_float extension"); 254 | } 255 | tdl.framebuffers.Framebuffer.call(this, width, height, opt_depth); 256 | }; 257 | 258 | tdl.base.inherit(tdl.framebuffers.Float32Framebuffer, tdl.framebuffers.Framebuffer); 259 | 260 | tdl.framebuffers.Float32Framebuffer.prototype.initializeTexture = function(tex) { 261 | gl.bindTexture(gl.TEXTURE_2D, tex.texture); 262 | tex.setParameter(gl.TEXTURE_MIN_FILTER, gl.NEAREST); 263 | tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST); 264 | tex.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 265 | tex.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 266 | gl.texImage2D(gl.TEXTURE_2D, 267 | 0, // level 268 | gl.RGBA, // internalFormat 269 | this.width, // width 270 | this.height, // height 271 | 0, // border 272 | gl.RGBA, // format 273 | gl.FLOAT, // type 274 | null); // data 275 | }; 276 | -------------------------------------------------------------------------------- /example_project/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 | 38 | tdl.provide('tdl.fullscreen'); 39 | 40 | /** 41 | * A module for misc. 42 | * @namespace 43 | */ 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 | 88 | -------------------------------------------------------------------------------- /example_project/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 | 37 | tdl.provide('tdl.io'); 38 | 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 | *
 61 |  * var g_loadInfo = null;
 62 |  * g_id = window.setInterval(statusUpdate, 500);
 63 |  * g_loadInfo = tdl.scene.loadScene('http://google.com/somescene.js',
 64 |  *                                  callback);
 65 |  *
 66 |  * function callback(exception) {
 67 |  *   g_loadInfo = null;
 68 |  *   window.clearInterval(g_id);
 69 |  *   if (!exception) {
 70 |  *     // do something with scene just loaded
 71 |  *   }
 72 |  * }
 73 |  *
 74 |  * function statusUpdate() {
 75 |  *   if (g_loadInfo) {
 76 |  *     var progress = g_loadInfo.getKnownProgressInfoSoFar();
 77 |  *     document.getElementById('loadstatus').innerHTML = progress.percent;
 78 |  *   }
 79 |  * }
 80 |  * 
81 | * 82 | * @constructor 83 | * @param {XMLHttpRequest} opt_request 84 | * The request to watch. 85 | * @see tdl.loader.Loader 86 | */ 87 | tdl.io.LoadInfo = function(opt_request) { 88 | this.request_ = opt_request; 89 | this.streamLength_ = 0; // because the request may have been freed. 90 | this.children_ = []; 91 | }; 92 | 93 | /** 94 | * Adds another LoadInfo as a child of this LoadInfo so they can be 95 | * managed as a group. 96 | * @param {!tdl.io.LoadInfo} loadInfo The child LoadInfo. 97 | */ 98 | tdl.io.LoadInfo.prototype.addChild = function(loadInfo) { 99 | this.children_.push(loadInfo); 100 | }; 101 | 102 | /** 103 | * Marks this LoadInfo as finished. 104 | */ 105 | tdl.io.LoadInfo.prototype.finish = function() { 106 | if (this.request_) { 107 | if (this.hasStatus_) { 108 | this.streamLength_ = this.request_.streamLength; 109 | } 110 | this.request_ = null; 111 | } 112 | }; 113 | 114 | /** 115 | * Gets the total bytes that will be streamed known so far. 116 | * If you are only streaming 1 file then this will be the info for that file but 117 | * if you have queued up many files using an tdl.loader.Loader only a couple of 118 | * files are streamed at a time meaning that the size is not known for files 119 | * that have yet started to 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 aware the bar will 126 | * grown and then shrink as new files start to download and their lengths 127 | * become known. 128 | * 129 | * 2) Use LoadInfo.getTotalRequestsDownloaded() / 130 | * LoadInfo.getTotalKnownRequestsToStreamSoFar() and be aware the granularity 131 | * is not all that great since it only reports fully downloaded files. If you 132 | * are downloading a bunch of small files this might be ok. 133 | * 134 | * 3) Put all your files in one archive. Then there will be only one file and 135 | * method 1 will work well. 136 | * 137 | * 4) Figure out the total size in bytes of the files you will download and put 138 | * that number in your application, then use LoadInfo.getTotalBytesDownloaded() 139 | * / MY_APPS_TOTAL_BYTES_TO_DOWNLOAD. 140 | * 141 | * @return {number} The total number of currently known bytes to be streamed. 142 | */ 143 | tdl.io.LoadInfo.prototype.getTotalKnownBytesToStreamSoFar = function() { 144 | //if (!this.streamLength_ && this.request_ && this.hasStatus_) { 145 | // // 146 | // //this.streamLength_ = this.request_.streamLength; 147 | //} 148 | var total = this.streamLength_; 149 | for (var cc = 0; cc < this.children_.length; ++cc) { 150 | total += this.children_[cc].getTotalKnownBytesToStreamSoFar(); 151 | } 152 | return total; 153 | }; 154 | 155 | /** 156 | * Gets the total bytes downloaded so far. 157 | * @return {number} The total number of currently known bytes to be streamed. 158 | */ 159 | tdl.io.LoadInfo.prototype.getTotalBytesDownloaded = function() { 160 | var total = (this.request_ && this.hasStatus_) ? 161 | this.request_.bytesReceived : this.streamLength_; 162 | for (var cc = 0; cc < this.children_.length; ++cc) { 163 | total += this.children_[cc].getTotalBytesDownloaded(); 164 | } 165 | return total; 166 | }; 167 | 168 | /** 169 | * Gets the total streams that will be download known so far. 170 | * We can't know all the streams since you could use an tdl.loader.Loader 171 | * object, request some streams, then call this function, then request some 172 | * more. 173 | * 174 | * See LoadInfo.getTotalKnownBytesToStreamSoFar for details. 175 | * @return {number} The total number of requests currently known to be streamed. 176 | * @see tdl.io.LoadInfo.getTotalKnownBytesToStreamSoFar 177 | */ 178 | tdl.io.LoadInfo.prototype.getTotalKnownRequestsToStreamSoFar = function() { 179 | var total = 1; 180 | for (var cc = 0; cc < this.children_.length; ++cc) { 181 | total += this.children_[cc].getTotalKnownRequestToStreamSoFar(); 182 | } 183 | return total; 184 | }; 185 | 186 | /** 187 | * Gets the total requests downloaded so far. 188 | * @return {number} The total requests downloaded so far. 189 | */ 190 | tdl.io.LoadInfo.prototype.getTotalRequestsDownloaded = function() { 191 | var total = this.request_ ? 0 : 1; 192 | for (var cc = 0; cc < this.children_.length; ++cc) { 193 | total += this.children_[cc].getTotalRequestsDownloaded(); 194 | } 195 | return total; 196 | }; 197 | 198 | /** 199 | * Gets progress info. 200 | * This is commonly formatted version of the information available from a 201 | * LoadInfo. 202 | * 203 | * See LoadInfo.getTotalKnownBytesToStreamSoFar for details. 204 | * @return {{percent: number, downloaded: string, totalBytes: string, 205 | * base: number, suffix: string}} progress info. 206 | * @see tdl.io.LoadInfo.getTotalKnownBytesToStreamSoFar 207 | */ 208 | tdl.io.LoadInfo.prototype.getKnownProgressInfoSoFar = function() { 209 | var percent = 0; 210 | var bytesToDownload = this.getTotalKnownBytesToStreamSoFar(); 211 | var bytesDownloaded = this.getTotalBytesDownloaded(); 212 | if (bytesToDownload > 0) { 213 | percent = Math.floor(bytesDownloaded / bytesToDownload * 100); 214 | } 215 | 216 | var base = (bytesToDownload < 1024 * 1024) ? 1024 : (1024 * 1024); 217 | 218 | return { 219 | percent: percent, 220 | downloaded: (bytesDownloaded / base).toFixed(2), 221 | totalBytes: (bytesToDownload / base).toFixed(2), 222 | base: base, 223 | suffix: (base == 1024 ? 'kb' : 'mb')} 224 | 225 | }; 226 | 227 | /** 228 | * Loads text from an external file. This function is synchronous. 229 | * @param {string} url The url of the external file. 230 | * @return {string} the loaded text if the request is synchronous. 231 | */ 232 | tdl.io.loadTextFileSynchronous = function(url) { 233 | var error = 'loadTextFileSynchronous failed to load url "' + url + '"'; 234 | var request; 235 | if (window.XMLHttpRequest) { 236 | request = new XMLHttpRequest(); 237 | if (request.overrideMimeType) { 238 | request.overrideMimeType('text/plain'); 239 | } 240 | } else if (window.ActiveXObject) { 241 | request = new ActiveXObject('MSXML2.XMLHTTP.3.0'); 242 | } else { 243 | throw 'XMLHttpRequest is disabled'; 244 | } 245 | request.open('GET', url, false); 246 | request.send(null); 247 | if (request.readyState != 4) { 248 | throw error; 249 | } 250 | return request.responseText; 251 | }; 252 | 253 | /** 254 | * Loads text from an external file. This function is asynchronous. 255 | * @param {string} url The url of the external file. 256 | * @param {function(string, *): void} callback A callback passed the loaded 257 | * string and an exception which will be null on success. 258 | * @return {!tdl.io.LoadInfo} A LoadInfo to track progress. 259 | */ 260 | tdl.io.loadTextFile = function(url, callback) { 261 | var error = 'loadTextFile failed to load url "' + url + '"'; 262 | var request; 263 | if (window.XMLHttpRequest) { 264 | request = new XMLHttpRequest(); 265 | if (request.overrideMimeType) { 266 | request.overrideMimeType('text/plain; charset=utf-8'); 267 | } 268 | } else if (window.ActiveXObject) { 269 | request = new ActiveXObject('MSXML2.XMLHTTP.3.0'); 270 | } else { 271 | throw 'XMLHttpRequest is disabled'; 272 | } 273 | var loadInfo = tdl.io.createLoadInfo(request, false); 274 | request.open('GET', url, true); 275 | var finish = function() { 276 | if (request.readyState == 4) { 277 | var text = ''; 278 | // HTTP reports success with a 200 status. The file protocol reports 279 | // success with zero. HTTP does not use zero as a status code (they 280 | // start at 100). 281 | // https://developer.mozilla.org/En/Using_XMLHttpRequest 282 | var success = request.status == 200 || request.status == 0; 283 | if (success) { 284 | text = request.responseText; 285 | } 286 | loadInfo.finish(); 287 | callback(text, success ? null : 'could not load: ' + url); 288 | } 289 | }; 290 | request.onreadystatechange = finish; 291 | request.send(null); 292 | return loadInfo; 293 | }; 294 | 295 | /** 296 | * Loads a file from an external file. This function is 297 | * asynchronous. 298 | * @param {string} url The url of the external file. 299 | * @param {function(string, *): void} callback A callback passed the loaded 300 | * ArrayBuffer and an exception which will be null on 301 | * success. 302 | * @return {!tdl.io.LoadInfo} A LoadInfo to track progress. 303 | */ 304 | tdl.io.loadArrayBuffer = function(url, callback) { 305 | var error = 'loadArrayBuffer failed to load url "' + url + '"'; 306 | var request; 307 | if (window.XMLHttpRequest) { 308 | request = new XMLHttpRequest(); 309 | } else { 310 | throw 'XMLHttpRequest is disabled'; 311 | } 312 | var loadInfo = tdl.io.createLoadInfo(request, false); 313 | request.open('GET', url, true); 314 | var finish = function() { 315 | if (request.readyState == 4) { 316 | var text = ''; 317 | // HTTP reports success with a 200 status. The file protocol reports 318 | // success with zero. HTTP does not use zero as a status code (they 319 | // start at 100). 320 | // https://developer.mozilla.org/En/Using_XMLHttpRequest 321 | var success = request.status == 200 || request.status == 0; 322 | if (success) { 323 | arrayBuffer = request.response; 324 | } 325 | loadInfo.finish(); 326 | callback(arrayBuffer, success ? null : 'could not load: ' + url); 327 | } 328 | }; 329 | request.onreadystatechange = finish; 330 | if (request.responseType === undefined) { 331 | throw 'no support for binary files'; 332 | } 333 | request.responseType = "arraybuffer"; 334 | request.send(null); 335 | return loadInfo; 336 | }; 337 | 338 | /** 339 | * Loads JSON from an external file. This function is asynchronous. 340 | * @param {string} url The url of the external file. 341 | * @param {function(jsonObject, *): void} callback A callback passed the loaded 342 | * json and an exception which will be null on success. 343 | * @return {!tdl.io.LoadInfo} A LoadInfo to track progress. 344 | */ 345 | tdl.io.loadJSON = function(url, callback) { 346 | var error = 'loadJSON failed to load url "' + url + '"'; 347 | var request; 348 | if (window.XMLHttpRequest) { 349 | request = new XMLHttpRequest(); 350 | if (request.overrideMimeType) { 351 | request.overrideMimeType('text/plain'); 352 | } 353 | } else if (window.ActiveXObject) { 354 | request = new ActiveXObject('MSXML2.XMLHTTP.3.0'); 355 | } else { 356 | throw 'XMLHttpRequest is disabled'; 357 | } 358 | var loadInfo = tdl.io.createLoadInfo(request, false); 359 | request.open('GET', url, true); 360 | var finish = function() { 361 | if (request.readyState == 4) { 362 | var json = undefined; 363 | // HTTP reports success with a 200 status. The file protocol reports 364 | // success with zero. HTTP does not use zero as a status code (they 365 | // start at 100). 366 | // https://developer.mozilla.org/En/Using_XMLHttpRequest 367 | var success = request.status == 200 || request.status == 0; 368 | if (success) { 369 | try { 370 | json = JSON.parse(request.responseText); 371 | } catch (e) { 372 | success = false; 373 | } 374 | } 375 | loadInfo.finish(); 376 | callback(json, success ? null : 'could not load: ' + url); 377 | } 378 | }; 379 | try { 380 | request.onreadystatechange = finish; 381 | request.send(null); 382 | } catch (e) { 383 | callback(null, 'could not load: ' + url); 384 | } 385 | return loadInfo; 386 | }; 387 | 388 | /** 389 | * Sends an object. This function is asynchronous. 390 | * @param {string} url The url of the external file. 391 | * @param {function(jsonObject, *): void} callback A callback passed the loaded 392 | * json and an exception which will be null on success. 393 | * @return {!tdl.io.LoadInfo} A LoadInfo to track progress. 394 | */ 395 | tdl.io.sendJSON = function(url, jsonObject, callback) { 396 | var error = 'sendJSON failed to load url "' + url + '"'; 397 | var request; 398 | if (window.XMLHttpRequest) { 399 | request = new XMLHttpRequest(); 400 | if (request.overrideMimeType) { 401 | request.overrideMimeType('text/plain'); 402 | } 403 | } else if (window.ActiveXObject) { 404 | request = new ActiveXObject('MSXML2.XMLHTTP.3.0'); 405 | } else { 406 | throw 'XMLHttpRequest is disabled'; 407 | } 408 | var loadInfo = tdl.io.createLoadInfo(request, false); 409 | request.open('POST', url, true); 410 | var js = JSON.stringify(jsonObject); 411 | var finish = function() { 412 | if (request.readyState == 4) { 413 | var json = undefined; 414 | // HTTP reports success with a 200 status. The file protocol reports 415 | // success with zero. HTTP does not use zero as a status code (they 416 | // start at 100). 417 | // https://developer.mozilla.org/En/Using_XMLHttpRequest 418 | var success = request.status == 200 || request.status == 0; 419 | if (success) { 420 | try { 421 | json = JSON.parse(request.responseText); 422 | } catch (e) { 423 | success = false; 424 | } 425 | } 426 | loadInfo.finish(); 427 | callback(json, success ? null : 'could not load: ' + url); 428 | } 429 | }; 430 | try { 431 | request.onreadystatechange = finish; 432 | request.setRequestHeader("Content-type", "application/json"); 433 | request.send(js); 434 | } catch (e) { 435 | callback(null, 'could not load: ' + url); 436 | } 437 | return loadInfo; 438 | }; 439 | 440 | 441 | -------------------------------------------------------------------------------- /example_project/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 | 38 | tdl.provide('tdl.loader'); 39 | 40 | tdl.require('tdl.io'); 41 | 42 | /** 43 | * A Module with a loader class for helping to load muliple assets in an 44 | * asynchronous manner. 45 | * @namespace 46 | */ 47 | tdl.loader = tdl.loader || {}; 48 | 49 | /** 50 | * A simple Loader class to call some callback when everything has loaded. 51 | * @constructor 52 | * @param {!function(): void} onFinished Function to call when final item has 53 | * loaded. 54 | */ 55 | tdl.loader.Loader = function(onFinished) { 56 | this.count_ = 1; 57 | this.onFinished_ = onFinished; 58 | 59 | /** 60 | * The LoadInfo for this loader you can use to track progress. 61 | * @type {!tdl.io.LoadInfo} 62 | */ 63 | this.loadInfo = tdl.io.createLoadInfo(); 64 | }; 65 | 66 | /** 67 | * Creates a Loader for helping to load a bunch of items asychronously. 68 | * 69 | * The way you use this is as follows. 70 | * 71 | *
 72 |  * var loader = tdl.loader.createLoader(myFinishedCallback);
 73 |  * loader.loadTextFile(text1Url, callbackForText);
 74 |  * loader.loadTextFile(text2Url, callbackForText);
 75 |  * loader.loadTextFile(text3Url, callbackForText);
 76 |  * loader.finish();
 77 |  * 
78 | * 79 | * The loader guarantees that myFinishedCallback will be called after 80 | * all the items have been loaded. 81 | * 82 | * @param {!function(): void} onFinished Function to call when final item has 83 | * loaded. 84 | * @return {!tdl.loader.Loader} A Loader Object. 85 | */ 86 | tdl.loader.createLoader = function(onFinished) { 87 | return new tdl.loader.Loader(onFinished); 88 | }; 89 | 90 | /** 91 | * Loads a text file. 92 | * @param {string} url URL of scene to load. 93 | * @param {!function(string, *): void} onTextLoaded Function to call when 94 | * the file is loaded. It will be passed the contents of the file as a 95 | * string and an exception which is null on success. 96 | */ 97 | tdl.loader.Loader.prototype.loadTextFile = function(url, onTextLoaded) { 98 | var that = this; // so the function below can see "this". 99 | ++this.count_; 100 | var loadInfo = tdl.io.loadTextFile(url, function(string, exception) { 101 | onTextLoaded(string, exception); 102 | that.countDown_(); 103 | }); 104 | this.loadInfo.addChild(loadInfo); 105 | }; 106 | 107 | /** 108 | * Creates a loader that is tracked by this loader so that when the new loader 109 | * is finished it will be reported to this loader. 110 | * @param {!function(): void} onFinished Function to be called when everything 111 | * loaded with this loader has finished. 112 | * @return {!tdl.loader.Loader} The new Loader. 113 | */ 114 | tdl.loader.Loader.prototype.createLoader = function(onFinished) { 115 | var that = this; 116 | ++this.count_; 117 | var loader = tdl.loader.createLoader(function() { 118 | onFinished(); 119 | that.countDown_(); 120 | }); 121 | this.loadInfo.addChild(loader.loadInfo); 122 | return loader; 123 | }; 124 | 125 | /** 126 | * Counts down the internal count and if it gets to zero calls the callback. 127 | * @private 128 | */ 129 | tdl.loader.Loader.prototype.countDown_ = function() { 130 | --this.count_; 131 | if (this.count_ === 0) { 132 | this.onFinished_(); 133 | } 134 | }; 135 | 136 | /** 137 | * Finishes the loading process. 138 | * Actually this just calls countDown_ to account for the count starting at 1. 139 | */ 140 | tdl.loader.Loader.prototype.finish = function() { 141 | this.countDown_(); 142 | }; 143 | 144 | 145 | -------------------------------------------------------------------------------- /example_project/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 | 37 | tdl.provide('tdl.log'); 38 | 39 | tdl.require('tdl.string'); 40 | 41 | /** 42 | * A module for log. 43 | * @namespace 44 | */ 45 | tdl.log = tdl.log || {}; 46 | 47 | 48 | /** 49 | * Wrapped logging function. 50 | * @param {*} msg The message to log. 51 | */ 52 | tdl.log = function() { 53 | var str = tdl.string.argsToString(arguments); 54 | if (window.console && window.console.log) { 55 | window.console.log(str); 56 | } else if (window.dump) { 57 | window.dump(str + "\n"); 58 | } 59 | }; 60 | 61 | /** 62 | * Wrapped logging function. 63 | * @param {*} msg The message to log. 64 | */ 65 | tdl.error = function() { 66 | var str = tdl.string.argsToString(arguments); 67 | if (window.console) { 68 | if (window.console.error) { 69 | window.console.error(str); 70 | } else if (window.console.log) { 71 | window.console.log(str); 72 | } 73 | } else if (window.dump) { 74 | window.dump(str + "\n"); 75 | } 76 | }; 77 | 78 | /** 79 | * Dumps an object to the console. 80 | * 81 | * @param {!Object} obj Object to dump. 82 | * @param {string} opt_prefix string to prefix each value with. 83 | */ 84 | tdl.dumpObj = function(obj, opt_prefix) { 85 | tdl.log(tdl.string.objToString(obj, opt_prefix)); 86 | }; 87 | 88 | 89 | -------------------------------------------------------------------------------- /example_project/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 | 37 | tdl.provide('tdl.misc'); 38 | 39 | tdl.require('tdl.log'); 40 | 41 | /** 42 | * A module for misc. 43 | * @namespace 44 | */ 45 | tdl.misc = tdl.misc || {}; 46 | 47 | tdl.misc.parseUnquotedJSObjectString = function(str) { 48 | // NOTE: does not handle strings with : in them. 49 | var quoted = str.replace(/([a-zA-Z0-9_]+):/g,'"$1":') 50 | return JSON.parse(quoted); 51 | }; 52 | 53 | tdl.misc.applyUrlSettings = function(obj, opt_argumentName) { 54 | var argumentName = opt_argumentName || 'settings'; 55 | try { 56 | var s = window.location.href; 57 | var q = s.indexOf("?"); 58 | var e = s.indexOf("#"); 59 | if (e < 0) { 60 | e = s.length; 61 | } 62 | var query = s.substring(q + 1, e); 63 | //tdl.log("query:", query); 64 | var pairs = query.split("&"); 65 | //tdl.log("pairs:", pairs.length); 66 | for (var ii = 0; ii < pairs.length; ++ii) { 67 | var keyValue = pairs[ii].split("="); 68 | var key = keyValue[0]; 69 | var value = decodeURIComponent(keyValue[1]); 70 | //tdl.log(ii, ":", key, "=", value); 71 | switch (key) { 72 | case argumentName: 73 | //tdl.log(value); 74 | var settings = tdl.misc.parseUnquotedJSObjectString(value) 75 | //tdl.log("settings:", settings); 76 | tdl.misc.copyProperties(settings, obj); 77 | break; 78 | } 79 | } 80 | } catch (e) { 81 | tdl.error(e); 82 | tdl.error("settings:", settings); 83 | return; 84 | } 85 | }; 86 | 87 | /** 88 | * Copies properties from obj to dst recursively. 89 | * @private 90 | * @param {!Object} obj Object with new settings. 91 | * @param {!Object} dst Object to receive new settings. 92 | */ 93 | tdl.misc.copyProperties = function(obj, dst) { 94 | for (var name in obj) { 95 | var value = obj[name]; 96 | if (value instanceof Array) { 97 | //tdl.log("apply->: ", name, "[]"); 98 | var newDst = dst[name]; 99 | if (!newDst) { 100 | newDst = []; 101 | dst[name] = newDst; 102 | } 103 | tdl.misc.copyProperties(value, newDst); 104 | } else if (typeof value == 'object') { 105 | //tdl.log("apply->: ", name); 106 | var newDst = dst[name]; 107 | if (!newDst) { 108 | newDst = {}; 109 | dst[name] = newDst; 110 | } 111 | tdl.misc.copyProperties(value, newDst); 112 | } else { 113 | //tdl.log("apply: ", name, "=", value); 114 | dst[name] = value; 115 | } 116 | } 117 | }; 118 | 119 | 120 | -------------------------------------------------------------------------------- /example_project/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 | 37 | tdl.provide('tdl.models'); 38 | 39 | tdl.require('tdl.buffers'); 40 | 41 | /** 42 | * A module for models. 43 | * @namespace 44 | */ 45 | tdl.models = tdl.models || {}; 46 | 47 | /** 48 | * Manages a program, buffers and textures for easier drawing. 49 | * @constructor 50 | * @param {!tdl.programs.Program} program The program to render 51 | * this model with 52 | * @param {!Object.} arrays The 53 | * AttribBuffers to bind to draw this model. 54 | * @param {!Object.} textures The textures to 55 | * bind to draw this model. 56 | * @param {number} opt_mode Mode to call drawElements with. Default = 57 | * gl.TRIANGLES 58 | */ 59 | tdl.models.Model = function(program, arrays, textures, opt_mode) { 60 | this.buffers = { }; 61 | this.setBuffers(arrays); 62 | 63 | var textureUnits = { } 64 | var unit = 0; 65 | for (var texture in program.textures) { 66 | textureUnits[texture] = unit++; 67 | } 68 | 69 | this.mode = (opt_mode === undefined) ? gl.TRIANGLES : opt_mode; 70 | this.textures = textures; 71 | this.textureUnits = textureUnits; 72 | this.setProgram(program); 73 | } 74 | 75 | tdl.models.Model.prototype.setProgram = function(program) { 76 | this.program = program; 77 | } 78 | 79 | tdl.models.Model.prototype.setBuffer = function(name, array, opt_newBuffer) { 80 | var target = (name == 'indices') ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER; 81 | var b = this.buffers[name]; 82 | if (!b || opt_newBuffer) { 83 | b = new tdl.buffers.Buffer(array, target); 84 | } else { 85 | b.set(array); 86 | } 87 | this.buffers[name] = b; 88 | }; 89 | 90 | tdl.models.Model.prototype.setBuffers = function(arrays, opt_newBuffers) { 91 | var that = this; 92 | for (var name in arrays) { 93 | this.setBuffer(name, arrays[name], opt_newBuffers); 94 | } 95 | if (this.buffers.indices) { 96 | this.baseBuffer = this.buffers.indices; 97 | this.drawFunc = function(totalComponents, startOffset) { 98 | gl.drawElements(that.mode, totalComponents, gl.UNSIGNED_SHORT, startOffset); 99 | } 100 | } else { 101 | for (var key in this.buffers) { 102 | this.baseBuffer = this.buffers[key]; 103 | break; 104 | } 105 | this.drawFunc = function(totalComponents, startOffset) { 106 | gl.drawArrays(that.mode, startOffset, totalComponents); 107 | } 108 | } 109 | }; 110 | 111 | tdl.models.Model.prototype.applyUniforms_ = function(opt_uniforms) { 112 | if (opt_uniforms) { 113 | var program = this.program; 114 | for (var uniform in opt_uniforms) { 115 | program.setUniform(uniform, opt_uniforms[uniform]); 116 | } 117 | } 118 | }; 119 | 120 | /** 121 | * Sets up the shared parts of drawing this model. Uses the 122 | * program, binds the buffers, sets the textures. 123 | * 124 | * @param {!Object.} opt_uniforms An object of names to 125 | * values to set on this models uniforms. 126 | * @param {!Object.} opt_textures An object of names to 127 | * textures to set on this models uniforms. 128 | */ 129 | tdl.models.Model.prototype.drawPrep = function() { 130 | var program = this.program; 131 | var buffers = this.buffers; 132 | var textures = this.textures; 133 | 134 | program.use(); 135 | for (var buffer in buffers) { 136 | var b = buffers[buffer]; 137 | if (buffer == 'indices') { 138 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.buffer()); 139 | } else { 140 | var attrib = program.attrib[buffer]; 141 | if (attrib) { 142 | attrib(b); 143 | } 144 | } 145 | } 146 | 147 | this.applyUniforms_(textures); 148 | for (var ii = 0; ii < arguments.length; ++ii) { 149 | this.applyUniforms_(arguments[ii]); 150 | } 151 | }; 152 | 153 | /** 154 | * Draws this model. 155 | * 156 | * After calling tdl.models.Model.drawPrep you can call this 157 | * function multiple times to draw this model. 158 | * 159 | * @param {!Object.} opt_uniforms An object of names to 160 | * values to set on this models uniforms. 161 | * @param {!Object.} opt_textures An object of names to 162 | * textures to set on this models uniforms. 163 | */ 164 | tdl.models.Model.prototype.draw = function() { 165 | var buffers = this.buffers; 166 | // if no indices buffer then assume drawFunc is drawArrays and thus 167 | // totalComponents is the number of vertices (not indices). 168 | var totalComponents = buffers.indices? buffers.indices.totalComponents(): buffers.position.numElements(); 169 | var startOffset = 0; 170 | for (var ii = 0; ii < arguments.length; ++ii) { 171 | var arg = arguments[ii]; 172 | if (typeof arg == 'number') { 173 | switch (ii) { 174 | case 0: 175 | totalComponents = arg; 176 | break; 177 | case 1: 178 | startOffset = arg; 179 | break; 180 | default: 181 | throw 'unvalid argument'; 182 | } 183 | } else { 184 | this.applyUniforms_(arg); 185 | } 186 | } 187 | 188 | this.drawFunc(totalComponents, startOffset); 189 | }; 190 | -------------------------------------------------------------------------------- /example_project/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 | 38 | tdl.provide('tdl.programs'); 39 | 40 | tdl.require('tdl.log'); 41 | tdl.require('tdl.string'); 42 | tdl.require('tdl.webgl'); 43 | 44 | /** 45 | * A module for programs. 46 | * @namespace 47 | */ 48 | tdl.programs = tdl.programs || {}; 49 | 50 | /** 51 | * Loads a program from script tags. 52 | * @param {string} vertexShaderId The id of the script tag that contains the 53 | * vertex shader source. 54 | * @param {string} fragmentShaderId The id of the script tag that contains the 55 | * fragment shader source. 56 | * @return {tdl.programs.Program} The created program. 57 | */ 58 | tdl.programs.loadProgramFromScriptTags = function( 59 | vertexShaderId, fragmentShaderId) { 60 | var vertElem = document.getElementById(vertexShaderId); 61 | var fragElem = document.getElementById(fragmentShaderId); 62 | if (!vertElem) { 63 | throw("Can't find vertex program tag: " + vertexShaderId); 64 | } 65 | if (!fragElem) { 66 | throw("Can't find fragment program tag: " + fragmentShaderId); 67 | } 68 | return tdl.programs.loadProgram( 69 | document.getElementById(vertexShaderId).text, 70 | document.getElementById(fragmentShaderId).text); 71 | }; 72 | 73 | tdl.programs.makeProgramId = function(vertexShader, fragmentShader) { 74 | return vertexShader + fragmentShader; 75 | }; 76 | 77 | /** 78 | * Loads a program. 79 | * @param {string} vertexShader The vertex shader source. 80 | * @param {string} fragmentShader The fragment shader source. 81 | * @param {!function(error)) opt_asyncCallback. Called with 82 | * undefined if success or string if failure. 83 | * @return {tdl.programs.Program} The created program. 84 | */ 85 | tdl.programs.loadProgram = function(vertexShader, fragmentShader, opt_asyncCallback) { 86 | var id = tdl.programs.makeProgramId(vertexShader, fragmentShader); 87 | tdl.programs.init_(); 88 | var program = gl.tdl.programs.programDB[id]; 89 | if (program) { 90 | if (opt_asyncCallback) { 91 | setTimeout(function() { opt_asyncCallback(); }, 1); 92 | } 93 | return program; 94 | } 95 | try { 96 | program = new tdl.programs.Program(vertexShader, fragmentShader, opt_asyncCallback); 97 | } catch (e) { 98 | tdl.error(e); 99 | return null; 100 | } 101 | if (!opt_asyncCallback) { 102 | gl.tdl.programs.programDB[id] = program; 103 | } 104 | return program; 105 | }; 106 | 107 | /** 108 | * A object to manage a WebGLProgram. 109 | * @constructor 110 | * @param {string} vertexShader The vertex shader source. 111 | * @param {string} fragmentShader The fragment shader source. 112 | * @param {!function(error)) opt_asyncCallback. Called with 113 | * undefined if success or string if failure. 114 | */ 115 | tdl.programs.Program = function(vertexShader, fragmentShader, opt_asyncCallback) { 116 | var that = this; 117 | this.programId = tdl.programs.makeProgramId(vertexShader, fragmentShader); 118 | this.asyncCallback = opt_asyncCallback; 119 | 120 | var shaderId; 121 | var program; 122 | var vs; 123 | var fs; 124 | 125 | /** 126 | * Loads a shader. 127 | * @param {!WebGLContext} gl The WebGLContext to use. 128 | * @param {string} shaderSource The shader source. 129 | * @param {number} shaderType The type of shader. 130 | * @return {!WebGLShader} The created shader. 131 | */ 132 | var loadShader = function(gl, shaderSource, shaderType) { 133 | shaderId = shaderSource + shaderType; 134 | tdl.programs.init_(); 135 | var shader = gl.tdl.programs.shaderDB[shaderId]; 136 | if (shader) { 137 | return shader; 138 | } 139 | 140 | // Create the shader object 141 | var shader = gl.createShader(shaderType); 142 | 143 | // Load the shader source 144 | gl.shaderSource(shader, shaderSource); 145 | 146 | // Compile the shader 147 | gl.compileShader(shader); 148 | 149 | // Check the compile status 150 | if (!that.asyncCallback) { 151 | checkShader(shader); 152 | } 153 | return shader; 154 | } 155 | 156 | var checkShader = function(shader) { 157 | var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); 158 | if (!compiled && !gl.isContextLost()) { 159 | // Something went wrong during compilation; get the error 160 | tdl.programs.lastError = gl.getShaderInfoLog(shader); 161 | gl.deleteShader(shader); 162 | throw("*** Error compiling shader :" + tdl.programs.lastError); 163 | } 164 | gl.tdl.programs.shaderDB[shaderId] = shader; 165 | }; 166 | 167 | /** 168 | * Loads shaders from script tags, creates a program, attaches the shaders and 169 | * links. 170 | * @param {!WebGLContext} gl The WebGLContext to use. 171 | * @param {string} vertexShader The vertex shader. 172 | * @param {string} fragmentShader The fragment shader. 173 | * @return {!WebGLProgram} The created program. 174 | */ 175 | var loadProgram = function(gl, vertexShader, fragmentShader) { 176 | var e; 177 | try { 178 | vs = loadShader(gl, vertexShader, gl.VERTEX_SHADER); 179 | fs = loadShader(gl, fragmentShader, gl.FRAGMENT_SHADER); 180 | program = gl.createProgram(); 181 | gl.attachShader(program, vs); 182 | gl.attachShader(program, fs); 183 | linkProgram(gl, program); 184 | } catch (e) { 185 | deleteAll(e); 186 | } 187 | return program; 188 | }; 189 | 190 | var deleteAll = function(e) { 191 | if (vs) { gl.deleteShader(vs) } 192 | if (fs) { gl.deleteShader(fs) } 193 | if (program) { gl.deleteProgram(program) } 194 | throw e; 195 | }; 196 | 197 | /** 198 | * Links a WebGL program, throws if there are errors. 199 | * @param {!WebGLContext} gl The WebGLContext to use. 200 | * @param {!WebGLProgram} program The WebGLProgram to link. 201 | */ 202 | var linkProgram = function(gl, program) { 203 | // Link the program 204 | gl.linkProgram(program); 205 | 206 | // Check the link status 207 | if (!that.asyncCallback) { 208 | checkProgram(program); 209 | } 210 | }; 211 | 212 | var checkProgram = function(program) { 213 | var linked = gl.getProgramParameter(program, gl.LINK_STATUS); 214 | if (!linked && !gl.isContextLost()) { 215 | // something went wrong with the link 216 | tdl.programs.lastError = gl.getProgramInfoLog (program); 217 | throw("*** Error in program linking:" + tdl.programs.lastError); 218 | } 219 | }; 220 | 221 | // Compile shaders 222 | var program = loadProgram(gl, vertexShader, fragmentShader); 223 | if (!program && !gl.isContextLost()) { 224 | throw ("could not compile program"); 225 | } 226 | 227 | // TODO(gman): remove the need for this. 228 | function flatten(array){ 229 | var flat = []; 230 | for (var i = 0, l = array.length; i < l; i++) { 231 | var type = Object.prototype.toString.call(array[i]).split(' ').pop().split(']').shift().toLowerCase(); 232 | if (type) { flat = flat.concat(/^(array|collection|arguments|object)$/.test(type) ? flatten(array[i]) : array[i]); } 233 | } 234 | return flat; 235 | } 236 | 237 | function createSetters(program) { 238 | // Look up attribs. 239 | var attribs = { 240 | }; 241 | // Also make a plain table of the locs. 242 | var attribLocs = { 243 | }; 244 | 245 | function createAttribSetter(info, index) { 246 | if (info.size != 1) { 247 | throw("arrays of attribs not handled"); 248 | } 249 | return function(b) { 250 | gl.bindBuffer(gl.ARRAY_BUFFER, b.buffer()); 251 | gl.enableVertexAttribArray(index); 252 | gl.vertexAttribPointer( 253 | index, b.numComponents(), b.type(), b.normalize(), b.stride(), b.offset()); 254 | }; 255 | } 256 | 257 | var numAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); 258 | for (var ii = 0; ii < numAttribs; ++ii) { 259 | var info = gl.getActiveAttrib(program, ii); 260 | if (!info) { 261 | break; 262 | } 263 | var name = info.name; 264 | if (tdl.string.endsWith(name, "[0]")) { 265 | name = name.substr(0, name.length - 3); 266 | } 267 | var index = gl.getAttribLocation(program, info.name); 268 | attribs[name] = createAttribSetter(info, index); 269 | attribLocs[name] = index 270 | } 271 | 272 | // Look up uniforms 273 | var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); 274 | var uniforms = { 275 | }; 276 | var textureUnit = 0; 277 | 278 | function createUniformSetter(info) { 279 | var loc = gl.getUniformLocation(program, info.name); 280 | var type = info.type; 281 | if (info.size > 1 && tdl.string.endsWith(info.name, "[0]")) { 282 | // It's an array. 283 | if (type == gl.FLOAT) 284 | return function() { 285 | var old; 286 | return function(v) { 287 | if (v !== old) { 288 | old = v; 289 | gl.uniform1fv(loc, v); 290 | } 291 | }; 292 | }(); 293 | if (type == gl.FLOAT_VEC2) 294 | return function() { 295 | // I hope they don't use -1,-1 as their first draw 296 | var old = new Float32Array([-1, -1]); 297 | return function(v) { 298 | if (v[0] != old[0] || v[1] != old[1]) { 299 | gl.uniform2fv(loc, v); 300 | } 301 | }; 302 | }(); 303 | if (type == gl.FLOAT_VEC3) 304 | return function() { 305 | // I hope they don't use -1,-1,-1 as their first draw 306 | var old = new Float32Array([-1, -1, -1]); 307 | return function(v) { 308 | if (v[0] != old[0] || v[1] != old[1] || v[2] != old[2]) { 309 | gl.uniform3fv(loc, v); 310 | } 311 | }; 312 | }(); 313 | if (type == gl.FLOAT_VEC4) 314 | return function(v) { gl.uniform4fv(loc, v); }; 315 | if (type == gl.INT) 316 | return function(v) { gl.uniform1iv(loc, v); }; 317 | if (type == gl.INT_VEC2) 318 | return function(v) { gl.uniform2iv(loc, v); }; 319 | if (type == gl.INT_VEC3) 320 | return function(v) { gl.uniform3iv(loc, v); }; 321 | if (type == gl.INT_VEC4) 322 | return function(v) { gl.uniform4iv(loc, v); }; 323 | if (type == gl.BOOL) 324 | return function(v) { gl.uniform1iv(loc, v); }; 325 | if (type == gl.BOOL_VEC2) 326 | return function(v) { gl.uniform2iv(loc, v); }; 327 | if (type == gl.BOOL_VEC3) 328 | return function(v) { gl.uniform3iv(loc, v); }; 329 | if (type == gl.BOOL_VEC4) 330 | return function(v) { gl.uniform4iv(loc, v); }; 331 | if (type == gl.FLOAT_MAT2) 332 | return function(v) { gl.uniformMatrix2fv(loc, false, v); }; 333 | if (type == gl.FLOAT_MAT3) 334 | return function(v) { gl.uniformMatrix3fv(loc, false, v); }; 335 | if (type == gl.FLOAT_MAT4) 336 | return function(v) { gl.uniformMatrix4fv(loc, false, v); }; 337 | if (type == gl.SAMPLER_2D || type == gl.SAMPLER_CUBE) { 338 | var units = []; 339 | for (var ii = 0; ii < info.size; ++ii) { 340 | units.push(textureUnit++); 341 | } 342 | return function(units) { 343 | return function(v) { 344 | gl.uniform1iv(loc, units); 345 | v.bindToUnit(units); 346 | }; 347 | }(units); 348 | } 349 | throw ("unknown type: 0x" + type.toString(16)); 350 | } else { 351 | if (type == gl.FLOAT) 352 | return function(v) { gl.uniform1f(loc, v); }; 353 | if (type == gl.FLOAT_VEC2) 354 | return function(v) { gl.uniform2fv(loc, v); }; 355 | if (type == gl.FLOAT_VEC3) 356 | return function(v) { gl.uniform3fv(loc, v); }; 357 | if (type == gl.FLOAT_VEC4) 358 | return function(v) { gl.uniform4fv(loc, v); }; 359 | if (type == gl.INT) 360 | return function(v) { gl.uniform1i(loc, v); }; 361 | if (type == gl.INT_VEC2) 362 | return function(v) { gl.uniform2iv(loc, v); }; 363 | if (type == gl.INT_VEC3) 364 | return function(v) { gl.uniform3iv(loc, v); }; 365 | if (type == gl.INT_VEC4) 366 | return function(v) { gl.uniform4iv(loc, v); }; 367 | if (type == gl.BOOL) 368 | return function(v) { gl.uniform1i(loc, v); }; 369 | if (type == gl.BOOL_VEC2) 370 | return function(v) { gl.uniform2iv(loc, v); }; 371 | if (type == gl.BOOL_VEC3) 372 | return function(v) { gl.uniform3iv(loc, v); }; 373 | if (type == gl.BOOL_VEC4) 374 | return function(v) { gl.uniform4iv(loc, v); }; 375 | if (type == gl.FLOAT_MAT2) 376 | return function(v) { gl.uniformMatrix2fv(loc, false, v); }; 377 | if (type == gl.FLOAT_MAT3) 378 | return function(v) { gl.uniformMatrix3fv(loc, false, v); }; 379 | if (type == gl.FLOAT_MAT4) 380 | return function(v) { gl.uniformMatrix4fv(loc, false, v); }; 381 | if (type == gl.SAMPLER_2D || type == gl.SAMPLER_CUBE) { 382 | return function(unit) { 383 | return function(v) { 384 | gl.uniform1i(loc, unit); 385 | v.bindToUnit(unit); 386 | }; 387 | }(textureUnit++); 388 | } 389 | throw ("unknown type: 0x" + type.toString(16)); 390 | } 391 | } 392 | 393 | var textures = {}; 394 | 395 | for (var ii = 0; ii < numUniforms; ++ii) { 396 | var info = gl.getActiveUniform(program, ii); 397 | if (!info) { 398 | break; 399 | } 400 | name = info.name; 401 | if (tdl.string.endsWith(name, "[0]")) { 402 | name = name.substr(0, name.length - 3); 403 | } 404 | var setter = createUniformSetter(info); 405 | uniforms[name] = setter; 406 | if (info.type == gl.SAMPLER_2D || info.type == gl.SAMPLER_CUBE) { 407 | textures[name] = setter; 408 | } 409 | } 410 | 411 | that.textures = textures; 412 | that.attrib = attribs; 413 | that.attribLoc = attribLocs; 414 | that.uniform = uniforms; 415 | } 416 | createSetters(program); 417 | 418 | this.loadNewShaders = function(vertexShaderSource, fragmentShaderSource) { 419 | var program = loadProgram(gl, vertexShaderSource, fragmentShaderSource); 420 | if (!program && !gl.isContextLost()) { 421 | throw ("could not compile program"); 422 | } 423 | that.program = program; 424 | createSetters(); 425 | }; 426 | 427 | this.program = program; 428 | this.good = this.asyncCallback ? false : true; 429 | 430 | var checkLater = function() { 431 | var e; 432 | try { 433 | checkShader(vs); 434 | checkShader(fs); 435 | checkProgram(program); 436 | } catch (e) { 437 | that.asyncCallback(e.toString()); 438 | return; 439 | } 440 | gl.tdl.programs.programDB[that.programId] = this; 441 | that.asyncCallback(); 442 | }; 443 | if (this.asyncCallback) { 444 | setTimeout(checkLater, 1000); 445 | } 446 | }; 447 | 448 | tdl.programs.handleContextLost_ = function() { 449 | if (gl.tdl && gl.tdl.programs && gl.tdl.programs.shaderDB) { 450 | delete gl.tdl.programs.shaderDB; 451 | delete gl.tdl.programs.programDB; 452 | } 453 | }; 454 | 455 | tdl.programs.init_ = function() { 456 | if (!gl.tdl.programs) { 457 | gl.tdl.programs = { }; 458 | tdl.webgl.registerContextLostHandler(gl.canvas, tdl.programs.handleContextLost_, true); 459 | } 460 | if (!gl.tdl.programs.shaderDB) { 461 | gl.tdl.programs.shaderDB = { }; 462 | gl.tdl.programs.programDB = { }; 463 | } 464 | }; 465 | 466 | tdl.programs.Program.prototype.use = function() { 467 | gl.useProgram(this.program); 468 | }; 469 | 470 | //function dumpValue(msg, name, value) { 471 | // var str; 472 | // if (value.length) { 473 | // str = value[0].toString(); 474 | // for (var ii = 1; ii < value.length; ++ii) { 475 | // str += "," + value[ii]; 476 | // } 477 | // } else { 478 | // str = value.toString(); 479 | // } 480 | // tdl.log(msg + name + ": " + str); 481 | //} 482 | 483 | tdl.programs.Program.prototype.setUniform = function(uniform, value) { 484 | var func = this.uniform[uniform]; 485 | if (func) { 486 | //dumpValue("SET UNI:", uniform, value); 487 | func(value); 488 | } 489 | }; 490 | 491 | 492 | -------------------------------------------------------------------------------- /example_project/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 rules for 41 | * multiplication as described on Brougham Bridge: 42 | * i^2 = j^2 = k^2 = ijk = -1. 43 | * 44 | */ 45 | 46 | tdl.provide('tdl.quaternions'); 47 | 48 | /** 49 | * A Module for quaternion math. 50 | * @namespace 51 | */ 52 | tdl.quaternions = tdl.quaternions || {}; 53 | 54 | /** 55 | * A Quaternion. 56 | * @type {!Array.} 57 | */ 58 | tdl.quaternions.Quaternion = goog.typedef; 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 vector by evaluating 408 | * 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 | [2 * (qXqY - qWqZ) / d, 442 | (qWqW - qXqX + qYqY - qZqZ) / d, 443 | 2 * (qWqX + qYqZ) / d, 0], 444 | [2 * (qWqY + qXqZ) / d, 445 | 2 * (qYqZ - qWqX) / d, 446 | (qWqW - qXqX - qYqY + qZqZ) / d, 0], 447 | [0, 0, 0, 1]]; 448 | }; 449 | 450 | /** 451 | * Computes a quaternion whose rotation is equivalent to the given matrix. 452 | * @param {(!tdl.math.Matrix4|!tdl.math.Matrix3)} m A 3-by-3 or 4-by-4 453 | * rotation matrix. 454 | * @return {!tdl.quaternions.Quaternion} A quaternion q such that 455 | * quaternions.quaternionToRotation(q) is m. 456 | */ 457 | tdl.quaternions.rotationToQuaternion = function(m) { 458 | var u; 459 | var v; 460 | var w; 461 | 462 | // Choose u, v, and w such that u is the index of the biggest diagonal entry 463 | // of m, and u v w is an even permutation of 0 1 and 2. 464 | if (m[0][0] > m[1][1] && m[0][0] > m[2][2]) { 465 | u = 0; 466 | v = 1; 467 | w = 2; 468 | } else if (m[1][1] > m[0][0] && m[1][1] > m[2][2]) { 469 | u = 1; 470 | v = 2; 471 | w = 0; 472 | } else { 473 | u = 2; 474 | v = 0; 475 | w = 1; 476 | } 477 | 478 | var r = Math.sqrt(1 + m[u][u] - m[v][v] - m[w][w]); 479 | var q = []; 480 | q[u] = 0.5 * r; 481 | q[v] = 0.5 * (m[v][u] + m[u][v]) / r; 482 | q[w] = 0.5 * (m[u][w] + m[w][u]) / r; 483 | q[3] = 0.5 * (m[v][w] - m[w][v]) / r; 484 | 485 | return q; 486 | }; 487 | 488 | -------------------------------------------------------------------------------- /example_project/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 | 37 | tdl.provide('tdl.screenshot'); 38 | 39 | tdl.require('tdl.io'); 40 | tdl.require('tdl.log'); 41 | 42 | /** 43 | * takes a screenshot of a canvas. Sends it to the server to be saved. 44 | */ 45 | tdl.screenshot.takeScreenshot = function(canvas) { 46 | tdl.io.sendJSON( 47 | this.url, 48 | {cmd: 'screenshot', dataURL: canvas.toDataURL()}, 49 | function() {}); 50 | }; 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /example_project/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 | 38 | tdl.provide('tdl.shader'); 39 | 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|in)\s+\S+\s+(\w+)\s*(\[\d+\])?\s*;/g; 120 | var match = null; 121 | while ((match = re.exec(vertex)) != null) { 122 | var glslName = match[2]; 123 | var jsName = tdl.shader.glslNameToJs_(glslName); 124 | if (match[1] == "uniform") { 125 | this[jsName + "Loc"] = this.getUniform(glslName); 126 | } else if (match[1] == "attribute" || match[1] == "in") { 127 | this[jsName + "Loc"] = this.getAttribute(glslName); 128 | } 129 | } 130 | re = /(uniform)\s+\S+\s+(\w+)\s*(\[\d+\])?\s*;/g; 131 | match = null; 132 | while ((match = re.exec(fragment)) != null) { 133 | var glslName = match[2]; 134 | var jsName = tdl.shader.glslNameToJs_(glslName); 135 | if (match[1] == "uniform") { 136 | this[jsName + "Loc"] = this.getUniform(glslName); 137 | } 138 | } 139 | } 140 | 141 | /** 142 | * Binds the shader's program. 143 | */ 144 | tdl.shader.Shader.prototype.bind = function() { 145 | this.gl.useProgram(this.program); 146 | } 147 | 148 | /** 149 | * Helper for loading a shader. 150 | * @private 151 | */ 152 | tdl.shader.Shader.prototype.loadShader = function(type, shaderSrc) { 153 | var shader = this.gl.createShader(type); 154 | if (shader == null) { 155 | return null; 156 | } 157 | 158 | // Load the shader source 159 | this.gl.shaderSource(shader, shaderSrc); 160 | // Compile the shader 161 | this.gl.compileShader(shader); 162 | // Check the compile status 163 | if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { 164 | var infoLog = this.gl.getShaderInfoLog(shader); 165 | tdl.error("Error compiling shader:\n" + infoLog); 166 | this.gl.deleteShader(shader); 167 | return null; 168 | } 169 | return shader; 170 | } 171 | 172 | /** 173 | * Helper for looking up an attribute's location. 174 | * @private 175 | */ 176 | tdl.shader.Shader.prototype.getAttribute = function(name) { 177 | return this.gl.getAttribLocation(this.program, name); 178 | }; 179 | 180 | /** 181 | * Helper for looking up an attribute's location. 182 | * @private 183 | */ 184 | tdl.shader.Shader.prototype.getUniform = function(name) { 185 | return this.gl.getUniformLocation(this.program, name); 186 | } 187 | -------------------------------------------------------------------------------- /example_project/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 | 37 | tdl.provide('tdl.string'); 38 | 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 | * @param {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 | * @param {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 | */ 69 | tdl.string.argsToString = function(args) { 70 | var lastArgWasNumber = false; 71 | var numArgs = args.length; 72 | var strs = []; 73 | for (var ii = 0; ii < numArgs; ++ii) { 74 | var arg = args[ii]; 75 | if (arg === undefined) { 76 | strs.push('undefined'); 77 | } else if (typeof arg == 'number') { 78 | if (lastArgWasNumber) { 79 | strs.push(", "); 80 | } 81 | if (arg == Math.floor(arg)) { 82 | strs.push(arg.toFixed(0)); 83 | } else { 84 | strs.push(arg.toFixed(3)); 85 | } 86 | lastArgWasNumber = true; 87 | } else if (window.Float32Array && arg instanceof Float32Array) { 88 | // TODO(gman): Make this handle other types of arrays. 89 | strs.push(tdl.string.argsToString(arg)); 90 | } else { 91 | strs.push(arg.toString()); 92 | lastArgWasNumber = false; 93 | } 94 | } 95 | return strs.join(""); 96 | }; 97 | 98 | /** 99 | * Converts an object into a string. Similar to JSON.stringify but just used 100 | * for debugging. 101 | */ 102 | tdl.string.objToString = function(obj, opt_prefix) { 103 | var strs = []; 104 | 105 | function objToString(obj, opt_prefix) { 106 | opt_prefix = opt_prefix || ""; 107 | if (typeof obj == 'object') { 108 | if (obj.length !== undefined) { 109 | for (var ii = 0; ii < obj.length; ++ii) { 110 | objToString(obj[ii], opt_prefix + "[" + ii + "]"); 111 | } 112 | } else { 113 | for (var name in obj) { 114 | objToString(obj[name], opt_prefix + "." + name); 115 | } 116 | } 117 | } else { 118 | strs.push(tdl.string.argsToString([opt_prefix, ": ", obj])); 119 | } 120 | } 121 | 122 | objToString(obj); 123 | 124 | return strs.join("\n"); 125 | }; 126 | 127 | 128 | -------------------------------------------------------------------------------- /example_project/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 | 38 | tdl.provide('tdl.sync'); 39 | 40 | tdl.require('tdl.log'); 41 | tdl.require('tdl.io'); 42 | tdl.require('tdl.misc'); 43 | 44 | /** 45 | * A module for sync. 46 | * @namespace 47 | */ 48 | tdl.sync = tdl.sync || {}; 49 | 50 | /** 51 | * Manages synchronizing settings across browsers. Requires a server 52 | * running to support it. Note that even if you don't want to sync 53 | * across browsers you can still use the SyncManager. 54 | * 55 | * @constructor 56 | * @param {!Object} settings The object that contains the settings you 57 | * want kept in sync. 58 | */ 59 | tdl.sync.SyncManager = function(settings, opt_callback) { 60 | this.settings = settings; 61 | this.putCount = 0; 62 | this.getCount = 0; 63 | this.callback = opt_callback || function() {}; 64 | 65 | // This probably should not be here. 66 | tdl.misc.applyUrlSettings(settings); 67 | } 68 | 69 | /** 70 | * Initialize the sync manager to start syncing settings with a server. 71 | * @param {string} server domain name of server. 72 | * @param {number} port port of server. 73 | * @param {boolean} slave true if this page is a slave. Slaves only receive 74 | * settings from the server. Non slaves send settings the server. 75 | */ 76 | tdl.sync.SyncManager.prototype.init = function(url, slave) { 77 | var that = this; 78 | this.sync = true; 79 | this.slave = slave; 80 | this.socket = new WebSocket(url); 81 | this.opened = false; 82 | this.queued = []; 83 | this.socket.onopen = function(event) { 84 | tdl.log("SOCKET OPENED!"); 85 | that.opened = true; 86 | for (var ii = 0; ii < that.queued.length; ++ii) { 87 | var settings = that.queued[ii]; 88 | ++that.putCount; 89 | tdl.log("--PUT:[", that.putCount, "]-------------"); 90 | tdl.log(settings); 91 | that.socket.send(settings); 92 | } 93 | that.queued = []; 94 | }; 95 | this.socket.onerror = function(event) { 96 | tdl.log("SOCKET ERROR!"); 97 | }; 98 | this.socket.onclose = function(event) { 99 | tdl.log("SOCKET CLOSED!"); 100 | }; 101 | this.socket.onmessage = function(event) { 102 | ++that.getCount; 103 | tdl.log("--GET:[", that.getCount, ":", event.type, "]-------------"); 104 | var obj = JSON.parse(event.data); 105 | tdl.dumpObj(obj); 106 | tdl.misc.copyProperties(obj, that.settings); 107 | that.callback(obj); 108 | }; 109 | }; 110 | 111 | /** 112 | * Sets the settings. 113 | * 114 | * If we are synchronizing settings the settings are sent to the server. 115 | * Otherwise they are applied directy. 116 | * 117 | * @param {!Object} settings Object with new settings. 118 | */ 119 | tdl.sync.SyncManager.prototype.setSettings = function(settings) { 120 | if (this.sync) { 121 | if (!this.slave) { 122 | if (this.socket) { 123 | if (!this.opened) { 124 | this.queued.push(JSON.stringify(settings)); 125 | } else { 126 | ++this.putCount; 127 | tdl.log("--PUT:[", this.putCount, "]-------------"); 128 | tdl.dumpObj(settings); 129 | this.socket.send(JSON.stringify(settings)); 130 | } 131 | } 132 | } 133 | } else { 134 | tdl.misc.copyProperties(settings, this.settings); 135 | this.callback(settings); 136 | } 137 | }; 138 | 139 | 140 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /frontend/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npm run build 4 | cp -r ./node_modules/monaco-editor/min/vs ../cli/dist/assets 5 | #./package_monaco.py 6 | -------------------------------------------------------------------------------- /frontend/code.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{_codestage_title_}} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | 33 | {{_codestage_google_analytics_}} 34 | 35 | 36 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codestage", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build --emptyOutDir", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "monaco-editor": "^0.44.0", 13 | "solid-icons": "^1.1.0", 14 | "solid-js": "^1.7.8" 15 | }, 16 | "devDependencies": { 17 | "vite": "^4.4.5", 18 | "vite-plugin-solid": "^2.7.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/package_monaco.py: -------------------------------------------------------------------------------- 1 | #!/opt/homebrew/bin/python3 2 | 3 | import glob 4 | import glob 5 | import os 6 | 7 | types = ('*.js', '*.css', '*.ttf') 8 | files_grabbed = [] 9 | 10 | for files in types: 11 | files_grabbed.extend(glob.glob('../cli/dist/assets/' + files,recursive=False)) 12 | 13 | print(files_grabbed) 14 | 15 | subfolder_files_grabbed = [] 16 | 17 | for files in types: 18 | subfolder_files_grabbed.extend(glob.glob('../cli/dist/assets/vs/**/'+files,recursive=True)) 19 | 20 | 21 | print(subfolder_files_grabbed) 22 | 23 | exisiting_files = {} 24 | 25 | for f in subfolder_files_grabbed: 26 | filename = os.path.basename(f) 27 | exisiting_files[filename] = f 28 | 29 | print(exisiting_files) 30 | 31 | for f in files_grabbed: 32 | filename = os.path.basename(f) 33 | if filename in exisiting_files: 34 | os.remove(f) 35 | 36 | 37 | -------------------------------------------------------------------------------- /frontend/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Sorry, your browser does not support inline SVG. 12 | -------------------------------------------------------------------------------- /frontend/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { createSignal, onMount, For } from 'solid-js' 2 | import styles from './App.module.css' 3 | import MenuItem from './MenuItem'; 4 | import theme from "./assets/CodeStage.json"; 5 | import logo from "./assets/logo2.png"; 6 | import { IoMenuSharp } from 'solid-icons/io' 7 | import { AiOutlineFile } from 'solid-icons/ai' 8 | import { FaBrandsSquareGit } from 'solid-icons/fa' 9 | import { FaSolidPlay } from 'solid-icons/fa' 10 | import * as monaco from "monaco-editor"; 11 | 12 | function App() { 13 | 14 | function prefixSubPath(path) { 15 | return "$$_codestage_prefix_$$" + (path ? '/' + path : ''); 16 | } 17 | 18 | function isMobile() { 19 | if (screen.width <= 760) { 20 | return true; 21 | } else { 22 | return false; 23 | } 24 | } 25 | 26 | let menu = null; 27 | let main = null; 28 | let container = null; 29 | let movableBar = null; 30 | let output = null; 31 | let outputWindow = null; 32 | let editor = null; 33 | let loadedFiles = new Map(); 34 | let highlightRanges = []; 35 | 36 | const [content, setContent] = createSignal({}); 37 | let currentFolder = {}; 38 | const [files, setFiles] = createSignal([]); 39 | let fileRefs = []; 40 | const [isMenuOpen, setIsMenuOpen] = createSignal(false); 41 | 42 | function onMenuButtonClicked(e) { 43 | console.log("onMenuButtonClicked called") 44 | if (isMenuOpen()) { 45 | setIsMenuOpen(false); 46 | } else { 47 | console.log("set true") 48 | setIsMenuOpen(true); 49 | } 50 | } 51 | 52 | function openRepo(url) { 53 | window.open(url, "_blank"); 54 | } 55 | 56 | async function displayReadme() { 57 | const filePath = "readme/index.html"; 58 | let file = await fetch(filePath); 59 | let html_string = await file.text(); 60 | 61 | let newHTMLDocument = 62 | document.implementation.createHTMLDocument("preview"); 63 | newHTMLDocument.documentElement.innerHTML = html_string; 64 | 65 | let existing = newHTMLDocument.head.getElementsByTagName("base"); 66 | if (existing.length > 0) { 67 | for (let e = 0; e < existing.length; ++e) { 68 | existing[e].setAttribute( 69 | "href", 70 | "/" + prefixSubPath(currentFolder.folder) + '/' 71 | ); 72 | } 73 | } else { 74 | let base = document.createElement("base"); 75 | base.setAttribute( 76 | "href", 77 | "/" + prefixSubPath(currentFolder.folder) + '/' 78 | ); 79 | newHTMLDocument.head.appendChild(base); 80 | } 81 | 82 | let iframeDoc = outputWindow.contentDocument; 83 | iframeDoc.removeChild(iframeDoc.documentElement); 84 | outputWindow.srcdoc = 85 | newHTMLDocument.documentElement.innerHTML; 86 | } 87 | 88 | async function onRun() { 89 | const filePath = currentFolder.folder + "/index.html"; 90 | const model = await fetchFileByPath(filePath); 91 | const html_string = model.getValue(); 92 | 93 | let newHTMLDocument = 94 | document.implementation.createHTMLDocument("preview"); 95 | newHTMLDocument.documentElement.innerHTML = html_string; 96 | 97 | let styleTags = newHTMLDocument.getElementsByTagName("link"); 98 | 99 | for (let t = 0; t < styleTags.length; ++t) { 100 | const href = styleTags[t].getAttribute("href"); 101 | 102 | if (href) { 103 | for (let f of currentFolder.files) { 104 | if (f.filename === href) { 105 | const path = currentFolder.folder + "/" + href; 106 | 107 | const model = await fetchFileByPath(path); 108 | const style_string = model.getValue(); 109 | 110 | styleTags[t].remove(); 111 | const styleTag = newHTMLDocument.createElement("style"); 112 | 113 | styleTag.textContent = style_string; 114 | newHTMLDocument.head.appendChild(styleTag); 115 | break; 116 | } 117 | } 118 | } 119 | } 120 | 121 | //overwrite scripts 122 | let scriptTags = newHTMLDocument.getElementsByTagName("script"); 123 | for (let t = 0; t < scriptTags.length; ++t) { 124 | const src = scriptTags[t].getAttribute("src"); 125 | 126 | if (src) { 127 | for (let f of currentFolder.files) { 128 | if (f.filename === src) { 129 | const path = currentFolder.folder + "/" + src; 130 | scriptTags[t].removeAttribute("src"); 131 | const model = await fetchFileByPath(path); 132 | const script_string = model.getValue(); 133 | scriptTags[t].textContent = script_string; 134 | break; 135 | } 136 | } 137 | } 138 | } 139 | 140 | let existing = newHTMLDocument.head.getElementsByTagName("base"); 141 | if (existing.length > 0) { 142 | for (let e = 0; e < existing.length; ++e) { 143 | existing[e].setAttribute( 144 | "href", 145 | "/" + prefixSubPath(currentFolder.folder) + '/' 146 | ); 147 | } 148 | } else { 149 | let base = document.createElement("base"); 150 | base.setAttribute( 151 | "href", 152 | "/" + prefixSubPath(currentFolder.folder) + '/' 153 | ); 154 | newHTMLDocument.head.appendChild(base); 155 | } 156 | 157 | let iframeDoc = outputWindow.contentDocument; 158 | iframeDoc.removeChild(iframeDoc.documentElement); 159 | outputWindow.srcdoc = 160 | newHTMLDocument.documentElement.innerHTML; 161 | } 162 | 163 | function getFirstFolderDetails(defaultSample) { 164 | function helper(node) { 165 | if (node.folder) { 166 | if (defaultSample !== null && node.folder === defaultSample) { 167 | return node; 168 | } 169 | else if (defaultSample === null) { 170 | return node; 171 | } 172 | } 173 | 174 | if (node.sub_chapters && node.sub_chapters.length > 0) { 175 | for (let n of node.sub_chapters) { 176 | const r = helper(n); 177 | if (r !== null) { 178 | return r; 179 | } 180 | } 181 | } 182 | return null; 183 | } 184 | 185 | for (let n of content().content) { 186 | const r = helper(n); 187 | if (r !== null) { 188 | return r; 189 | } 190 | } 191 | return null; 192 | } 193 | 194 | function getDetailsByFolder(folderName) { 195 | function helper(node) { 196 | if (node.folder === folderName) { 197 | return node; 198 | } else { 199 | if (node.sub_chapters && node.sub_chapters.length > 0) { 200 | for (let n of node.sub_chapters) { 201 | const r = helper(n); 202 | if (r !== null) { 203 | return r; 204 | } 205 | } 206 | } 207 | } 208 | return null; 209 | } 210 | 211 | for (let n of content().content) { 212 | const r = helper(n); 213 | if (r !== null) { 214 | return r; 215 | } 216 | } 217 | return null; 218 | } 219 | 220 | async function fetchContent() { 221 | let manifest = await fetch("manifest.json"); 222 | let jsonContent = await manifest.json(); 223 | return jsonContent; 224 | } 225 | 226 | function onLoadSample(e) { 227 | console.log(e); 228 | setIsMenuOpen(false); 229 | window.location.href = prefixSubPath(); 230 | window.location.hash = "#" + e.folder; 231 | window.location.reload(); 232 | } 233 | 234 | async function fetchFileByPath(filePath) { 235 | if (loadedFiles.has(filePath)) { 236 | const model = loadedFiles.get(filePath); 237 | return model; 238 | } else { 239 | let file = await fetch(filePath); 240 | let fileContent = await file.text(); 241 | const model = monaco.editor.createModel( 242 | fileContent, 243 | undefined, // language 244 | monaco.Uri.file(filePath) // uri 245 | ); 246 | loadedFiles.set(filePath, model); 247 | return model; 248 | } 249 | } 250 | 251 | async function onLoadFile(e, f) { 252 | for (let re of fileRefs) { 253 | if (re.getAttribute("filename") === f.filename) { 254 | if (f.is_readonly == true) { 255 | re.classList.add("readonly"); 256 | } else { 257 | re.classList.add("active"); 258 | } 259 | } else { 260 | re.classList.remove("active"); 261 | re.classList.remove("readonly"); 262 | } 263 | } 264 | 265 | const filePath = currentFolder.folder + "/" + f.filename; 266 | const model = await fetchFileByPath(filePath); 267 | editor.setModel(model); 268 | if (f.is_readonly == true) { 269 | editor.updateOptions({ readOnly: true }); 270 | } else { 271 | editor.updateOptions({ readOnly: false }); 272 | } 273 | 274 | document.title = currentFolder.title + " - " + f.filename; 275 | 276 | if (content().readme_folder) { 277 | console.log("show readme", content().readme_folder) 278 | displayReadme(); 279 | } 280 | else { 281 | console.log("no readme folder") 282 | } 283 | 284 | 285 | if (highlightRanges.length > 0) { 286 | console.log("decorations", highlightRanges.map((range) => { 287 | return { 288 | range: range, 289 | options: { 290 | isWholeLine: true, 291 | inlineClassName: "highlight-line" 292 | } 293 | }; 294 | })); 295 | 296 | editor.createDecorationsCollection(highlightRanges.map((range) => { 297 | return { 298 | range: range, 299 | options: { 300 | isWholeLine: true, 301 | linesDecorationsClassName: "highlight-lines" 302 | } 303 | }; 304 | })); 305 | 306 | editor.revealLineInCenter(highlightRanges[0].startLineNumber); 307 | } 308 | } 309 | 310 | function updateIFrameSize() { 311 | const outputRect = document 312 | .getElementById("output") 313 | .getBoundingClientRect(); 314 | 315 | outputWindow.style.width = outputRect.width + "px"; 316 | outputWindow.style.height = outputRect.height + "px"; 317 | } 318 | 319 | onMount(async () => { 320 | if (isMobile()) { 321 | return; 322 | } 323 | 324 | setContent(await fetchContent()); 325 | document.title = content().title; 326 | 327 | let folder = window.location.hash; 328 | if (folder.length > 0) { 329 | folder = folder.substring(1); 330 | } else { 331 | folder = null; 332 | } 333 | 334 | const params = new URL(document.location.toString()).searchParams; 335 | if (params) { 336 | const highlight = params.get("highlight"); 337 | if (highlight) { 338 | const sections = highlight.split(','); 339 | for(const s of sections){ 340 | const startAndEndLine = s.split(':'); 341 | const startLine = parseInt(startAndEndLine[0]); 342 | const endLine = parseInt(startAndEndLine[1]); 343 | 344 | /*const range = { 345 | startLineNumber: startLine + 1, 346 | startColumn: 1, 347 | endLineNumber: endLine + 1, 348 | endColumn: 9 349 | }*/ 350 | 351 | const range = new monaco.Range(startLine + 1, 1, endLine + 1, 9); 352 | 353 | highlightRanges.push(range); 354 | } 355 | } 356 | } 357 | 358 | console.log('highlightRanges', highlightRanges); 359 | 360 | currentFolder = 361 | (folder && getDetailsByFolder(folder)) || 362 | getFirstFolderDetails(content().default_sample ? content().default_sample : null); 363 | 364 | if (currentFolder !== null) { 365 | setFiles([...currentFolder.files]); 366 | } 367 | 368 | let mouseX = 0; 369 | let panelLeft = 0; 370 | let panelRight = 0; 371 | let panelMain = 0; 372 | 373 | let movableOnMouseMove = function (e) { 374 | e.stopPropagation(); 375 | e.preventDefault(); 376 | const dx = e.screenX - mouseX; 377 | panelLeft = container.getBoundingClientRect().width; 378 | panelRight = output.getBoundingClientRect().width; 379 | panelMain = main.getBoundingClientRect().width; 380 | const left = ((panelLeft + dx) * 100) / panelMain; 381 | 382 | container.style.width = `${left}%`; 383 | const right = ((panelRight - dx) * 100) / panelMain; 384 | output.style.width = `${right}%`; 385 | mouseX = e.screenX; 386 | }; 387 | 388 | let movableOnMouseUp = function (event) { 389 | event.stopPropagation(); 390 | event.preventDefault(); 391 | document.removeEventListener("mousemove", movableOnMouseMove); 392 | document.removeEventListener("mouseup", movableOnMouseUp); 393 | updateIFrameSize(); 394 | outputWindow.style.pointerEvents = "auto"; 395 | container.style.pointerEvents = "auto"; 396 | }; 397 | 398 | let timer = null; 399 | window.onresize = () => { 400 | if (timer) { 401 | clearTimeout(timer); 402 | } 403 | timer = setTimeout(() => { 404 | updateIFrameSize(); 405 | }, 500); 406 | }; 407 | 408 | movableBar.onmousedown = function (e) { 409 | mouseX = e.screenX; 410 | document.addEventListener("mousemove", movableOnMouseMove); 411 | document.addEventListener("mouseup", movableOnMouseUp); 412 | // cancel iframe mouse event https://www.gyrocode.com/articles/how-to-detect-mousemove-event-over-iframe-element/ 413 | outputWindow.style.pointerEvents = "none"; 414 | container.style.pointerEvents = "none"; 415 | 416 | e.stopPropagation(); 417 | e.preventDefault(); 418 | }; 419 | 420 | updateIFrameSize(); 421 | 422 | window.MonacoEnvironment = { 423 | getWorkerUrl: function (workerId, label) { 424 | console.log("moduleid", workerId, label) 425 | if (workerId === 'workerMain.js') { 426 | return 'assets/vs/base/worker/' + workerId; 427 | } 428 | } 429 | } 430 | 431 | monaco.editor.defineTheme("codestage", theme); 432 | monaco.editor.setTheme("codestage"); 433 | 434 | editor = monaco.editor.create(container, { 435 | value: "", 436 | language: undefined, 437 | automaticLayout: true, 438 | suggest: { 439 | showSnippets: false, 440 | showWords: false, 441 | showKeywords: false, 442 | showVariables: false, // disables `undefined`, but also disables user-defined variables suggestions. 443 | showModules: false, // disables `globalThis`, but also disables user-defined modules suggestions. 444 | }, 445 | overviewRulerLanes: 0, 446 | hideCursorInOverviewRuler: true, 447 | overviewRulerBorder: false, 448 | guides: { 449 | indentation: false, 450 | }, 451 | codeLens: false, 452 | ordBasedSuggestions: false, 453 | suggestOnTriggerCharacters: false, 454 | wordBasedSuggestions: false, 455 | snippetSuggestions: "none", 456 | hover: { enabled: false }, 457 | }); 458 | 459 | onLoadFile(null, files()[0]); 460 | 461 | }); 462 | 463 | return ( 464 | 467 | 468 |

Only desktop browsers are supported.

469 | 470 | }> 471 |
472 |
473 |

{content().title}

474 |
475 |
    476 | 477 | {(item, i) => 478 | } 483 | 484 |
485 |
486 |
487 |
488 | 495 | 496 | {(f, i) => 497 | 511 | } 512 | 513 |
514 | 518 | 519 | 526 | 527 |
528 |
529 |
530 |
531 |
532 | 533 |
534 |
535 |
536 |
) 537 | } 538 | 539 | export default App -------------------------------------------------------------------------------- /frontend/src/App.module.css: -------------------------------------------------------------------------------- 1 | .Menu { 2 | width: 320px; 3 | height: 100vh; 4 | position: fixed; 5 | top: 0; 6 | left: -320px; 7 | background-color: white; 8 | z-index: 100; 9 | box-shadow: 0 3px 10px rgb(0 0 0 / 0.2); 10 | display: flex; 11 | flex-direction: column; 12 | background-color: #0e0b33; 13 | } 14 | 15 | .MenuTitle { 16 | margin-top: 40px; 17 | color: #ffffff; 18 | text-align: center; 19 | margin-bottom: 8px; 20 | } 21 | 22 | .MenuContent { 23 | flex-grow: 1; 24 | overflow-y: scroll; 25 | overflow-x: hidden; 26 | padding: 16px; 27 | margin-left: 10px; 28 | } 29 | 30 | .ToolBar { 31 | flex-grow: 0; 32 | flex-shrink: 1; 33 | display: flex; 34 | flex-direction: row; 35 | height: 40px; 36 | padding-right: 10px; 37 | } 38 | 39 | .TabButton { 40 | border: 0; 41 | background: none; 42 | box-shadow: none; 43 | border-radius: 0px; 44 | color: #9492b1; 45 | cursor: pointer; 46 | } 47 | 48 | .TabButton:hover { 49 | color: #ffffff; 50 | } 51 | 52 | .TabButtonText { 53 | margin-left: 8px; 54 | font-family: "Source Code Pro", monospace; 55 | } 56 | 57 | /* 58 | .TabButton.active { 59 | border-bottom-width: 4px; 60 | border-style: solid; 61 | border-color: #4631c5; 62 | cursor: default; 63 | color: #ffffff; 64 | } 65 | 66 | .TabButton.readonly { 67 | border-bottom-width: 4px; 68 | border-style: solid; 69 | border-color: #9b304c; 70 | cursor: default; 71 | color: #ffffff; 72 | }*/ 73 | 74 | .TabButton:focus { 75 | outline: none; 76 | } 77 | 78 | .Main { 79 | display: flex; 80 | width: 100%; 81 | flex-direction: row; 82 | flex-grow: 1; 83 | overflow: hidden; 84 | } 85 | 86 | .Editor { 87 | display: block; 88 | width: 50%; 89 | border-radius: 4px; 90 | border-width: 1px; 91 | border-style: solid; 92 | border-color: #2b284b; 93 | background-color: #1a183d; 94 | position: relative; 95 | } 96 | 97 | .Output { 98 | overflow: hidden; 99 | width: 50%; 100 | } 101 | 102 | .Sandbox { 103 | position: absolute; 104 | border-radius: 4px; 105 | border-width: 1px; 106 | border-style: solid; 107 | border-color: #2b284b; 108 | background-color: #1a183d; 109 | } -------------------------------------------------------------------------------- /frontend/src/MenuItem.jsx: -------------------------------------------------------------------------------- 1 | import { createSignal } from 'solid-js' 2 | import styles from './MenuItem.module.css' 3 | 4 | function MenuItem(props) { 5 | function onSelect(e) { 6 | props.load(props.item); 7 | } 8 | return ( 9 |
  • 10 |

    11 | {props.item.title} 12 |

    13 | 0}> 14 |
      15 | 16 | {(item, i) => 17 | 22 | } 23 | 24 |
    25 |
    26 |
  • 27 | ) 28 | } 29 | 30 | export default MenuItem 31 | -------------------------------------------------------------------------------- /frontend/src/MenuItem.module.css: -------------------------------------------------------------------------------- 1 | .MenuItem { 2 | color: #9492b1; 3 | border: 0; 4 | background: none; 5 | box-shadow: none; 6 | border-radius: 0px; 7 | cursor: pointer; 8 | margin-left: 2px; 9 | } 10 | 11 | .ClickableItem { 12 | padding: 2px; 13 | } -------------------------------------------------------------------------------- /frontend/src/assets/CodeStage.json: -------------------------------------------------------------------------------- 1 | { 2 | "base": "vs-dark", 3 | "inherit": true, 4 | "rules": [ 5 | { 6 | "background": "282a36", 7 | "token": "" 8 | }, 9 | { 10 | "foreground": "6272a4", 11 | "token": "comment" 12 | }, 13 | { 14 | "foreground": "f1fa8c", 15 | "token": "string" 16 | }, 17 | { 18 | "foreground": "bd93f9", 19 | "token": "constant.numeric" 20 | }, 21 | { 22 | "foreground": "bd93f9", 23 | "token": "constant.language" 24 | }, 25 | { 26 | "foreground": "bd93f9", 27 | "token": "constant.character" 28 | }, 29 | { 30 | "foreground": "bd93f9", 31 | "token": "constant.other" 32 | }, 33 | { 34 | "foreground": "ffb86c", 35 | "token": "variable.other.readwrite.instance" 36 | }, 37 | { 38 | "foreground": "ff79c6", 39 | "token": "constant.character.escaped" 40 | }, 41 | { 42 | "foreground": "ff79c6", 43 | "token": "constant.character.escape" 44 | }, 45 | { 46 | "foreground": "ff79c6", 47 | "token": "string source" 48 | }, 49 | { 50 | "foreground": "ff79c6", 51 | "token": "string source.ruby" 52 | }, 53 | { 54 | "foreground": "ff79c6", 55 | "token": "keyword" 56 | }, 57 | { 58 | "foreground": "ff79c6", 59 | "token": "storage" 60 | }, 61 | { 62 | "foreground": "8be9fd", 63 | "fontStyle": "italic", 64 | "token": "storage.type" 65 | }, 66 | { 67 | "foreground": "50fa7b", 68 | "fontStyle": "underline", 69 | "token": "entity.name.class" 70 | }, 71 | { 72 | "foreground": "50fa7b", 73 | "fontStyle": "italic underline", 74 | "token": "entity.other.inherited-class" 75 | }, 76 | { 77 | "foreground": "50fa7b", 78 | "token": "entity.name.function" 79 | }, 80 | { 81 | "foreground": "ffb86c", 82 | "fontStyle": "italic", 83 | "token": "variable.parameter" 84 | }, 85 | { 86 | "foreground": "ff79c6", 87 | "token": "entity.name.tag" 88 | }, 89 | { 90 | "foreground": "50fa7b", 91 | "token": "entity.other.attribute-name" 92 | }, 93 | { 94 | "foreground": "8be9fd", 95 | "token": "support.function" 96 | }, 97 | { 98 | "foreground": "6be5fd", 99 | "token": "support.constant" 100 | }, 101 | { 102 | "foreground": "66d9ef", 103 | "fontStyle": " italic", 104 | "token": "support.type" 105 | }, 106 | { 107 | "foreground": "66d9ef", 108 | "fontStyle": " italic", 109 | "token": "support.class" 110 | }, 111 | { 112 | "foreground": "f8f8f0", 113 | "background": "ff79c6", 114 | "token": "invalid" 115 | }, 116 | { 117 | "foreground": "f8f8f0", 118 | "background": "bd93f9", 119 | "token": "invalid.deprecated" 120 | }, 121 | { 122 | "foreground": "cfcfc2", 123 | "token": "meta.structure.dictionary.json string.quoted.double.json" 124 | }, 125 | { 126 | "foreground": "6272a4", 127 | "token": "meta.diff" 128 | }, 129 | { 130 | "foreground": "6272a4", 131 | "token": "meta.diff.header" 132 | }, 133 | { 134 | "foreground": "ff79c6", 135 | "token": "markup.deleted" 136 | }, 137 | { 138 | "foreground": "50fa7b", 139 | "token": "markup.inserted" 140 | }, 141 | { 142 | "foreground": "e6db74", 143 | "token": "markup.changed" 144 | }, 145 | { 146 | "foreground": "bd93f9", 147 | "token": "constant.numeric.line-number.find-in-files - match" 148 | }, 149 | { 150 | "foreground": "e6db74", 151 | "token": "entity.name.filename" 152 | }, 153 | { 154 | "foreground": "f83333", 155 | "token": "message.error" 156 | }, 157 | { 158 | "foreground": "eeeeee", 159 | "token": "punctuation.definition.string.begin.json - meta.structure.dictionary.value.json" 160 | }, 161 | { 162 | "foreground": "eeeeee", 163 | "token": "punctuation.definition.string.end.json - meta.structure.dictionary.value.json" 164 | }, 165 | { 166 | "foreground": "8be9fd", 167 | "token": "meta.structure.dictionary.json string.quoted.double.json" 168 | }, 169 | { 170 | "foreground": "f1fa8c", 171 | "token": "meta.structure.dictionary.value.json string.quoted.double.json" 172 | }, 173 | { 174 | "foreground": "50fa7b", 175 | "token": "meta meta meta meta meta meta meta.structure.dictionary.value string" 176 | }, 177 | { 178 | "foreground": "ffb86c", 179 | "token": "meta meta meta meta meta meta.structure.dictionary.value string" 180 | }, 181 | { 182 | "foreground": "ff79c6", 183 | "token": "meta meta meta meta meta.structure.dictionary.value string" 184 | }, 185 | { 186 | "foreground": "bd93f9", 187 | "token": "meta meta meta meta.structure.dictionary.value string" 188 | }, 189 | { 190 | "foreground": "50fa7b", 191 | "token": "meta meta meta.structure.dictionary.value string" 192 | }, 193 | { 194 | "foreground": "ffb86c", 195 | "token": "meta meta.structure.dictionary.value string" 196 | } 197 | ], 198 | "colors": { 199 | "editor.foreground": "#f8f8f2", 200 | "editor.background": "#1a183d", 201 | "editor.selectionBackground": "#44475a", 202 | "editor.lineHighlightBackground": "#28264e", 203 | "editorCursor.foreground": "#f8f8f0", 204 | "editorWhitespace.foreground": "#3B3A32", 205 | "editorIndentGuide.activeBackground": "#9D550FB0", 206 | "editor.selectionHighlightBorder": "#222218", 207 | "minimap.background": "#181638", 208 | "minimap.selectionHighlight": "#44475a", 209 | "scrollbarSlider.activeBackground": "#28264e", 210 | "scrollbarSlider.hoverBackground": "#28264e" 211 | } 212 | } -------------------------------------------------------------------------------- /frontend/src/assets/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/frontend/src/assets/logo2.png -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | #root { 2 | font-family: "Roboto", sans-serif; 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; 5 | display: flex; 6 | flex-direction: row; 7 | overflow: hidden; 8 | background-color: #0e0b33; 9 | width: 100%; 10 | position: relative; 11 | } 12 | 13 | html { 14 | overflow: hidden; 15 | position: relative; 16 | } 17 | 18 | a { 19 | font-weight: 500; 20 | color: #646cff; 21 | text-decoration: inherit; 22 | } 23 | 24 | a:hover { 25 | color: #535bf2; 26 | } 27 | 28 | body { 29 | margin: 0; 30 | display: flex; 31 | min-width: 320px; 32 | min-height: 100vh; 33 | } 34 | 35 | h1 { 36 | font-size: 3.2em; 37 | line-height: 1.1; 38 | } 39 | 40 | button { 41 | border-radius: 8px; 42 | border: 1px solid transparent; 43 | padding: 0.6em 1.2em; 44 | font-size: 1em; 45 | font-weight: 500; 46 | font-family: inherit; 47 | background-color: #1a1a1a; 48 | cursor: pointer; 49 | transition: border-color 0.25s; 50 | } 51 | 52 | button:hover { 53 | border-color: #646cff; 54 | } 55 | 56 | button:focus, 57 | button:focus-visible { 58 | outline: 4px auto -webkit-focus-ring-color; 59 | } 60 | 61 | .slide { 62 | left: 0px !important; 63 | transition: 0.3s; 64 | } 65 | 66 | /* width */ 67 | ::-webkit-scrollbar { 68 | width: 8px; 69 | } 70 | 71 | /* Track */ 72 | ::-webkit-scrollbar-track { 73 | background-color: #1a183d; 74 | border-radius: 4px; 75 | border-radius: 2px; 76 | border-style: solid; 77 | border-color: #1a183d; 78 | } 79 | 80 | /* Handle */ 81 | ::-webkit-scrollbar-thumb { 82 | background: #222045; 83 | border-radius: 4px; 84 | } 85 | 86 | .clickable { 87 | cursor: pointer; 88 | margin-block-start: 4px; 89 | margin-block-end: 4px; 90 | } 91 | 92 | .menutitle { 93 | margin:0; 94 | font-size: 16px; 95 | margin-top: 3px; 96 | } 97 | 98 | ul { 99 | margin-left: 2px; 100 | padding: 0; 101 | } 102 | 103 | li { 104 | margin-left: 2px; 105 | } 106 | 107 | .clickable:hover { 108 | background-color: #222045; 109 | color: #ffffff; 110 | border-radius: 4px; 111 | border-width: 1px; 112 | border-style: solid; 113 | border-color: #222045; 114 | } 115 | 116 | #adjust-bar { 117 | border-top-width: 0px; 118 | border-left-width: 1px; 119 | border-right-width: 1px; 120 | border-bottom-width: 0px; 121 | border-style: solid; 122 | border-color: black; 123 | width: 3px; 124 | cursor: ew-resize; 125 | } 126 | 127 | .mobile { 128 | display: flex; 129 | flex-direction: column; 130 | width: 100vw; 131 | background-color: black; 132 | color: white; 133 | font-weight: 600; 134 | font-size: 24px; 135 | text-align: center; 136 | } 137 | 138 | .highlight-lines { 139 | border-left-color: #c1ad8b; 140 | border-left-style: solid; 141 | margin-left:6px; 142 | border-left-width:4px; 143 | } -------------------------------------------------------------------------------- /frontend/src/index.jsx: -------------------------------------------------------------------------------- 1 | /* @refresh reload */ 2 | import { render } from 'solid-js/web' 3 | 4 | import './index.css' 5 | import App from './App' 6 | 7 | const root = document.getElementById('root') 8 | 9 | render(() => , root) 10 | -------------------------------------------------------------------------------- /frontend/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import solid from 'vite-plugin-solid' 3 | 4 | export default defineConfig({ 5 | plugins: [solid()], 6 | base: '$$_codestage_prefix_$$', 7 | build: { 8 | outDir: '../cli/dist', 9 | minify: false, 10 | rollupOptions: { 11 | output: { 12 | entryFileNames: `assets/[name].js`, 13 | chunkFileNames: `assets/[name].js`, 14 | assetFileNames: `assets/[name].[ext]` 15 | }, 16 | input: { 17 | app: 'code.html', // default 18 | }, 19 | external: [ 20 | 'node_modules/monaco-editor/min' 21 | ], 22 | } 23 | }, 24 | optimizeDeps: { 25 | exclude: ['node_modules/monaco-editor'], 26 | }, 27 | }) 28 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/logo.png -------------------------------------------------------------------------------- /logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/logo.psd -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Sorry, your browser does not support inline SVG. 12 | -------------------------------------------------------------------------------- /logo2.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/logo2.psd -------------------------------------------------------------------------------- /logo3.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/logo3.psd -------------------------------------------------------------------------------- /meta.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shi-yan/codestage/c2b085c3e0e33b377bbf49106207931741a1fafe/meta.psd --------------------------------------------------------------------------------