├── .gitattributes ├── .gitignore ├── AUTHORS ├── BSD-LICENSE ├── LICENSE ├── Makefile ├── README ├── VERSION ├── demo ├── assets │ ├── shared.css │ └── test_image.png ├── debugger.html ├── external │ ├── codemirror.css │ ├── codemirror.js │ ├── jsDump.js │ └── mode │ │ └── glsl.js ├── lib │ ├── DebugView.js │ ├── EditorView.js │ ├── ImageView.js │ ├── RenderView.js │ └── VariableView.js ├── parser.html └── translate.html ├── docs ├── Runtime Notes.md └── samples │ ├── s1.frag │ └── s1.js ├── lib ├── compiler │ ├── ast.js │ ├── codegen.js │ ├── parser.js │ ├── pretty.js │ ├── program.js │ ├── shader.js │ ├── typecheck.js │ └── visitor.js ├── error.js ├── events.js ├── glsl.js ├── runtime.js └── runtime │ ├── access.js │ ├── builtins.js │ ├── environment.js │ ├── internal.js │ ├── matrix.js │ ├── ops.js │ ├── texture.js │ └── vector.js ├── src └── glsl.pegjs └── test └── runtime └── test.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.txt text 2 | *.js text 3 | *.html text 4 | *.md text 5 | *.json text 6 | *.yml text 7 | *.css text 8 | *.svg text 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .htaccess 2 | *.Makefile 3 | *.gypcmd 4 | *.mk 5 | *.ncb 6 | *.ninja 7 | *.orig 8 | *.opensdf 9 | *.props 10 | *.pyc 11 | *.rules 12 | *.scons 13 | *.sdf 14 | *.sln 15 | *.suo 16 | *.targets 17 | *.user 18 | *.vpj 19 | *.vpw 20 | *.vpwhistu 21 | *.vtg 22 | *.xcodeproj 23 | *~ 24 | ~* 25 | .*.sw? 26 | .DS_Store 27 | .cproject 28 | .gdb_history 29 | .gdbinit 30 | .metadata 31 | .project 32 | .settings/ # settings directory for eclipse 33 | tags 34 | Thumbs.db 35 | 36 | node_modules/ 37 | browser/ 38 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Brian Burg 2 | Xiao Sophia Wang 3 | -------------------------------------------------------------------------------- /BSD-LICENSE: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without 2 | modification, are permitted provided that the following conditions are met: 3 | 4 | 1. Redistributions of source code must retain the above copyright notice, this 5 | list of conditions and the following disclaimer. 6 | 2. Redistributions in binary form must reproduce the above copyright notice, 7 | this list of conditions and the following disclaimer in the documentation 8 | and/or other materials provided with the distribution. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 11 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 12 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 13 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 14 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 15 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 16 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 17 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 18 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 19 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This file summarizes the licenses used within this repository. 2 | Original portions of glsl-simulator are licensed under the Simplified BSD license. 3 | 4 | ---- 5 | 6 | glsl-mode.js is originally from glsl-sandbox 7 | and is distributed under the MIT license. 8 | 9 | CodeMirror is copyright (c) by Marijn Haverbeke and others and 10 | distributed under an MIT license: http://codemirror.net/LICENSE. 11 | 12 | jsDump is copyright (c) by Ariel Flesler and is distributed under the BSD license. 13 | 14 | The glsl.pegjs grammar is originally from glslunit 15 | and is distributed under the Apache 2.0 license. 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Build script borrowed from PEG.js 2 | 3 | # ===== Variables ===== 4 | 5 | GLSL_SIMULATOR_VERSION = `cat $(VERSION_FILE)` 6 | 7 | # ===== Modules ===== 8 | 9 | # Order matters -- dependencies must be listed before modules dependent on them. 10 | MODULES = error \ 11 | events \ 12 | runtime/vector \ 13 | runtime/matrix \ 14 | runtime/access \ 15 | runtime/internal \ 16 | runtime/builtins \ 17 | runtime/ops \ 18 | runtime/environment \ 19 | runtime \ 20 | compiler/ast \ 21 | compiler/visitor \ 22 | compiler/typecheck \ 23 | compiler/pretty \ 24 | compiler/codegen \ 25 | compiler/parser \ 26 | compiler/shader \ 27 | compiler/program \ 28 | glsl \ 29 | 30 | 31 | # ===== Directories ===== 32 | 33 | SRC_DIR = src 34 | LIB_DIR = lib 35 | BIN_DIR = bin 36 | BROWSER_DIR = browser 37 | NODE_MODULES_DIR = node_modules 38 | NODE_MODULES_BIN_DIR = $(NODE_MODULES_DIR)/.bin 39 | 40 | # ===== Files ===== 41 | 42 | PARSER_SRC_FILE = $(SRC_DIR)/glsl.pegjs 43 | PARSER_OUT_FILE = $(LIB_DIR)/compiler/parser.js 44 | 45 | BROWSER_FILE_DEV = $(BROWSER_DIR)/glsl-simulator-$(GLSL_SIMULATOR_VERSION).js 46 | BROWSER_FILE_MIN = $(BROWSER_DIR)/glsl-simulator-$(GLSL_SIMULATOR_VERSION).min.js 47 | 48 | VERSION_FILE = VERSION 49 | 50 | # ===== Executables ===== 51 | # Use '$ npm install pegjs uglify-js' from repository root to install dependencies locally. 52 | 53 | UGLIFYJS = $(NODE_MODULES_BIN_DIR)/uglifyjs 54 | PEGJS = $(NODE_MODULES_BIN_DIR)/pegjs 55 | 56 | # ===== Targets ===== 57 | 58 | # Default target 59 | all: parser browser 60 | 61 | # Generate the grammar parser 62 | parser: 63 | $(PEGJS) $(PARSER_SRC_FILE) $(PARSER_OUT_FILE) 64 | 65 | # Build the browser version of the library 66 | browser: 67 | mkdir -p $(BROWSER_DIR) 68 | 69 | rm -f $(BROWSER_FILE_DEV) 70 | rm -f $(BROWSER_FILE_MIN) 71 | 72 | # The following code is inspired by CoffeeScript's Cakefile. 73 | 74 | echo '/*' >> $(BROWSER_FILE_DEV) 75 | echo " glsl-simulator.js $(GLSL_SIMULATOR_VERSION)" >> $(BROWSER_FILE_DEV) 76 | echo ' https://www.github.com/burg/glsl-simulator/' >> $(BROWSER_FILE_DEV) 77 | echo '' >> $(BROWSER_FILE_DEV) 78 | echo ' Copyright (c) 2014, Brian Burg ' >> $(BROWSER_FILE_DEV) 79 | echo ' Copyright (c) 2014, Xiao Sophia Wang.' >> $(BROWSER_FILE_DEV) 80 | echo ' All rights reserved.' >> $(BROWSER_FILE_DEV) 81 | echo '' >> $(BROWSER_FILE_DEV) 82 | echo ' Distributed under the terms of the Simplified BSD License:' >> $(BROWSER_FILE_DEV) 83 | echo '' >> $(BROWSER_FILE_DEV) 84 | cat ./BSD-LICENSE >> $(BROWSER_FILE_DEV) 85 | echo '*/' >> $(BROWSER_FILE_DEV) 86 | echo '' >> $(BROWSER_FILE_DEV) 87 | echo 'var GLSL = (function(undefined) {' >> $(BROWSER_FILE_DEV) 88 | echo ' var modules = {' >> $(BROWSER_FILE_DEV) 89 | echo ' define: function(name, factory) {' >> $(BROWSER_FILE_DEV) 90 | echo ' var dir = name.replace(/(^|\/)[^/]+$$/, "$$1"),' >> $(BROWSER_FILE_DEV) 91 | echo ' module = { exports: {} };' >> $(BROWSER_FILE_DEV) 92 | echo '' >> $(BROWSER_FILE_DEV) 93 | echo ' function require(path) {' >> $(BROWSER_FILE_DEV) 94 | echo ' var name = dir + path,' >> $(BROWSER_FILE_DEV) 95 | echo ' regexp = /[^\/]+\/\.\.\/|\.\//;' >> $(BROWSER_FILE_DEV) 96 | echo '' >> $(BROWSER_FILE_DEV) 97 | echo " /* Can't use /.../g because we can move backwards in the string. */" >> $(BROWSER_FILE_DEV) 98 | echo ' while (regexp.test(name)) {' >> $(BROWSER_FILE_DEV) 99 | echo ' name = name.replace(regexp, "");' >> $(BROWSER_FILE_DEV) 100 | echo ' }' >> $(BROWSER_FILE_DEV) 101 | echo '' >> $(BROWSER_FILE_DEV) 102 | echo ' return modules[name];' >> $(BROWSER_FILE_DEV) 103 | echo ' }' >> $(BROWSER_FILE_DEV) 104 | echo '' >> $(BROWSER_FILE_DEV) 105 | echo ' factory(module, require);' >> $(BROWSER_FILE_DEV) 106 | echo ' this[name] = module.exports;' >> $(BROWSER_FILE_DEV) 107 | echo ' }' >> $(BROWSER_FILE_DEV) 108 | echo ' };' >> $(BROWSER_FILE_DEV) 109 | 110 | for module in $(MODULES); do \ 111 | echo " modules.define(\"$$module\", function(module, require) {" >> $(BROWSER_FILE_DEV); \ 112 | sed -e 's/^/ /' lib/$$module.js >> $(BROWSER_FILE_DEV); \ 113 | echo ' });' >> $(BROWSER_FILE_DEV); \ 114 | echo '' >> $(BROWSER_FILE_DEV); \ 115 | done 116 | 117 | echo ' return modules["glsl"]' >> $(BROWSER_FILE_DEV) 118 | echo '})();' >> $(BROWSER_FILE_DEV) 119 | 120 | $(UGLIFYJS) \ 121 | --mangle \ 122 | --compress warnings=false \ 123 | --comments /Simplified\ BSD\ License/ \ 124 | -o $(BROWSER_FILE_MIN) \ 125 | $(BROWSER_FILE_DEV) 126 | 127 | # Remove browser version of the library (created by "browser") 128 | browserclean: 129 | rm -rf $(BROWSER_DIR) 130 | 131 | .PHONY: all parser browser browserclean 132 | .SILENT: all parser browser browserclean 133 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | # Target 2 | 3 | `glsl-simulator` is a JavaScript compiler and runtime for GLSL shaders. It is designed to provide (browser) developer tools with the runtime information necessary to implement features such as stepping through shader control flow, introspecting live state and logging. As such, it is currently designed to run in a full browser environment. However, the development environment makes use of NodeJS. 4 | 5 | # Dependencies 6 | 7 | `glsl-simulator` requires NodeJS, npm, and the npm packages `pegjs` and `uglify-js`. To satisfy the latter dependencies, run 8 | 9 | $ npm install pegjs uglify-js 10 | 11 | To combine the script resources: 12 | 13 | $ make 14 | 15 | # Running 16 | 17 | Currently, the project is exercised through several demo pages. See the `demo/` directory. 18 | 19 | To use `glsl-simulator` from another (browser) code base, run `make` and use the resulting library in the `browser/` build directory. 20 | 21 | # Hacking 22 | 23 | `glsl-simulator` uses a PEG.js grammar to parse the OpenGL ES 2.0 Shader Language (GLSL). 24 | Usually the generated grammar is also checked into the repository. To change the grammar 25 | and regenerate the JavaScript parser, you need to run `make`. 26 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.1.0 2 | -------------------------------------------------------------------------------- /demo/assets/shared.css: -------------------------------------------------------------------------------- 1 | #demo-container { 2 | display: flex; 3 | display: -webkit-flex; 4 | flex-direction: row; 5 | -webkit-flex-direction: row; 6 | flex-wrap: nowrap; 7 | -webkit-flex-wrap: nowrap; 8 | justify: space-around; 9 | -webkit-justify: space-around; 10 | 11 | position: absolute; 12 | top: 0; right: 0; bottom: 0; left: 0; 13 | overflow: none; 14 | } 15 | 16 | #demo-container > div { 17 | width: 50%; 18 | display: flex; 19 | display: -webkit-flex; 20 | flex-direction: column; 21 | -webkit-flex-direction: column; 22 | flex-wrap: nowrap; 23 | -webkit-flex-wrap: nowrap; 24 | } 25 | 26 | #demo-header { 27 | height: 30px; 28 | font: 14px Helvetica, Arial, sans-serif; 29 | font-weight: bold; 30 | line-height: 28px; 31 | text-transform: uppercase; 32 | padding-left: 20px; 33 | } 34 | 35 | .editor-options { 36 | position: absolute; 37 | right: 5px; 38 | top: 5px; 39 | z-index: 1000; 40 | background-color: rgba(200, 200, 200, 0.6); 41 | border: #ccc solid 1px; 42 | } 43 | 44 | .editor-options ul { 45 | list-style-type: none; 46 | font-size: 12px; 47 | font-family: Myriad Pro, Helvetica, sans-serif; 48 | margin: 10px; 49 | padding: 0; 50 | } 51 | 52 | .editor-options li { 53 | margin: 4px 0; 54 | } 55 | 56 | .editor-options input[type=checkbox] { 57 | float: left; 58 | margin-right: 5px; 59 | } 60 | 61 | .shader-editor { 62 | border: solid 3px #c99; 63 | -webkit-flex-grow: 1; 64 | flex-grow: 1; 65 | position: relative; 66 | } 67 | 68 | .shader-editor .stub { 69 | height: 100%; 70 | } 71 | 72 | .CodeMirror-scroll { 73 | overflow: auto; 74 | height: 100%; 75 | /* This is needed to prevent an IE[67] bug where the scrolled content 76 | is visible outside of the scrolling box. */ 77 | position: relative; 78 | outline: none; 79 | } 80 | 81 | .results-viewer { 82 | overflow: scroll; 83 | border: solid 3px #9c9; 84 | position: relative; 85 | flex-grow: 1; 86 | -webkit-flex-grow: 1; 87 | } 88 | 89 | .results-viewer .output-options { 90 | position: fixed; 91 | right: 20px; 92 | display: inline; 93 | z-index: 1000; 94 | } 95 | 96 | .results-viewer .output { 97 | white-space: pre; 98 | font-family: Menlo, monospace; 99 | font-size: 12px; 100 | 101 | position: absolute; 102 | top: 0; bottom: 0; 103 | left: 0; right: 0; 104 | } 105 | 106 | .results-viewer.error-message .output { 107 | top: 50%; 108 | padding: 0 30px; 109 | 110 | color: #c33; 111 | text-align: center; 112 | font-size: 14px; 113 | font-weight: bold; 114 | } 115 | 116 | .results-viewer.error-message .output-options select { 117 | opacity: 0.5; 118 | pointer-events: none; 119 | } 120 | 121 | .image-container { 122 | border: solid 3px #c99; 123 | height: 160px; 124 | overflow-y: scroll; 125 | } 126 | 127 | .variable-list { 128 | border: solid 3px #99c; 129 | height: 200px; 130 | overflow-y: scroll; 131 | } 132 | 133 | .variable-row { 134 | display: -webkit-flex; 135 | display: flex; 136 | -webkit-align-items: center; 137 | align-items: center; 138 | 139 | border-bottom: 1px solid #e0e0e0; 140 | } 141 | 142 | .variable-row.out { 143 | background-color: #f5f5f5; 144 | } 145 | 146 | span.variable-usage { 147 | text-transform: uppercase; 148 | text-align: center; 149 | font-family: Arial; 150 | font-size: 10px; 151 | font-weight: bold; 152 | color: #666; 153 | background-color: #fff; 154 | 155 | border-radius: 4px; 156 | border: solid 2px #ccc; 157 | 158 | margin: 0px 5px; 159 | padding: 2px; 160 | width: 25px; 161 | } 162 | 163 | span.variable-qualifier { 164 | text-transform: uppercase; 165 | text-align: center; 166 | font-family: Arial; 167 | font-size: 10px; 168 | font-weight: bold; 169 | color: #666; 170 | background-color: #fff; 171 | 172 | border-radius: 4px; 173 | border: solid 2px #ccc; 174 | 175 | padding: 2px; 176 | margin: 0px 5px; 177 | width: 60px; 178 | } 179 | 180 | .variable-qualifier.uniform { 181 | color: #1b7837; 182 | } 183 | 184 | .variable-qualifier.varying { 185 | color: #b2182b; 186 | } 187 | 188 | .variable-qualifier.attribute { 189 | color: #3288bd; 190 | } 191 | 192 | span.variable-label { 193 | width: 20%; 194 | text-align: right; 195 | line-height: 25px; 196 | padding-right: 10px; 197 | padding-top: 5px; 198 | font-size: 16px; 199 | font-family: Myriad Pro, Helvetica, sans-serif; 200 | } 201 | 202 | span.variable-label.builtin { 203 | font-style: italic; 204 | } 205 | 206 | span.variable-type { 207 | font-family: Arial; 208 | font-size: 10px; 209 | border-radius: 4px; 210 | color: #fff; 211 | font-weight: bold; 212 | padding: 2px; 213 | } 214 | 215 | span.variable-type, 216 | span.variable-type.float { 217 | background-color: rgb(166, 171, 183); 218 | border: solid 2px rgb(86, 100, 138); 219 | text-shadow: 1px 1px 2px rgb(86, 100, 138); 220 | } 221 | 222 | span.variable-type.vec2, 223 | span.variable-type.vec3, 224 | span.variable-type.vec4 { 225 | background-color: rgb(149, 192, 148); 226 | border: solid 2px rgb(100, 137, 97); 227 | text-shadow: 1px 1px 2px rgb(100, 137, 97); 228 | } 229 | 230 | span.variable-type.mat2, 231 | span.variable-type.mat3, 232 | span.variable-type.mat4 { 233 | background-color: rgb(171, 143, 186); 234 | border: solid 2px rgb(119, 94, 131); 235 | text-shadow: 1px 1px 2px rgb(119, 94, 131); 236 | } 237 | 238 | table.variable-cells { 239 | font-family: monospace; 240 | font-size: 16px; 241 | } 242 | 243 | table.variable-cells input[type="text"] { 244 | font-size: 12px; 245 | font-family: Menlo, monospace; 246 | width: 4em; 247 | } 248 | 249 | .invalid-input { 250 | color: rgb(224, 8, 97); 251 | } 252 | 253 | .valid-input { 254 | color: rgb(24, 47, 117); 255 | } 256 | 257 | .inverted { 258 | filter: invert(0%); 259 | -webkit-filter: invert(0%); 260 | } 261 | 262 | .render-view { 263 | padding: 20px; 264 | } 265 | 266 | .picker-label { 267 | font-size: 16px; 268 | font-family: Myriad Pro, Helvetica, sans-serif; 269 | } 270 | 271 | .pixel-color { 272 | width: 25px; 273 | height: 25px; 274 | 275 | border: 1px solid black; 276 | } 277 | -------------------------------------------------------------------------------- /demo/assets/test_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burg/glsl-simulator/6d8c5fe2fd9a26c24fc34ff72cb607377f987e7e/demo/assets/test_image.png -------------------------------------------------------------------------------- /demo/debugger.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | glsl-simulator: debugger 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |
22 | GLSL Simulator Demo: Debugger 23 |
24 |
25 |
26 |
27 |
28 | 29 | 37 | 107 | 108 | 227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /demo/external/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | } 8 | .CodeMirror-scroll { 9 | /* Set scrolling behaviour here */ 10 | overflow: auto; 11 | } 12 | 13 | /* PADDING */ 14 | 15 | .CodeMirror-lines { 16 | padding: 4px 0; /* Vertical padding around content */ 17 | } 18 | .CodeMirror pre { 19 | padding: 0 4px; /* Horizontal padding of content */ 20 | } 21 | 22 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 23 | background-color: white; /* The little square between H and V scrollbars */ 24 | } 25 | 26 | /* GUTTER */ 27 | 28 | .CodeMirror-gutters { 29 | border-right: 1px solid #ddd; 30 | background-color: #f7f7f7; 31 | white-space: nowrap; 32 | } 33 | .CodeMirror-linenumbers {} 34 | .CodeMirror-linenumber { 35 | padding: 0 3px 0 5px; 36 | min-width: 20px; 37 | text-align: right; 38 | color: #999; 39 | -moz-box-sizing: content-box; 40 | box-sizing: content-box; 41 | } 42 | 43 | .CodeMirror-guttermarker { color: black; } 44 | .CodeMirror-guttermarker-subtle { color: #999; } 45 | 46 | /* CURSOR */ 47 | 48 | .CodeMirror div.CodeMirror-cursor { 49 | border-left: 1px solid black; 50 | } 51 | /* Shown when moving in bi-directional text */ 52 | .CodeMirror div.CodeMirror-secondarycursor { 53 | border-left: 1px solid silver; 54 | } 55 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor { 56 | width: auto; 57 | border: 0; 58 | background: #7e7; 59 | } 60 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursors { 61 | z-index: 1; 62 | } 63 | 64 | .cm-animate-fat-cursor { 65 | width: auto; 66 | border: 0; 67 | -webkit-animation: blink 1.06s steps(1) infinite; 68 | -moz-animation: blink 1.06s steps(1) infinite; 69 | animation: blink 1.06s steps(1) infinite; 70 | } 71 | @-moz-keyframes blink { 72 | 0% { background: #7e7; } 73 | 50% { background: none; } 74 | 100% { background: #7e7; } 75 | } 76 | @-webkit-keyframes blink { 77 | 0% { background: #7e7; } 78 | 50% { background: none; } 79 | 100% { background: #7e7; } 80 | } 81 | @keyframes blink { 82 | 0% { background: #7e7; } 83 | 50% { background: none; } 84 | 100% { background: #7e7; } 85 | } 86 | 87 | /* Can style cursor different in overwrite (non-insert) mode */ 88 | div.CodeMirror-overwrite div.CodeMirror-cursor {} 89 | 90 | .cm-tab { display: inline-block; text-decoration: inherit; } 91 | 92 | .CodeMirror-ruler { 93 | border-left: 1px solid #ccc; 94 | position: absolute; 95 | } 96 | 97 | /* DEFAULT THEME */ 98 | 99 | .cm-s-default .cm-keyword {color: #708;} 100 | .cm-s-default .cm-atom {color: #219;} 101 | .cm-s-default .cm-number {color: #164;} 102 | .cm-s-default .cm-def {color: #00f;} 103 | .cm-s-default .cm-variable, 104 | .cm-s-default .cm-punctuation, 105 | .cm-s-default .cm-property, 106 | .cm-s-default .cm-operator {} 107 | .cm-s-default .cm-variable-2 {color: #05a;} 108 | .cm-s-default .cm-variable-3 {color: #085;} 109 | .cm-s-default .cm-comment {color: #a50;} 110 | .cm-s-default .cm-string {color: #a11;} 111 | .cm-s-default .cm-string-2 {color: #f50;} 112 | .cm-s-default .cm-meta {color: #555;} 113 | .cm-s-default .cm-qualifier {color: #555;} 114 | .cm-s-default .cm-builtin {color: #30a;} 115 | .cm-s-default .cm-bracket {color: #997;} 116 | .cm-s-default .cm-tag {color: #170;} 117 | .cm-s-default .cm-attribute {color: #00c;} 118 | .cm-s-default .cm-header {color: blue;} 119 | .cm-s-default .cm-quote {color: #090;} 120 | .cm-s-default .cm-hr {color: #999;} 121 | .cm-s-default .cm-link {color: #00c;} 122 | 123 | .cm-negative {color: #d44;} 124 | .cm-positive {color: #292;} 125 | .cm-header, .cm-strong {font-weight: bold;} 126 | .cm-em {font-style: italic;} 127 | .cm-link {text-decoration: underline;} 128 | 129 | .cm-s-default .cm-error {color: #f00;} 130 | .cm-invalidchar {color: #f00;} 131 | 132 | /* Default styles for common addons */ 133 | 134 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 135 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 136 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 137 | .CodeMirror-activeline-background {background: #e8f2ff;} 138 | 139 | /* STOP */ 140 | 141 | /* The rest of this file contains styles related to the mechanics of 142 | the editor. You probably shouldn't touch them. */ 143 | 144 | .CodeMirror { 145 | line-height: 1; 146 | position: relative; 147 | overflow: hidden; 148 | background: white; 149 | color: black; 150 | } 151 | 152 | .CodeMirror-scroll { 153 | /* 30px is the magic margin used to hide the element's real scrollbars */ 154 | /* See overflow: hidden in .CodeMirror */ 155 | margin-bottom: -30px; margin-right: -30px; 156 | padding-bottom: 30px; 157 | height: 100%; 158 | outline: none; /* Prevent dragging from highlighting the element */ 159 | position: relative; 160 | -moz-box-sizing: content-box; 161 | box-sizing: content-box; 162 | } 163 | .CodeMirror-sizer { 164 | position: relative; 165 | border-right: 30px solid transparent; 166 | -moz-box-sizing: content-box; 167 | box-sizing: content-box; 168 | } 169 | 170 | /* The fake, visible scrollbars. Used to force redraw during scrolling 171 | before actuall scrolling happens, thus preventing shaking and 172 | flickering artifacts. */ 173 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 174 | position: absolute; 175 | z-index: 6; 176 | display: none; 177 | } 178 | .CodeMirror-vscrollbar { 179 | right: 0; top: 0; 180 | overflow-x: hidden; 181 | overflow-y: scroll; 182 | } 183 | .CodeMirror-hscrollbar { 184 | bottom: 0; left: 0; 185 | overflow-y: hidden; 186 | overflow-x: scroll; 187 | } 188 | .CodeMirror-scrollbar-filler { 189 | right: 0; bottom: 0; 190 | } 191 | .CodeMirror-gutter-filler { 192 | left: 0; bottom: 0; 193 | } 194 | 195 | .CodeMirror-gutters { 196 | position: absolute; left: 0; top: 0; 197 | padding-bottom: 30px; 198 | z-index: 3; 199 | } 200 | .CodeMirror-gutter { 201 | white-space: normal; 202 | height: 100%; 203 | -moz-box-sizing: content-box; 204 | box-sizing: content-box; 205 | padding-bottom: 30px; 206 | margin-bottom: -32px; 207 | display: inline-block; 208 | /* Hack to make IE7 behave */ 209 | *zoom:1; 210 | *display:inline; 211 | } 212 | .CodeMirror-gutter-elt { 213 | position: absolute; 214 | cursor: default; 215 | z-index: 4; 216 | } 217 | 218 | .CodeMirror-lines { 219 | cursor: text; 220 | min-height: 1px; /* prevents collapsing before first draw */ 221 | } 222 | .CodeMirror pre { 223 | /* Reset some styles that the rest of the page might have set */ 224 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 225 | border-width: 0; 226 | background: transparent; 227 | font-family: inherit; 228 | font-size: inherit; 229 | margin: 0; 230 | white-space: pre; 231 | word-wrap: normal; 232 | line-height: inherit; 233 | color: inherit; 234 | z-index: 2; 235 | position: relative; 236 | overflow: visible; 237 | } 238 | .CodeMirror-wrap pre { 239 | word-wrap: break-word; 240 | white-space: pre-wrap; 241 | word-break: normal; 242 | } 243 | 244 | .CodeMirror-linebackground { 245 | position: absolute; 246 | left: 0; right: 0; top: 0; bottom: 0; 247 | z-index: 0; 248 | } 249 | 250 | .CodeMirror-linewidget { 251 | position: relative; 252 | z-index: 2; 253 | overflow: auto; 254 | } 255 | 256 | .CodeMirror-widget {} 257 | 258 | .CodeMirror-wrap .CodeMirror-scroll { 259 | overflow-x: hidden; 260 | } 261 | 262 | .CodeMirror-measure { 263 | position: absolute; 264 | width: 100%; 265 | height: 0; 266 | overflow: hidden; 267 | visibility: hidden; 268 | } 269 | .CodeMirror-measure pre { position: static; } 270 | 271 | .CodeMirror div.CodeMirror-cursor { 272 | position: absolute; 273 | border-right: none; 274 | width: 0; 275 | } 276 | 277 | div.CodeMirror-cursors { 278 | visibility: hidden; 279 | position: relative; 280 | z-index: 3; 281 | } 282 | .CodeMirror-focused div.CodeMirror-cursors { 283 | visibility: visible; 284 | } 285 | 286 | .CodeMirror-selected { background: #d9d9d9; } 287 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 288 | .CodeMirror-crosshair { cursor: crosshair; } 289 | 290 | .cm-searching { 291 | background: #ffa; 292 | background: rgba(255, 255, 0, .4); 293 | } 294 | 295 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 296 | .CodeMirror span { *vertical-align: text-bottom; } 297 | 298 | /* Used to force a border model for a node */ 299 | .cm-force-border { padding-right: .1px; } 300 | 301 | @media print { 302 | /* Hide the cursor when printing */ 303 | .CodeMirror div.CodeMirror-cursors { 304 | visibility: hidden; 305 | } 306 | } 307 | 308 | /* Help users use markselection to safely style text background */ 309 | span.CodeMirror-selectedtext { background: none; } 310 | -------------------------------------------------------------------------------- /demo/external/jsDump.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jsDump 3 | * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com 4 | * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php) 5 | * Date: 5/15/2008 6 | * @projectDescription Advanced and extensible data dumping for Javascript. 7 | * @version 1.0.0 8 | * @author Ariel Flesler 9 | */ 10 | var jsDump; 11 | 12 | (function(){ 13 | function quote( str ){ 14 | return '"' + str.toString().replace(/"/g, '\\"') + '"'; 15 | }; 16 | function literal( o ){ 17 | return o + ''; 18 | }; 19 | function join( pre, arr, post ){ 20 | var s = jsDump.separator(), 21 | base = jsDump.indent(); 22 | inner = jsDump.indent(1); 23 | if( arr.join ) 24 | arr = arr.join( ',' + s + inner ); 25 | if( !arr ) 26 | return pre + post; 27 | return [ pre, inner + arr, base + post ].join(s); 28 | }; 29 | function array( arr ){ 30 | var i = arr.length, ret = Array(i); 31 | this.up(); 32 | while( i-- ) 33 | ret[i] = this.parse( arr[i] ); 34 | this.down(); 35 | return join( '[', ret, ']' ); 36 | }; 37 | 38 | var reName = /^function (\w+)/; 39 | 40 | jsDump = { 41 | parse:function( obj, type ){//type is used mostly internally, you can fix a (custom)type in advance 42 | var parser = this.parsers[ type || this.typeOf(obj) ]; 43 | type = typeof parser; 44 | 45 | return type == 'function' ? parser.call( this, obj ) : 46 | type == 'string' ? parser : 47 | this.parsers.error; 48 | }, 49 | typeOf:function( obj ){ 50 | var type = typeof obj, 51 | f = 'function';//we'll use it 3 times, save it 52 | return type != 'object' && type != f ? type : 53 | !obj ? 'null' : 54 | obj.exec ? 'regexp' :// some browsers (FF) consider regexps functions 55 | obj.getHours ? 'date' : 56 | obj.scrollBy ? 'window' : 57 | obj.nodeName == '#document' ? 'document' : 58 | obj.nodeName ? 'node' : 59 | obj.item ? 'nodelist' : // Safari reports nodelists as functions 60 | obj.callee ? 'arguments' : 61 | obj.call || obj.constructor != Array && //an array would also fall on this hack 62 | (obj+'').indexOf(f) != -1 ? f : //IE reports functions like alert, as objects 63 | 'length' in obj ? 'array' : 64 | type; 65 | }, 66 | separator:function(){ 67 | return this.multiline ? this.HTML ? '
' : '\n' : this.HTML ? ' ' : ' '; 68 | }, 69 | indent:function( extra ){// extra can be a number, shortcut for increasing-calling-decreasing 70 | if( !this.multiline ) 71 | return ''; 72 | var chr = this.indentChar; 73 | if( this.HTML ) 74 | chr = chr.replace(/\t/g,' ').replace(/ /g,' '); 75 | return Array( this._depth_ + (extra||0) ).join(chr); 76 | }, 77 | up:function( a ){ 78 | this._depth_ += a || 1; 79 | }, 80 | down:function( a ){ 81 | this._depth_ -= a || 1; 82 | }, 83 | setParser:function( name, parser ){ 84 | this.parsers[name] = parser; 85 | }, 86 | // The next 3 are exposed so you can use them 87 | quote:quote, 88 | literal:literal, 89 | join:join, 90 | // 91 | _depth_: 1, 92 | // This is the list of parsers, to modify them, use jsDump.setParser 93 | parsers:{ 94 | window: '[Window]', 95 | document: '[Document]', 96 | error:'[ERROR]', //when no parser is found, shouldn't happen 97 | unknown: '[Unknown]', 98 | 'null':'null', 99 | undefined:'undefined', 100 | 'function':function( fn ){ 101 | var ret = 'function', 102 | name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE 103 | if( name ) 104 | ret += ' ' + name; 105 | ret += '('; 106 | 107 | ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join(''); 108 | return join( ret, this.parse(fn,'functionCode'), '}' ); 109 | }, 110 | array: array, 111 | nodelist: array, 112 | arguments: array, 113 | object:function( map ){ 114 | var ret = [ ]; 115 | this.up(); 116 | for( var key in map ) 117 | ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) ); 118 | this.down(); 119 | return join( '{', ret, '}' ); 120 | }, 121 | node:function( node ){ 122 | var open = this.HTML ? '<' : '<', 123 | close = this.HTML ? '>' : '>'; 124 | 125 | var tag = node.nodeName.toLowerCase(), 126 | ret = open + tag; 127 | 128 | for( var a in this.DOMAttrs ){ 129 | var val = node[this.DOMAttrs[a]]; 130 | if( val ) 131 | ret += ' ' + a + '=' + this.parse( val, 'attribute' ); 132 | } 133 | return ret + close + open + '/' + tag + close; 134 | }, 135 | functionArgs:function( fn ){//function calls it internally, it's the arguments part of the function 136 | var l = fn.length; 137 | if( !l ) return ''; 138 | 139 | var args = Array(l); 140 | while( l-- ) 141 | args[l] = String.fromCharCode(97+l);//97 is 'a' 142 | return ' ' + args.join(', ') + ' '; 143 | }, 144 | key:quote, //object calls it internally, the key part of an item in a map 145 | functionCode:'[code]', //function calls it internally, it's the content of the function 146 | attribute:quote, //onode calls it internally, it's an html attribute value 147 | string:quote, 148 | date:quote, 149 | regexp:literal, //regex 150 | number:literal, 151 | 'boolean':literal 152 | }, 153 | DOMAttrs:{//attributes to dump from nodes, name=>realName 154 | id:'id', 155 | name:'name', 156 | 'class':'className' 157 | }, 158 | HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) 159 | indentChar:' ',//indentation unit 160 | multiline:true //if true, items in a collection, are separated by a \n, else just a space. 161 | }; 162 | 163 | })(); 164 | -------------------------------------------------------------------------------- /demo/external/mode/glsl.js: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * Copyright (c) 2011 Mr.doob 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 18 | * THE SOFTWARE. 19 | */ 20 | 21 | CodeMirror.defineMode("glsl", function(config, parserConfig) { 22 | var indentUnit = config.indentUnit, 23 | keywords = parserConfig.keywords || {}, 24 | builtins = parserConfig.builtins || {}, 25 | blockKeywords = parserConfig.blockKeywords || {}, 26 | atoms = parserConfig.atoms || {}, 27 | hooks = parserConfig.hooks || {}, 28 | multiLineStrings = parserConfig.multiLineStrings; 29 | var isOperatorChar = /[+\-*&%=<>!?|\/]/; 30 | 31 | var curPunc; 32 | 33 | function tokenBase(stream, state) { 34 | var ch = stream.next(); 35 | if (hooks[ch]) { 36 | var result = hooks[ch](stream, state); 37 | if (result !== false) return result; 38 | } 39 | if (ch == '"' || ch == "'") { 40 | state.tokenize = tokenString(ch); 41 | return state.tokenize(stream, state); 42 | } 43 | if (/[\[\]{}\(\),;\:\.]/.test(ch)) { 44 | curPunc = ch; 45 | return "bracket"; 46 | } 47 | if (/\d/.test(ch)) { 48 | stream.eatWhile(/[\w\.]/); 49 | return "number"; 50 | } 51 | if (ch == "/") { 52 | if (stream.eat("*")) { 53 | state.tokenize = tokenComment; 54 | return tokenComment(stream, state); 55 | } 56 | if (stream.eat("/")) { 57 | stream.skipToEnd(); 58 | return "comment"; 59 | } 60 | } 61 | if (isOperatorChar.test(ch)) { 62 | stream.eatWhile(isOperatorChar); 63 | return "operator"; 64 | } 65 | stream.eatWhile(/[\w\$_]/); 66 | var cur = stream.current(); 67 | if (keywords.propertyIsEnumerable(cur)) { 68 | if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; 69 | return "keyword"; 70 | } 71 | if (builtins.propertyIsEnumerable(cur)) { 72 | return "builtin"; 73 | } 74 | if (atoms.propertyIsEnumerable(cur)) return "atom"; 75 | return "word"; 76 | } 77 | 78 | function tokenString(quote) { 79 | return function(stream, state) { 80 | var escaped = false, next, end = false; 81 | while ((next = stream.next()) != null) { 82 | if (next == quote && !escaped) {end = true; break;} 83 | escaped = !escaped && next == "\\"; 84 | } 85 | if (end || !(escaped || multiLineStrings)) 86 | state.tokenize = tokenBase; 87 | return "string"; 88 | }; 89 | } 90 | 91 | function tokenComment(stream, state) { 92 | var maybeEnd = false, ch; 93 | while (ch = stream.next()) { 94 | if (ch == "/" && maybeEnd) { 95 | state.tokenize = tokenBase; 96 | break; 97 | } 98 | maybeEnd = (ch == "*"); 99 | } 100 | return "comment"; 101 | } 102 | 103 | function Context(indented, column, type, align, prev) { 104 | this.indented = indented; 105 | this.column = column; 106 | this.type = type; 107 | this.align = align; 108 | this.prev = prev; 109 | } 110 | function pushContext(state, col, type) { 111 | return state.context = new Context(state.indented, col, type, null, state.context); 112 | } 113 | function popContext(state) { 114 | var t = state.context.type; 115 | if (t == ")" || t == "]" || t == "}") 116 | state.indented = state.context.indented; 117 | return state.context = state.context.prev; 118 | } 119 | 120 | // Interface 121 | 122 | return { 123 | startState: function(basecolumn) { 124 | return { 125 | tokenize: null, 126 | context: new Context((basecolumn || 0) - indentUnit, 0, "top", false), 127 | indented: 0, 128 | startOfLine: true 129 | }; 130 | }, 131 | 132 | token: function(stream, state) { 133 | var ctx = state.context; 134 | if (stream.sol()) { 135 | if (ctx.align == null) ctx.align = false; 136 | state.indented = stream.indentation(); 137 | state.startOfLine = true; 138 | } 139 | if (stream.eatSpace()) return null; 140 | curPunc = null; 141 | var style = (state.tokenize || tokenBase)(stream, state); 142 | if (style == "comment" || style == "meta") return style; 143 | if (ctx.align == null) ctx.align = true; 144 | 145 | if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state); 146 | else if (curPunc == "{") pushContext(state, stream.column(), "}"); 147 | else if (curPunc == "[") pushContext(state, stream.column(), "]"); 148 | else if (curPunc == "(") pushContext(state, stream.column(), ")"); 149 | else if (curPunc == "}") { 150 | while (ctx.type == "statement") ctx = popContext(state); 151 | if (ctx.type == "}") ctx = popContext(state); 152 | while (ctx.type == "statement") ctx = popContext(state); 153 | } 154 | else if (curPunc == ctx.type) popContext(state); 155 | else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement")) 156 | pushContext(state, stream.column(), "statement"); 157 | state.startOfLine = false; 158 | return style; 159 | }, 160 | 161 | indent: function(state, textAfter) { 162 | if (state.tokenize != tokenBase && state.tokenize != null) return 0; 163 | var firstChar = textAfter && textAfter.charAt(0), ctx = state.context, closing = firstChar == ctx.type; 164 | if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : indentUnit); 165 | else if (ctx.align) return ctx.column + (closing ? 0 : 1); 166 | else return ctx.indented + (closing ? 0 : indentUnit); 167 | }, 168 | 169 | electricChars: "{}" 170 | }; 171 | }); 172 | 173 | (function() { 174 | function words(str) { 175 | var obj = {}, words = str.split(" "); 176 | for (var i = 0; i < words.length; ++i) obj[words[i]] = true; 177 | return obj; 178 | } 179 | var glslKeywords = "attribute const uniform varying break continue " + 180 | "do for while if else in out inout float int void bool true false " + 181 | "lowp mediump highp precision invariant discard return mat2 mat3 " + 182 | "mat4 vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 sampler2D " + 183 | "samplerCube struct gl_FragCoord gl_FragColor"; 184 | var glslBuiltins = "radians degrees sin cos tan asin acos atan pow " + 185 | "exp log exp2 log2 sqrt inversesqrt abs sign floor ceil fract mod " + 186 | "min max clamp mix step smoothstep length distance dot cross " + 187 | "normalize faceforward reflect refract matrixCompMult lessThan " + 188 | "lessThanEqual greaterThan greaterThanEqual equal notEqual any all " + 189 | "not dFdx dFdy fwidth texture2D texture2DProj texture2DLod " + 190 | "texture2DProjLod textureCube textureCubeLod"; 191 | 192 | function cppHook(stream, state) { 193 | if (!state.startOfLine) return false; 194 | stream.skipToEnd(); 195 | return "meta"; 196 | } 197 | 198 | // C#-style strings where "" escapes a quote. 199 | function tokenAtString(stream, state) { 200 | var next; 201 | while ((next = stream.next()) != null) { 202 | if (next == '"' && !stream.eat('"')) { 203 | state.tokenize = null; 204 | break; 205 | } 206 | } 207 | return "string"; 208 | } 209 | 210 | CodeMirror.defineMIME("text/x-glsl", { 211 | name: "glsl", 212 | keywords: words(glslKeywords), 213 | builtins: words(glslBuiltins), 214 | blockKeywords: words("case do else for if switch while struct"), 215 | atoms: words("null"), 216 | hooks: {"#": cppHook} 217 | }); 218 | }()); 219 | -------------------------------------------------------------------------------- /demo/lib/DebugView.js: -------------------------------------------------------------------------------- 1 | var DebugView = function(program, env, which) { 2 | console.assert(program instanceof GLSL.Program); 3 | console.assert(env instanceof GLSL.Environment); 4 | 5 | this.program = program; 6 | this.env = env; 7 | 8 | this._showingError = false; 9 | 10 | this._errorSources = {}; 11 | this._errorSources[GLSL.Error.Type.VertexShaderParsing] = "Vertex Shader Parsing Problem:"; 12 | this._errorSources[GLSL.Error.Type.FragmentShaderParsing] = "Fragment Shader Parsing Problem:"; 13 | this._errorSources[GLSL.Error.Type.VertexShaderTranslation] = "Vertex Shader Translation Problem:"; 14 | this._errorSources[GLSL.Error.Type.FragmentShaderTranslation] = "Fragment Shader Translation Problem:"; 15 | this._errorSources[GLSL.Error.Type.VertexShaderExecution] = "Vertex Shader Execution Problem:"; 16 | this._errorSources[GLSL.Error.Type.FragmentShaderExecution] = "Fragment Shader Execution Problem:"; 17 | 18 | this.element = document.createElement("div"); 19 | this.element.classList.add("results-viewer"); 20 | this.element.id = "results-" + which; 21 | 22 | var optionsContainer = document.createElement("span"); 23 | optionsContainer.classList.add("output-options"); 24 | this.element.appendChild(optionsContainer); 25 | 26 | this.shaderSelectorElement = document.createElement("select"); 27 | this.shaderSelectorElement.id = "results-" + which + "-shader-selector"; 28 | var option = null; 29 | option = document.createElement("option"); 30 | option.text = "Vertex Shader"; 31 | option.value = GLSL.Shader.Type.Vertex; 32 | this.shaderSelectorElement.add(option, null); 33 | 34 | option = document.createElement("option"); 35 | option.text = "Fragment Shader"; 36 | option.value = GLSL.Shader.Type.Fragment; 37 | this.shaderSelectorElement.add(option, null); 38 | optionsContainer.appendChild(this.shaderSelectorElement); 39 | 40 | this.outputSelectorElement = document.createElement("select"); 41 | this.outputSelectorElement.id = "results-" + which + "-output-selector"; 42 | option = document.createElement("option"); 43 | option.text = "Shader AST"; 44 | option.value = "ast"; 45 | this.outputSelectorElement.add(option, null); 46 | 47 | option = document.createElement("option"); 48 | option.text = "Pretty Printer"; 49 | option.value = "pretty"; 50 | this.outputSelectorElement.add(option, null); 51 | 52 | option = document.createElement("option"); 53 | option.text = "Generated JavaScript"; 54 | option.value = "codegen"; 55 | this.outputSelectorElement.add(option, null); 56 | 57 | option = document.createElement("option"); 58 | option.text = "Primary Output"; 59 | option.value = "output"; 60 | this.outputSelectorElement.add(option, null); 61 | optionsContainer.appendChild(this.outputSelectorElement); 62 | 63 | this.outputElement = document.createElement("span"); 64 | this.outputElement.classList.add("output"); 65 | this.element.appendChild(this.outputElement); 66 | 67 | 68 | this.shaderSelectorElement.addEventListener("change", this.refresh.bind(this)); 69 | this.outputSelectorElement.addEventListener("change", this.refresh.bind(this)); 70 | this.program.addEventListener(GLSL.Program.Event.ShaderChanged, this.refresh, this); 71 | this.env.addEventListener(GLSL.Environment.Event.ResultChanged, this.refresh, this); 72 | }; 73 | 74 | DebugView.prototype = { 75 | constructor: DebugView, 76 | __proto__: GLSL.Object.prototype, 77 | 78 | get activeShaderType() 79 | { 80 | return this.shaderSelectorElement.options[this.shaderSelectorElement.selectedIndex].value; 81 | }, 82 | 83 | set activeShaderType(value) 84 | { 85 | var options = this.shaderSelectorElement.options; 86 | for (var i = 0; i < options.length; ++i) 87 | if (value === options[i].value) 88 | options[i].selected = true; 89 | 90 | this.refresh(); 91 | }, 92 | 93 | get activeOutputType() 94 | { 95 | return this.outputSelectorElement.options[this.outputSelectorElement.selectedIndex].value; 96 | }, 97 | 98 | set activeOutputType(value) 99 | { 100 | var options = this.outputSelectorElement.options; 101 | for (var i = 0; i < options.length; ++i) 102 | if (value === options[i].value) 103 | options[i].selected = true; 104 | 105 | this.refresh(); 106 | }, 107 | 108 | showErrorMessage: function(type, message) 109 | { 110 | this._showingError = true; 111 | 112 | if (this.outputElement) { 113 | this.element.removeChild(this.outputElement); 114 | delete this.outputElement; 115 | } 116 | 117 | this.outputElement = document.createElement("span"); 118 | this.element.classList.add("error-message"); 119 | this.outputElement.classList.add("output"); 120 | this.outputElement.textContent = (this._errorSources[type] || "Error:") + "\n" + message; 121 | this.element.appendChild(this.outputElement); 122 | }, 123 | 124 | clearError: function() 125 | { 126 | this._showingError = false; 127 | }, 128 | 129 | refresh: function() 130 | { 131 | if (this._showingError) 132 | return; 133 | 134 | if (this.outputElement) { 135 | this.element.removeChild(this.outputElement); 136 | delete this.outputElement; 137 | } 138 | 139 | var shader = this.program.shaderWithType(this.activeShaderType); 140 | if (!shader) 141 | return; 142 | 143 | this.outputElement = document.createElement("span"); 144 | this.outputElement.classList.add("output"); 145 | this.element.appendChild(this.outputElement); 146 | this.element.classList.remove("error-message"); 147 | 148 | switch (this.activeOutputType) { 149 | case "ast": 150 | this.outputElement.appendChild(document.createTextNode(jsDump.parse(shader.ast))); 151 | break; 152 | 153 | case "codegen": 154 | var source = shader.executable.source || shader.executable.code.toString(); 155 | this.outputElement.appendChild(document.createTextNode(source)); 156 | break; 157 | 158 | case "output": 159 | if (this.activeShaderType === GLSL.Shader.Type.Fragment) { 160 | var rawColor = this.env.get('gl_FragColor'); 161 | if (!rawColor) 162 | break; 163 | 164 | var color = GLSL.Runtime.clamp(this.env.get('gl_FragColor'), 0.0, 1.0); 165 | var hex = GLSL.Runtime.floor(color.get("rgb").multiply(255.0)); 166 | var colorString = "rgba(" + [hex.get('r'), hex.get('g'), hex.get('b'), color.get('a')].join(", ") + ")"; 167 | var vectorString = [rawColor.get("r"), rawColor.get("g"), rawColor.get("b"), rawColor.get("a")].join(", "); 168 | this.outputElement.style.backgroundColor = colorString; 169 | var text = this.outputElement.appendChild(document.createElement("span")); 170 | text.classList.add("inverted"); 171 | text.textContent = "gl_FragColor = [" + vectorString + "]\n\n(color: " + colorString + ")"; 172 | } 173 | 174 | if (this.activeShaderType === GLSL.Shader.Type.Vertex) { 175 | var position = this.env.get('gl_Position'); 176 | var pointSize = this.env.get('gl_PointSize'); 177 | 178 | if (!position && !pointSize) 179 | break; 180 | 181 | var output = ""; 182 | if (position) { 183 | var positionString = [position.get("x"), position.get("y"), position.get("z"), position.get("w")].join(", "); 184 | output += "gl_Position = [" + positionString + "]\n"; 185 | } 186 | if (pointSize) 187 | output += "gl_PointSize = " + pointSize; 188 | 189 | this.outputElement.textContent = output; 190 | } 191 | break; 192 | 193 | case "pretty": 194 | default: 195 | var printer = new GLSL.PrettyPrinter(); 196 | this.outputElement.appendChild(document.createTextNode(printer.formattedText(shader.ast))); 197 | break; 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /demo/lib/EditorView.js: -------------------------------------------------------------------------------- 1 | var EditorView = function(program) { 2 | this.program = program; 3 | 4 | this.element = document.createElement("div"); 5 | this.element.classList.add("shader-editor"); 6 | 7 | var item = null; 8 | 9 | this.optionsElement = document.createElement("span"); 10 | this.optionsElement.classList.add("editor-options"); 11 | var optionsList = this.optionsElement.appendChild(document.createElement("ul")); 12 | this.selectorElement = document.createElement("select"); 13 | this.selectorElement.id = "editor-selector"; 14 | var option = null; 15 | option = document.createElement("option"); 16 | option.text = "Vertex Shader"; 17 | option.value = GLSL.Shader.Type.Vertex; 18 | this.selectorElement.add(option, null); 19 | 20 | option = document.createElement("option"); 21 | option.text = "Fragment Shader"; 22 | option.value = GLSL.Shader.Type.Fragment; 23 | this.selectorElement.add(option, null); 24 | item = optionsList.appendChild(document.createElement("li")); 25 | item.appendChild(this.selectorElement); 26 | var checkboxLabel = document.createElement("label"); 27 | this.emitDebuggerCheckbox = document.createElement("input"); 28 | this.emitDebuggerCheckbox.type = "checkbox"; 29 | checkboxLabel.textContent = "Pause Inside Shader"; 30 | checkboxLabel.appendChild(this.emitDebuggerCheckbox); 31 | item = optionsList.appendChild(document.createElement("li")); 32 | item.appendChild(checkboxLabel); 33 | this.element.appendChild(this.optionsElement); 34 | 35 | // Select the fragment shader by default. 36 | this.selectorElement.options[1].selected = true; 37 | this.selectorElement.addEventListener("change", this._shaderSelectorChanged.bind(this)); 38 | 39 | var editorStub = document.createElement("div"); 40 | editorStub.id = "shader-editor-stub"; 41 | this.element.appendChild(editorStub); 42 | 43 | // Set up CodeMirror to replace the editor stub. 44 | this._cm = CodeMirror(function(editorNode) { 45 | editorStub.parentNode.replaceChild(editorNode, editorStub); 46 | editorNode.id = editorStub.id; 47 | editorNode.classList.add("stub"); 48 | }, { 49 | value: "", 50 | mode: "glsl", 51 | lineNumbers: true 52 | }); 53 | 54 | this._cm.on("changes", this._shaderEditorContentChanged.bind(this)); 55 | this.emitDebuggerCheckbox.addEventListener("change", this._emitDebuggerCheckboxChanged.bind(this)); 56 | }; 57 | 58 | EditorView.Event = { 59 | ShaderTypeChanged: "editor-view-shader-type-changed" 60 | }; 61 | 62 | EditorView.prototype = { 63 | constructor: EditorView, 64 | __proto__: GLSL.Object.prototype, 65 | 66 | // Public 67 | 68 | get activeShaderType() 69 | { 70 | return this.selectorElement.options[this.selectorElement.selectedIndex].value; 71 | }, 72 | 73 | set activeShaderType(value) 74 | { 75 | var options = this.selectorElement.options; 76 | for (var i = 0; i < options.length; ++i) 77 | if (value === options[i].value) 78 | options[i].selected = true; 79 | 80 | this.refresh(); 81 | }, 82 | 83 | refresh: function() 84 | { 85 | this.autosizeHeight(); 86 | 87 | var selectedShader = this.program.shaderWithType(this.activeShaderType); 88 | this._cm.setValue(selectedShader ? selectedShader.sourceText : ""); 89 | }, 90 | 91 | autosizeHeight: function() 92 | { 93 | var allowedHeight = this.element.offsetHeight - 5 + "px"; 94 | document.getElementById("shader-editor-stub").style.height = allowedHeight; 95 | }, 96 | 97 | // Private 98 | 99 | _shaderSelectorChanged: function(event) 100 | { 101 | console.assert(event.target === this.selectorElement, event.target); 102 | console.assert(event.target.selectedIndex != -1, event.target); 103 | 104 | var inactiveShaderType = (this.activeShaderType === GLSL.Shader.Type.Vertex) ? GLSL.Shader.Type.Fragment : GLSL.Shader.Type.Vertex; 105 | this.program.shaderWithType(inactiveShaderType).shouldEmitDebuggerStatement = false; 106 | this.program.shaderWithType(this.activeShaderType).shouldEmitDebuggerStatement = !!this.emitDebuggerCheckbox.checked; 107 | 108 | this.refresh(); 109 | this.dispatchEventToListeners(EditorView.Event.ShaderTypeChanged, this.activeShaderType); 110 | }, 111 | 112 | _shaderEditorContentChanged: function(event) 113 | { 114 | this.program.updateShaderWithType(this.activeShaderType, this._cm.getValue()); 115 | }, 116 | 117 | _emitDebuggerCheckboxChanged: function(event) 118 | { 119 | var activeShader = this.program.shaderWithType(this.activeShaderType); 120 | activeShader.shouldEmitDebuggerStatement = !!this.emitDebuggerCheckbox.checked; 121 | }, 122 | }; 123 | -------------------------------------------------------------------------------- /demo/lib/ImageView.js: -------------------------------------------------------------------------------- 1 | var ImageView = function(options, suggestedVariableValueCallback) { 2 | 3 | this.element = document.createElement("div"); 4 | this.element.classList.add("image-container"); 5 | 6 | this.image = document.createElement("img"); 7 | this.image.src = "assets/test_image.png"; 8 | this.image.height = 150; 9 | this.element.appendChild(this.image); 10 | }; 11 | 12 | ImageView.Event = { 13 | VariableValueChanged: "image-view-value-changed" 14 | }; 15 | 16 | ImageView.prototype = { 17 | constructor: ImageView, 18 | __proto__: GLSL.Object.prototype, 19 | 20 | // Public 21 | 22 | populateResults: function(env) 23 | { 24 | console.assert(env instanceof GLSL.Environment, env); 25 | 26 | this._variableMap.forEach(function(value, key, map) { 27 | var variable = key, view = value; 28 | var storedValue = env.get(variable.name); 29 | 30 | if (!variable.readOnly && storedValue) 31 | view.insertValue(storedValue); 32 | }, this); 33 | }, 34 | 35 | // Private 36 | 37 | _inputChanged: function(event) 38 | { 39 | var view = event.target; 40 | var data = {variable: view.variable, value: view.extractValue()}; 41 | this.dispatchEventToListeners(ImageView.Event.VariableValueChanged, data); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /demo/lib/RenderView.js: -------------------------------------------------------------------------------- 1 | var RenderView = function(program, env) { 2 | this.element = document.createElement("div"); 3 | this.element.classList.add("render-view"); 4 | 5 | var label = this.element.appendChild(document.createElement("div")); 6 | label.classList.add("picker-label"); 7 | label.textContent = "Select a pixel to debug:"; 8 | 9 | this._canvasElement = document.createElement("canvas"); 10 | this.element.appendChild(this._canvasElement); 11 | 12 | this._canvasElement.addEventListener("mousemove", this._canvasMouseEvent.bind(this)); 13 | this._canvasElement.addEventListener("click", this._canvasMouseClicked.bind(this)); 14 | 15 | this._updateButton = document.createElement("input"); 16 | this._updateButton.type = "button"; 17 | this._updateButton.value = "Render"; 18 | this._updateButton.disabled = true; 19 | this.element.appendChild(this._updateButton); 20 | this._updateButton.addEventListener("click", this._updateButtonClicked.bind(this)); 21 | 22 | this._pixelCoordElement = document.createElement("span"); 23 | this._pixelCoordElement.classList.add("pixel-coord"); 24 | this.element.appendChild(this._pixelCoordElement); 25 | 26 | this._pixelColorElement = document.createElement("div"); 27 | this._pixelColorElement.classList.add("pixel-color"); 28 | this.element.appendChild(this._pixelColorElement); 29 | 30 | this.updateProgram(program); 31 | this.updateEnvironment(env); 32 | }; 33 | 34 | RenderView.Event = { 35 | PixelSelected: "render-view-pixel-selected" 36 | } 37 | 38 | RenderView.prototype = { 39 | constructor: RenderView, 40 | __proto__: GLSL.Object.prototype, 41 | 42 | updateProgram: function(program) 43 | { 44 | console.assert(program instanceof GLSL.Program); 45 | this.program = new GLSL.Program; 46 | if (program.vertexShader) 47 | this.program.updateShaderWithType(GLSL.Shader.Type.Vertex, program.vertexShader.sourceText); 48 | 49 | if (program.fragmentShader) 50 | this.program.updateShaderWithType(GLSL.Shader.Type.Fragment, program.fragmentShader.sourceText); 51 | 52 | this._updateButton.disabled = false; 53 | }, 54 | 55 | updateEnvironment: function(env) 56 | { 57 | console.assert(env instanceof GLSL.Environment); 58 | this.env = env.clone(); 59 | 60 | var resolution = this.env.get("iResolution") || this.env.get("resolution") || GLSL.Runtime.Vec3(200, 200, 1); 61 | var w = resolution.get('x'); 62 | var h = resolution.get('y'); 63 | if (!this._context || !(this.renderWidth == w && this.renderHeight == h)) { 64 | this.renderWidth = this._canvasElement.width = w; 65 | this.renderHeight = this._canvasElement.height = h; 66 | var ctx = this._context = this._canvasElement.getContext("2d"); 67 | this._buffer = this._context.createImageData(this.renderWidth, this.renderHeight); 68 | 69 | // Draw placeholder box. 70 | ctx.fillStyle = "white"; 71 | ctx.strokeStyle = "black"; 72 | ctx.fillRect(0, 0, w, h); 73 | ctx.strokeRect(0, 0, w, h); 74 | } 75 | 76 | this._updateButton.disabled = false; 77 | }, 78 | 79 | // Private 80 | 81 | _renderImage: function() 82 | { 83 | if (!this.program.vertexShader || !this.program.fragmentShader) 84 | return; 85 | 86 | this.program.renderToBuffer(this.env, this._buffer); 87 | this._context.putImageData(this._buffer, 0, 0); 88 | this._updateButton.disabled = true; 89 | }, 90 | 91 | _updateButtonClicked: function() 92 | { 93 | this._renderImage(); 94 | }, 95 | 96 | _canvasMouseEvent: function(event) 97 | { 98 | var x = event.clientX - event.target.offsetLeft; 99 | var y = event.clientY - event.target.offsetTop; 100 | this._selectedCoord = [x, y]; 101 | 102 | var rgba = [this._buffer.data[(y * this.renderWidth + x) * 4 + 0] | 0, 103 | this._buffer.data[(y * this.renderWidth + x) * 4 + 1] | 0, 104 | this._buffer.data[(y * this.renderWidth + x) * 4 + 2] | 0, 105 | (this._buffer.data[(y * this.renderWidth + x) * 4 + 3] / 255) | 0]; 106 | 107 | this._pixelColorElement.style.backgroundColor = "rgba(" + rgba.join(", ") + ")"; 108 | this._pixelCoordElement.textContent = "gl_FragCoord = (" + [x, y, 1, 1].join(", ") + ")"; 109 | }, 110 | 111 | _canvasMouseClicked: function(event) 112 | { 113 | this._canvasMouseEvent(event); 114 | 115 | this.dispatchEventToListeners(RenderView.Event.PixelSelected, {x: this._selectedCoord.x, y: this._selectedCoord.y}); 116 | } 117 | }; 118 | -------------------------------------------------------------------------------- /demo/lib/VariableView.js: -------------------------------------------------------------------------------- 1 | var VariableView = function(variable, suggestedValue) { 2 | this.variable = variable; 3 | 4 | this._fields = []; 5 | 6 | var variableRow = this.element = document.createElement("div"); 7 | variableRow.classList.add("variable-row", variable.usage); 8 | 9 | var usageLabel = variableRow.appendChild(document.createElement("span")); 10 | usageLabel.classList.add("variable-usage", variable.usage); 11 | usageLabel.textContent = variable.usage; 12 | 13 | var qualifierLabel = variableRow.appendChild(document.createElement("span")); 14 | qualifierLabel.classList.add("variable-qualifier", variable.qualifier); 15 | qualifierLabel.textContent = variable.qualifier; 16 | 17 | var label = variableRow.appendChild(document.createElement("span")); 18 | label.classList.add("variable-label"); 19 | if (variable.builtin) 20 | label.classList.add("builtin"); 21 | label.textContent = variable.name; 22 | 23 | var typeLabel = variableRow.appendChild(document.createElement("span")); 24 | typeLabel.classList.add(variable.type, "variable-type"); 25 | typeLabel.textContent = variable.type; 26 | 27 | // TODO: handle sampler2D and samplerCube seperately, by adding a 28 | // file upload widget and thumbnail preview. 29 | 30 | var cellTable = variableRow.appendChild(document.createElement("table")); 31 | cellTable.classList.add("variable-cells"); 32 | 33 | var rowCount = 1; 34 | var colCount = 1; 35 | 36 | switch (variable.type) { 37 | case "float": break; 38 | case "vec2": colCount = 2; break; 39 | case "vec3": colCount = 3; break; 40 | case "vec4": colCount = 4; break; 41 | case "mat2": colCount = 2; rowCount = 2; break; 42 | case "mat3": colCount = 3; rowCount = 3; break; 43 | case "mat4": colCount = 4; rowCount = 4; break; 44 | default: break; 45 | } 46 | 47 | for (var i = 0; i < rowCount; ++i) { 48 | var row = document.createElement("tr"); 49 | for (var j = 0; j < colCount; ++j) { 50 | var cell = document.createElement("td"); 51 | if (j == 0) 52 | cell.appendChild(document.createTextNode(i == 0 && colCount > 1 ? "[" : "\u00A0")); 53 | 54 | var input = cell.appendChild(document.createElement("input")); 55 | input.type = "text"; 56 | input.maxLength = 5; 57 | input.placeholder = "1.0"; 58 | if (variable.usage === "out") 59 | input.readOnly = true; 60 | this._fields.push(input); 61 | if (j < colCount - 1) 62 | cell.appendChild(document.createTextNode(",")); 63 | else if (colCount > 1 && i == rowCount - 1) 64 | cell.appendChild(document.createTextNode("]")) 65 | row.appendChild(cell); 66 | } 67 | 68 | cellTable.appendChild(row); 69 | } 70 | 71 | if (variable.usage === "in" && !!suggestedValue) 72 | this.insertValue(suggestedValue); 73 | 74 | if (variable.usage === "in") 75 | cellTable.addEventListener("change", this._fieldInputChanged.bind(this)); 76 | } 77 | 78 | VariableView.Event = { 79 | "InputChanged": "variable-view-input-changed", 80 | }; 81 | 82 | VariableView.prototype = { 83 | constructor: VariableView, 84 | __proto__: GLSL.Object.prototype, 85 | 86 | // Public 87 | 88 | insertValue: function(value) 89 | { 90 | var dim = 4; 91 | 92 | switch (this.variable.type) { 93 | case "float": this._fields[0].value = value; break; 94 | 95 | case "vec2": dim--; 96 | case "vec3": dim--; 97 | case "vec4": 98 | for (var i = 0; i < dim; ++i) 99 | this._fields[i].value = value.d[i]; 100 | break; 101 | 102 | case "vec2": dim--; 103 | case "vec3": dim--; 104 | case "vec4": 105 | for (var i = 0; i < dim; ++i) 106 | for (var j = 0; j < dim; ++j) 107 | this._fields[i * dim + j].value = value.d[i][j]; 108 | break; 109 | 110 | default: break; 111 | } 112 | }, 113 | 114 | extractValue: function() 115 | { 116 | var values = this._fields.map(function(f) { 117 | var endsWithDot = f.value && f.value.charAt(f.value.length - 1) === '.'; 118 | return (f.value && f.value.length && !endsWithDot) ? Number(f.value) : NaN; 119 | }); 120 | if (values.some(function(v) { return isNaN(v); })) 121 | return null; 122 | 123 | switch (this.variable.type) { 124 | case "float": return values[0]; 125 | case "vec2": return GLSL.Runtime.Vec2.apply(null, values); 126 | case "vec3": return GLSL.Runtime.Vec3.apply(null, values); 127 | case "vec4": return GLSL.Runtime.Vec4.apply(null, values); 128 | case "mat2": return GLSL.Runtime.Mat2.apply(null, values); 129 | case "mat3": return GLSL.Runtime.Mat3.apply(null, values); 130 | case "mat4": return GLSL.Runtime.Mat4.apply(null, values); 131 | } 132 | }, 133 | 134 | // Private 135 | 136 | _fieldInputChanged: function(event) 137 | { 138 | var cell = event.target; 139 | cell.classList.remove("valid-input"); 140 | cell.classList.remove("invalid-input"); 141 | 142 | var endsWithDot = cell.value && cell.value.charAt(cell.value.length - 1) === '.'; 143 | if (endsWithDot || isNaN(Number(cell.value))) { 144 | cell.classList.add("invalid-input"); 145 | return; 146 | } 147 | 148 | cell.classList.add("valid-input"); 149 | 150 | if (this.extractValue() !== null) // Don't send an event unless all fields complete. 151 | this.dispatchEventToListeners(VariableView.Event.InputChanged); 152 | } 153 | }; 154 | 155 | var VariableListView = function(variables, suggestedVariableValueCallback) { 156 | this._variableMap = new Map; 157 | 158 | this.element = document.createElement("div"); 159 | this.element.classList.add("variable-list"); 160 | 161 | if (!variables.length) 162 | return; 163 | 164 | function createVariable(variable) { 165 | var view = new VariableView(variable, suggestedVariableValueCallback(variable)); 166 | this._variableMap.set(variable, view); 167 | this.element.appendChild(view.element); 168 | 169 | if (!variable.readOnly) 170 | view.addEventListener(VariableView.Event.InputChanged, this._inputChanged, this); 171 | } 172 | 173 | variables.map(createVariable.bind(this)); 174 | }; 175 | 176 | VariableListView.fromShader = function(shader, suggestedVariableValueCallback) { 177 | var variables = []; 178 | if (shader) { 179 | variables = variables.concat(shader.uniforms); 180 | if (shader.type === GLSL.Shader.Type.Vertex) 181 | variables = variables.concat(shader.attributes); 182 | 183 | variables = variables.concat(shader.varyings); 184 | } 185 | return new VariableListView(variables, suggestedVariableValueCallback); 186 | } 187 | 188 | VariableListView.Event = { 189 | VariableValueChanged: "variable-list-view-variable-value-changed" 190 | }; 191 | 192 | VariableListView.prototype = { 193 | constructor: VariableListView, 194 | __proto__: GLSL.Object.prototype, 195 | 196 | // Public 197 | 198 | populateResults: function(env) 199 | { 200 | console.assert(env instanceof GLSL.Environment, env); 201 | 202 | this._variableMap.forEach(function(value, key, map) { 203 | var variable = key, view = value; 204 | var storedValue = env.get(variable.name); 205 | 206 | if (!variable.readOnly && storedValue) 207 | view.insertValue(storedValue); 208 | }, this); 209 | }, 210 | 211 | // Private 212 | 213 | _inputChanged: function(event) 214 | { 215 | var view = event.target; 216 | var data = {variable: view.variable, value: view.extractValue()}; 217 | this.dispatchEventToListeners(VariableListView.Event.VariableValueChanged, data); 218 | } 219 | }; 220 | -------------------------------------------------------------------------------- /demo/parser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | glsl-simulator: parser 4 | 5 | 6 | 7 | 8 | 9 | 10 | 43 | 44 | 45 | 46 | 47 |
48 |
49 | // Sample shader from: http://glslsandbox.com/e#21330.5 50 | 51 | /* Here's a block 52 | comment for you. 53 | */ 54 | 55 | #ifdef GL_ES 56 | precision mediump float; 57 | #endif 58 | 59 | uniform float time; 60 | uniform vec2 mouse; 61 | uniform vec2 resolution; 62 | // b/w remix 63 | // tomaes.32x.de 2014.11 64 | void main( void ) { 65 | 66 | vec3 col = vec3(0.1,0.2,0.3); 67 | vec2 pos = ( gl_FragCoord.xy / resolution.xy ); 68 | 69 | float sd = 0.19 - (pos.y*0.004 / pos.x*0.5) - atan( pos.x + pos.y, 40.0 ); 70 | float so = 0.22 + pos.y*0.0003/pos.x*(0.15 + sin(time*0.04+pos.x*3.5)); 71 | 72 | float t = mod(time*0.1, 2.0) + 440.0; 73 | float x = mod(pos.x + t, so); 74 | float y = mod(pos.y + t, so*2.0); 75 | float d1 = mod( distance( vec2(x,y), vec2(so*0.45,so*1.05) ) + t*0.5, 0.05) * 3.0 + pos.x * 0.5; 76 | float d2 = mod( distance( vec2(x,y), vec2(so*0.55,so*0.95) ) + t*0.5, 0.015) * 3.0; 77 | 78 | if ((x-0.03 < sd) && (y-0.03 < sd*2.0)) 79 | if ((x < sd) && (y < sd*2.0)) 80 | col = vec3(0.2, 0.6, mix(d1, d2, 0.8) ); 81 | else 82 | col = vec3(0.72, 0.25, pos.y * 0.06 + 0.3); 83 | 84 | 85 | float l = length(mod(t* 0.1 + col * distance(pos,vec2(pos.y,0.0)) ,0.02)*27.5); 86 | 87 | if ((pos.y>0.1) && (pos.y < 0.9)) 88 | gl_FragColor = vec4( l,l,l, 1.0 ); 89 | else 90 | gl_FragColor = vec4( 0.9,0.9,0.9, 1.0); 91 | } 92 |
93 | 94 |
95 | No results yet. 96 |
97 |
98 | 99 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /demo/translate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | glsl-simulator: manual input 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 | GLSL Simulator Demo: Single Execution 21 |
22 |
23 |
24 | 25 |
26 | 27 | 35 | 105 | 106 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /docs/Runtime Notes.md: -------------------------------------------------------------------------------- 1 | ## GLSL simulator Runtime specs ## 2 | 3 | Here are a set of our APIs that are different from the GLSL shader language. 4 | 5 | | | GLSL | Runtime Type | Runtime Function 6 | | :------------ |:---------------| :----- | :----- | 7 | | | length() | | r.len() | 8 | | vec access | u = v.xy | u = v.get("xy") | u = r.get(v, "xy") | 9 | | vec access | v.xy = u | v.set("xy", u) | r.set(v, "xy", u) | 10 | | vec access | f = v[3] | f = v.get(3) | f = r.get(v, 3) | 11 | | vec access | v[3] = f | v.set(3, u) | r.set(v, 3, f) | 12 | | vec op | v = f * v | v.multiply(f) | r.multiply(f, v) | 13 | | mat access | v = m[1] | v = m.get(1) | v = r.get(m, 1) | 14 | | mat access | f = m[1][2] | f = m.get(1, 2) | f = r.get(m, 1, 2) | 15 | | mat access | f = m[1].y | f = m.get(1, "y") | f = r.get(m, 1, "y") | 16 | | mat access | m[1] = vec4(2.0) | m.set(1, vec4(2.0)) | r.set(m, 1, vec4(2.0)) | 17 | | mat access | m[1][2] = 1.0 | m.set(1, 2, 1.0) | r.set(m, 1, 2, 1.0) | 18 | | mat | m = f * m | m.multiply(f) | r.multiply(f, m) | 19 | 20 | ### Runtime APIs ### 21 | 22 | ### TODOs ### 23 | 24 | Week of 11/24/14 -- 11/30/14 25 | 26 | * [x] Make getters and setters work 27 | * [x] Make casting (for vec? and mat?) work, from both low to high dimensions and high to low dimensions 28 | * [x] Construct vec and mat by using a single value 29 | * [x] Make built-in functions work for vec? 30 | * [ ] Implement op.js build-in functions 31 | * [ ] Testing framework 32 | 33 | Week of 12/1/14 -- 12/7/14 34 | 35 | * [ ] Implement texture lookup functions 36 | * [ ] Implement Vertex/Fragment shader special variables 37 | * [ ] Test thoroughly 38 | 39 | Week of 12/8/14 -- 12/12/14 40 | 41 | * [ ] Demo 42 | * [ ] Writeups 43 | -------------------------------------------------------------------------------- /docs/samples/s1.frag: -------------------------------------------------------------------------------- 1 | 2 | // Sample shader from: http://glslsandbox.com/e#21330.5 3 | 4 | /* Here's a block 5 | comment for you. 6 | */ 7 | 8 | #ifdef GL_ES 9 | precision mediump float; 10 | #endif 11 | 12 | uniform float time; 13 | uniform vec2 mouse; 14 | uniform vec2 resolution; 15 | // b/w remix 16 | // tomaes.32x.de 2014.11 17 | void main( void ) { 18 | 19 | vec3 col = vec3(0.1,0.2,0.3); 20 | vec2 pos = ( gl_FragCoord.xy / resolution.xy ); 21 | 22 | float sd = 0.19 - (pos.y*0.004 / pos.x*0.5) - atan( pos.x + pos.y, 40.0 ); 23 | float so = 0.22 + pos.y*0.0003/pos.x*(0.15 + sin(time*0.04+pos.x*3.5)); 24 | 25 | float t = mod(time*0.1, 2.0) + 440.0; 26 | float x = mod(pos.x + t, so); 27 | float y = mod(pos.y + t, so*2.0); 28 | float d1 = mod( distance( vec2(x,y), vec2(so*0.45,so*1.05) ) + t*0.5, 0.05) * 3.0 + pos.x * 0.5; 29 | float d2 = mod( distance( vec2(x,y), vec2(so*0.55,so*0.95) ) + t*0.5, 0.015) * 3.0; 30 | 31 | if ((x-0.03 < sd) && (y-0.03 < sd*2.0)) 32 | if ((x < sd) && (y < sd*2.0)) 33 | col = vec3(0.2, 0.6, mix(d1, d2, 0.8) ); 34 | else 35 | col = vec3(0.72, 0.25, pos.y * 0.06 + 0.3); 36 | 37 | 38 | float l = length(mod(t* 0.1 + col * distance(pos,vec2(pos.y,0.0)) ,0.02)*27.5); 39 | 40 | if ((pos.y>0.1) && (pos.y < 0.9)) 41 | gl_FragColor = vec4( l,l,l, 1.0 ); 42 | else 43 | gl_FragColor = vec4( 0.9,0.9,0.9, 1.0); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /docs/samples/s1.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GLSL: pass in the GLSL runtime 3 | * opt: pass in the inputs/outputs; return opt in the end 4 | */ 5 | 6 | (function(GLSL, opt) { 7 | 8 | (function (time, mouse, resolution, r) { 9 | var col = r.vec3(0.1, 0.2, 0.3); 10 | var pos = r.get(opt.gl_FragCoord, "xy").divide(r.get(resolution, "xy")); 11 | 12 | var sd = 0.19 - (pos.y * 0.004 / pos.x * 0.5) - r.atan(pos.x + pos.y, 40.0); 13 | var so = 0.22 + pos.y * 0.0003 / pos.x * (0.15 + r.sin(time * 0.04 + pos.x * 3.5)); 14 | 15 | var t = r.mod(time * 0.1, 2.0) + 440.0; 16 | var x = r.mod(pos.x + t, so); 17 | var y = r.mod(pos.y + t, so * 2.0); 18 | var d1 = r.mod( 19 | r.distance(r.vec2(x, y), r.vec2(so * 0.45, so * 1.05)) + t * 0.5 20 | , 0.05) * 3.0 + pos.x * 0.5; 21 | var d2 = r.mod( 22 | r.distance(r.vec2(x, y), r.vec2(so * 0.55, so * 0.95)) + t * 0.5 23 | , 0.015) * 3.0; 24 | 25 | if (x - 0.03 < sd && y - 0.03 < sd * 2.0) 26 | if (x < sd && y < sd * 2.0) 27 | col = r.vec3(0.2, 0.6, r.mix(d1, d2, 0.8)); 28 | else 29 | col = r.vec3(0.72, 0.25, pos.y * 0.06 + 0.3); 30 | 31 | l = r.length(r.mod(t * 0.1 + dol * r.distance(pos, r.vec2(pos.y, 0.0)), 0.02) * 27.5); 32 | 33 | if (pos.y > 0.1 && pos.y < 0.9) 34 | opt.gl_FragColor = r.vec4(1, 1, 1, 1.0); 35 | else 36 | opt.gl_FragColor = r.vec4(0.9, 0.9, 0.9, 1.0); 37 | 38 | return opt; 39 | })(opt.time, opt.mouse, opt.resolution, GLSL.r); 40 | 41 | })(GLSL, opt); 42 | -------------------------------------------------------------------------------- /lib/compiler/ast.js: -------------------------------------------------------------------------------- 1 | ASTNode = function(type, position, properties) 2 | { 3 | this.type = type; 4 | this.position = position; 5 | 6 | for (var prop in properties) 7 | if (properties.hasOwnProperty(prop)) 8 | this[prop] = properties[prop]; 9 | }; 10 | 11 | ASTNode.__nextId = 1; 12 | 13 | // Map of our node type names to values of the PEG parser's "type" property. 14 | // Expected properties are documented here per-node. 15 | // 16 | // Some aliases used in the comments: 17 | // Node.$initializer == Node.{FunctionPrototype, FunctionDeclaration, Invariant, Precision, Declarator} 18 | // Node.$expression == Node.{Operator, PostfixExpression, UnaryExpression, BinaryExpression, TernaryExpression, IndexSelector, FieldSelector, Identifier, IntegerLiteral, FloatLiteral, BooleanLiteral} 19 | // Node.$statement == Node.{IfStatement, ForStatement, WhileStatement, DoStatement, ReturnStatement, ContinueStatement, BreakStatement, DiscardStatement, ExpressionStatement, Preprocessor, MacroCall} 20 | ASTNode.Types = { 21 | Program: 'root', // {statements: [Node.$statement]*} 22 | Preprocessor: 'preprocessor', // {directive: string, identifier: string?, parameters: [string]?, value: string?, guarded_statements: [Node.$statement]*} 23 | MacroCall: 'macro_call', // {macro_name: string, parameters: [string]+} 24 | FunctionCall: 'function_call', // {function_name: string, parameters: [Node.$expression]*} 25 | FunctionPrototype: 'function_prototype', // {name: string, returnType: Node.Type, parameters: [Node.Parameter]*} 26 | FunctionDeclaration: 'function_declaration', // {name: string, returnType: Node.Type, parameters: [Node.Parameter]*, body: Node.Scope} 27 | Scope: 'scope', // {statements: [Node.$statement]*} 28 | 29 | IfStatement: 'if_statement', // {condition: Node.$expression, body: Node.Scope, elseBody: Node.Scope?} 30 | ForStatement: 'for_statement', // {initializer: Node.$initializer, condition: Node.$expression, increment: Node.$expression, body: Node.Scope}} 31 | WhileStatement: 'while_statement', // {condition: Node.$expression, body: Node.Scope} 32 | DoStatement: 'do_statement', // {condition: Node.$expression, body: Node.Scope} 33 | ReturnStatement: 'return', // {value: Node.$expression} 34 | ContinueStatement: 'continue', // {} 35 | BreakStatement: 'break', // {} 36 | DiscardStatement: 'discard', // {} 37 | ExpressionStatement: 'expression', // {expression: Node.$expression?} 38 | 39 | Declarator: 'declarator', // {typeAttribute: Node.Type, declarators: [Node.DeclaratorItem]+} 40 | DeclaratorItem: 'declarator_item', // {name: Node.Identifier, initializer: Node.$expression} 41 | Invariant: 'invariant', // {identifiers: [Node.Identifier]*} 42 | Precision: 'precision', // {precision: string, typeName: string} 43 | Parameter: 'parameter', // {type_name: string, name: string, typeQualifier: string?, parameterQualifier: string?, precision: string?, arraySize: Node.$expression} 44 | StructDefinition: 'struct_definition', // {qualifier: string?, name: string?, members: [Node.Declarator]+, declarators: [Node.Declarator]?} 45 | Type: 'type', // {name: string, precision: string?, qualifier: string?} 46 | 47 | IntegerLiteral: 'int', // {value: number} 48 | FloatLiteral: 'float', // {value: number} 49 | BooleanLiteral: 'bool', // {value: boolean} 50 | Identifier: 'identifier', // {name: string} 51 | 52 | Operator: 'operator', // {operator: {string, Node.FieldSelector, Node.IndexSelector}} 53 | PostfixExpression: 'postfix', // {operator: Node.Operator, expression: Node.$expression} 54 | UnaryExpression: 'unary', // {operator: Node.Operator, expression: Node.$expression} 55 | BinaryExpression: 'binary', // {operator: Node.Operator, left: Node.$expression, right:}Node.$expression} 56 | TernaryExpression: 'ternary', // {condition: Node.$expression, is_true: Node.$expression, is_false: Node.$expression} 57 | IndexSelector: 'accessor', // {index: Node.$expression} 58 | FieldSelector: 'field_selector', // {selection: string} 59 | }; 60 | 61 | module.exports = ASTNode; 62 | -------------------------------------------------------------------------------- /lib/compiler/codegen.js: -------------------------------------------------------------------------------- 1 | var ASTVisitor = require("./visitor"); 2 | var Builtins = require("../runtime/builtins"); 3 | var Operations = require("../runtime/ops"); 4 | 5 | var CodeGenerator = function(shader) 6 | { 7 | ASTVisitor.call(this); 8 | this._shader = shader; 9 | this._currentIndent = ""; 10 | 11 | this._withinFunctionScope = null; 12 | 13 | this._globalVariableNames = new Set; 14 | 15 | for (var list = shader.uniforms, i = 0; i < list.length; ++i) 16 | this._globalVariableNames.add(list[i].name); 17 | 18 | for (var list = shader.varyings, i = 0; i < list.length; ++i) 19 | this._globalVariableNames.add(list[i].name); 20 | 21 | for (var list = shader.attributes, i = 0; i < list.length; ++i) 22 | this._globalVariableNames.add(list[i].name); 23 | }; 24 | 25 | CodeGenerator.ConstructorMap = { 26 | "vec2": "Vec2", 27 | "vec3": "Vec3", 28 | "vec4": "Vec4", 29 | "mat2": "Mat2", 30 | "mat3": "Mat3", 31 | "mat4": "Mat4", 32 | }; 33 | 34 | CodeGenerator.prototype = { 35 | constructor: CodeGenerator, 36 | __proto__: ASTVisitor.prototype, 37 | 38 | translateShader: function() { 39 | this._lines = []; 40 | this._addLine(""); 41 | this._addLine("var RT = GLSL.Runtime;"); 42 | this._addLine(""); 43 | this.visitNode(this._shader.ast); 44 | this._addLine(""); 45 | if (this._shader.shouldEmitDebuggerStatement) 46 | this._addLine("debugger;") 47 | this._addLine(this._resolveFunction("main") + "();"); 48 | this._addLine(""); 49 | var executable = {}; 50 | try { 51 | executable.code = new Function("GLSL", "env", this._lines.join("\n")); 52 | } catch (e) { 53 | executable.source = "function(GLSL, env) {\n" + this._lines.join("\n") + "}"; 54 | executable.error = e.message; 55 | } 56 | 57 | return executable; 58 | }, 59 | 60 | // Overrides for ASTVisitor 61 | 62 | visitorForType: function(type) 63 | { 64 | if (type in CodeGenerator.Callbacks) 65 | return CodeGenerator.Callbacks[type]; 66 | 67 | return ASTVisitor.prototype.visitorForType.call(this, type); 68 | }, 69 | 70 | // Private 71 | 72 | _resolveGetLocal: function(name) 73 | { 74 | console.assert(typeof name === "string", name); 75 | 76 | return this._resolveGetName(name); 77 | }, 78 | 79 | _resolveFunction: function(name) 80 | { 81 | console.assert(typeof name === "string", name); 82 | 83 | if (name in Builtins) 84 | return "RT." + name; 85 | 86 | if (name in Operations) 87 | return "RT." + name; 88 | 89 | if (name in CodeGenerator.ConstructorMap) 90 | return "RT." + CodeGenerator.ConstructorMap[name]; 91 | 92 | return this._resolveGetName(name); 93 | }, 94 | 95 | _resolveGetName: function(name) 96 | { 97 | console.assert(typeof name === "string", name); 98 | 99 | if (this._globalVariableNames.has(name)) 100 | return "env.get('" + name + "')"; 101 | 102 | return "$" + name; 103 | }, 104 | 105 | _resolveSetName: function(name, valueExpr) 106 | { 107 | console.assert(typeof name === "string", name); 108 | 109 | if (this._globalVariableNames.has(name)) 110 | return "env.set('" + name + "', " + valueExpr + ")"; 111 | 112 | return "$" + name + " = " + valueExpr; 113 | }, 114 | 115 | _addLine: function(line) 116 | { 117 | this._lines.push([this._currentIndent, line].join("")); 118 | }, 119 | 120 | _increaseIndent: function() 121 | { 122 | var oldIndent = this._currentIndent; 123 | this._currentIndent = [this._currentIndent, CodeGenerator.IndentString].join(""); 124 | return oldIndent; 125 | }, 126 | }; 127 | 128 | CodeGenerator.IndentString = " "; 129 | 130 | CodeGenerator.Callbacks = {}; 131 | 132 | CodeGenerator.Callbacks[ASTNode.Types.Program] = function(node) 133 | { 134 | // {statements: [Node.$statement]*} 135 | this.visitList(node.statements); 136 | } 137 | 138 | CodeGenerator.Callbacks[ASTNode.Types.Preprocessor] = function(node) 139 | { 140 | // {directive: string, identifier: string?, parameters: [string]?, value: string?, guarded_statements: [Node.$statement]*} 141 | } 142 | 143 | CodeGenerator.Callbacks[ASTNode.Types.MacroCall] = function(node) 144 | { 145 | // {macro_name: string, parameters: [string]+} 146 | } 147 | 148 | CodeGenerator.Callbacks[ASTNode.Types.FunctionCall] = function(node) 149 | { 150 | // {function_name: string, parameters: [Node.$expression]*} 151 | var params = this.visitList(node.parameters) || []; 152 | return this._resolveFunction(node.function_name) + "(" + params.join(", ") + ")"; 153 | } 154 | 155 | CodeGenerator.Callbacks[ASTNode.Types.FunctionPrototype] = function(node) 156 | { 157 | // {name: string, returnType: Node.Type, parameters: [Node.Parameter]*} 158 | } 159 | 160 | CodeGenerator.Callbacks[ASTNode.Types.FunctionDeclaration] = function(node) 161 | { 162 | // {name: string, returnType: Node.Type, parameters: [Node.Parameter]*, body: Node.Scope} 163 | 164 | var paramNames = this.visitList(node.parameters) || []; 165 | paramNames = paramNames.map(function(p) { return this._resolveGetLocal(p); }, this); 166 | 167 | // TODO: emit code that asserts argument types for debugging purposes. 168 | // This is unnecessary if the shader is statically typechecked (and there are no bugs in the checker). 169 | 170 | this._withinFunctionScope = node; 171 | this._addLine("var " + this._resolveGetLocal(node.name) + " = function(" + paramNames.join(", ") + ") {"); 172 | var oldIndent = this._increaseIndent(); 173 | this.visitNode(node.body); 174 | this._currentIndent = oldIndent; 175 | this._addLine("};") 176 | delete this._withinFunctionScope; 177 | } 178 | 179 | CodeGenerator.Callbacks[ASTNode.Types.Scope] = function(node) 180 | { 181 | // {statements: [Node.$statement]*} 182 | this.visitList(node.statements); 183 | } 184 | 185 | CodeGenerator.Callbacks[ASTNode.Types.IfStatement] = function(node) 186 | { 187 | // {condition: Node.$expression, body: Node.Scope, elseBody: Node.Scope?} 188 | var expr = this.visitNode(node.condition); 189 | this._addLine("if (" + expr + ") {"); 190 | var oldIndent = this._increaseIndent(); 191 | this.visitNode(node.body); 192 | this._currentIndent = oldIndent; 193 | 194 | if (node.elseBody) { 195 | this._addLine("} else {"); 196 | var oldIndent = this._increaseIndent(); 197 | this.visitNode(node.elseBody); 198 | this._currentIndent = oldIndent; 199 | } 200 | 201 | this._addLine("}"); 202 | } 203 | 204 | CodeGenerator.Callbacks[ASTNode.Types.ForStatement] = function(node) 205 | { 206 | // {initializer: Node.$initializer, condition: Node.$expression, increment: Node.$expression, body: Node.Scope}} 207 | 208 | // We emit for-loops as while loops because they have a simpler grammar. In particular, 209 | // declarators are treated as statements elsewhere, so we can emit it to its own line. 210 | this.visitNode(node.initializer); 211 | 212 | var condition = this.visitNode(node.condition) || "true"; 213 | var increment = this.visitNode(node.increment); 214 | this._addLine("while (" + condition + ") {"); 215 | var oldIndent = this._increaseIndent(); 216 | this.visitNode(node.body); 217 | if (increment) 218 | this._addLine(increment + ";"); 219 | this._currentIndent = oldIndent; 220 | this._addLine("}"); 221 | } 222 | 223 | CodeGenerator.Callbacks[ASTNode.Types.WhileStatement] = function(node) 224 | { 225 | // {condition: Node.$expression, body: Node.Scope} 226 | var expr = this.visitNode(node.condition); 227 | this._addLine("while (" + expr + ") {"); 228 | var oldIndent = this._increaseIndent(); 229 | this.visitNode(node.body); 230 | this._currentIndent = oldIndent; 231 | this._addLine("}") 232 | } 233 | 234 | CodeGenerator.Callbacks[ASTNode.Types.DoStatement] = function(node) 235 | { 236 | // {condition: Node.$expression, body: Node.Scope} 237 | var expr = this.visitNode(node.condition); 238 | this._addLine("do {"); 239 | var oldIndent = this._increaseIndent(); 240 | this.visitNode(node.body); 241 | this._currentIndent = oldIndent; 242 | this._addLine("} while (" + expr + ");"); 243 | } 244 | 245 | CodeGenerator.Callbacks[ASTNode.Types.ReturnStatement] = function(node) 246 | { 247 | // {value: Node.$expression} 248 | if (node.value) 249 | this._addLine("return " + this.visitNode(node.value) + ";"); 250 | else 251 | this._addLine("return;"); 252 | } 253 | 254 | CodeGenerator.Callbacks[ASTNode.Types.ContinueStatement] = function(node) 255 | { 256 | // {} 257 | this._addLine("continue;"); 258 | } 259 | 260 | CodeGenerator.Callbacks[ASTNode.Types.BreakStatement] = function(node) 261 | { 262 | // {} 263 | this._addLine("break;"); 264 | } 265 | 266 | CodeGenerator.Callbacks[ASTNode.Types.DiscardStatement] = function(node) 267 | { 268 | // {} 269 | this._addLine("discard;"); 270 | } 271 | 272 | CodeGenerator.Callbacks[ASTNode.Types.ExpressionStatement] = function(node) 273 | { 274 | // {expression: Node.$expression?} 275 | this._addLine(this.visitNode(node.expression) + ";"); 276 | } 277 | 278 | CodeGenerator.Callbacks[ASTNode.Types.Declarator] = function(node) 279 | { 280 | // {typeAttribute: Node.Type, declarators: [Node.DeclaratorItem]+} 281 | 282 | // If outside a function, then this is a uniform, attribute, or varying and passed through env. 283 | if (!this._withinFunctionScope) 284 | return; 285 | 286 | // TODO: register type information here to ensure assignments are type-compatible 287 | this.visitList(node.declarators); 288 | } 289 | 290 | CodeGenerator.Callbacks[ASTNode.Types.DeclaratorItem] = function(node) 291 | { 292 | // {name: Node.Identifier, initializer: Node.$expression} 293 | var name = this.visitNode(node.name); 294 | if (node.initializer) 295 | this._addLine("var " + name + " = " + this.visitNode(node.initializer) + ";"); 296 | else 297 | this._addLine("var " + name + ";"); 298 | } 299 | 300 | CodeGenerator.Callbacks[ASTNode.Types.Invariant] = function(node) 301 | { 302 | // {identifiers: [Node.Identifier]*} 303 | } 304 | 305 | CodeGenerator.Callbacks[ASTNode.Types.Precision] = function(node) 306 | { 307 | // {precision: string, typeName: string} 308 | } 309 | 310 | CodeGenerator.Callbacks[ASTNode.Types.Parameter] = function(node) 311 | { 312 | // {type_name: string, name: string, typeQualifier: string?, parameterQualifier: string?, precision: string?, arraySize: Node.$expression} 313 | return node.name; 314 | } 315 | 316 | CodeGenerator.Callbacks[ASTNode.Types.StructDefinition] = function(node) 317 | { 318 | // {qualifier: string?, name: string?, members: [Node.Declarator]+, declarators: [Node.Declarator]?} 319 | } 320 | 321 | CodeGenerator.Callbacks[ASTNode.Types.Type] = function(node) 322 | { 323 | // {name: string, precision: string?, qualifier: string?} 324 | } 325 | 326 | CodeGenerator.Callbacks[ASTNode.Types.IntegerLiteral] = function(node) 327 | { 328 | // {value: number} 329 | return Number(node.value); 330 | } 331 | 332 | CodeGenerator.Callbacks[ASTNode.Types.FloatLiteral] = function(node) 333 | { 334 | // {value: number} 335 | return Number(node.value); 336 | } 337 | 338 | CodeGenerator.Callbacks[ASTNode.Types.BooleanLiteral] = function(node) 339 | { 340 | // {value: boolean} 341 | return node.value ? "true" : "false"; 342 | } 343 | 344 | CodeGenerator.Callbacks[ASTNode.Types.Identifier] = function(node) 345 | { 346 | // {name: string} 347 | return this._resolveGetName(node.name); 348 | } 349 | 350 | CodeGenerator.Callbacks[ASTNode.Types.Operator] = function(node) 351 | { 352 | // {operator: string} 353 | return node.operator; 354 | } 355 | 356 | CodeGenerator.Callbacks[ASTNode.Types.PostfixExpression] = function(node) 357 | { 358 | // {operator: Node.Operator, expression: Node.$expression} 359 | var op = this.visitNode(node.operator); 360 | var expr = this.visitNode(node.expression); 361 | var builder = new SelectorBuilder(node); 362 | var params = builder.params.slice(); 363 | 364 | // FIXME: we assume op is a field or index selector and expr is not an lvalue. 365 | // This will fall apart if code does something like |max(color.x++, color.x++)| 366 | 367 | var func = null; 368 | switch (op) { 369 | case '++': func = "op_add"; break; 370 | case '--': func = "op_sub"; break; 371 | default: 372 | params.unshift(this._resolveGetName(builder.operand)); 373 | return "RT.get(" + params.join(", ") + ")"; 374 | } 375 | 376 | var result = (func) ? (this._resolveFunction(func) + "(" + expr + ", 1.0)") : "/* " + op + " */" + expr; 377 | 378 | var params = builder.params.slice(); 379 | params.unshift(this._resolveGetLocal(builder.operand)); 380 | params.push(result); 381 | 382 | // FIXME: the generated code here returns the value of ++expr, not expr++. We 383 | // need to copy the pre-assignment value into a temporary variable and return that. 384 | if (builder.params.length) 385 | return this._resolveSetName(builder.operand, "RT.set(" + params.join(", ") + ")"); 386 | else 387 | return this._resolveSetName(builder.operand, result); 388 | } 389 | 390 | CodeGenerator.Callbacks[ASTNode.Types.UnaryExpression] = function(node) 391 | { 392 | // {operator: Node.Operator, expression: Node.$expression} 393 | var op = this.visitNode(node.operator); 394 | var expr = this.visitNode(node.expression); 395 | 396 | var func = null; 397 | switch (op) { 398 | case '+': func = "op_pos"; break; 399 | case '-': func = "op_neg"; break; 400 | case '~': func = "op_bnot"; break; 401 | case '!': func = "op_lnot"; break; 402 | default: 403 | return "/* " + op + " */" + expr; 404 | } 405 | 406 | return (func) ? (this._resolveFunction(func) + "(" + expr + ")") : "/* " + op + " */" + expr; 407 | } 408 | 409 | CodeGenerator.Callbacks[ASTNode.Types.BinaryExpression] = function(node) 410 | { 411 | // {operator: Node.Operator, left: Node.$expression, right:}Node.$expression} 412 | var op = this.visitNode(node.operator); 413 | var left = this.visitNode(node.left); 414 | var right = this.visitNode(node.right); 415 | 416 | var func = null; 417 | var do_assign = false; 418 | 419 | switch (op) { 420 | case '==': func = "op_eq"; break; 421 | case '!=': func = "op_neq"; break; 422 | case '*': func = "op_mul"; break; 423 | case '/': func = "op_div"; break; 424 | case '%': func = "op_mod"; break; 425 | case '+': func = "op_add"; break; 426 | case '-': func = "op_sub"; break; 427 | case '<<': func = "op_shl"; break; 428 | case '>>': func = "op_shr"; break; 429 | case '<': func = "op_lt"; break; 430 | case '>': func = "op_gt"; break; 431 | case '<=': func = "op_le"; break; 432 | case '>=': func = "op_ge"; break; 433 | case '&': func = "op_band"; break; 434 | case '^': func = "op_bxor"; break; 435 | case '|': func = "op_bor"; break; 436 | case '&&': func = "op_land"; break; 437 | case '^^': func = "op_lxor"; break; 438 | case '||': func = "op_lor"; break; 439 | 440 | /* TODO: we need a strategy for extracting and assigning to l-values. 441 | Current idea: hard-code l-vlaue cases and synthesize the correct setter. 442 | Cases are listed in section 5.8 of the specification (reproduced here): 443 | 444 | - variables of builtin type 445 | - entire structures or arrays 446 | - single fields of structs 447 | - arrays dereferenced with the subscript operator '[]' 448 | - components or swizzles chosen with the field selector '.'' 449 | 450 | Note that subscript and swizzle can be chained up to two times, in the case 451 | of code like |m[1].yxz = vec3(1,0,0);| 452 | */ 453 | case '=': do_assign = true; break; 454 | case '+=': func = "op_add"; do_assign = true; break; 455 | case '-=': func = "op_sub"; do_assign = true; break; 456 | case '*=': func = "op_mul"; do_assign = true; break; 457 | case '/=': func = "op_div"; do_assign = true; break; 458 | case '%=': func = "op_mod"; do_assign = true; break; 459 | case '<<=': func = "op_shl"; do_assign = true; break; 460 | case '>>=': func = "op_shr"; do_assign = true; break; 461 | case '&=': func = "op_band"; do_assign = true; break; 462 | case '^=': func = "op_bxor"; do_assign = true; break; 463 | case "|=": func = "op_bor"; do_assign = true; break; 464 | default: 465 | return left + " /* " + op + " */ " + right; 466 | } 467 | 468 | var result = (func) ? (this._resolveFunction(func) + "(" + left + ", " + right + ")") : right; 469 | // TODO: assert that LHS is an lvalue, or figure this out in typechecking 470 | if (do_assign) { 471 | var builder = new SelectorBuilder(node.left); 472 | var params = builder.params.slice(); 473 | params.unshift(this._resolveGetLocal(builder.operand)); 474 | params.push(result); 475 | if (builder.params.length) 476 | return this._resolveSetName(builder.operand, "RT.set(" + params.join(", ") + ")"); 477 | else 478 | return this._resolveSetName(builder.operand, result); 479 | } 480 | else 481 | return result; 482 | 483 | } 484 | 485 | CodeGenerator.Callbacks[ASTNode.Types.TernaryExpression] = function(node) 486 | { 487 | // {condition: Node.$expression, is_true: Node.$expression, is_false: Node.$expression} 488 | return "(" + this.visitNode(node.condition) + ") " + this.visitNode(node.is_true) + " : " + this.visitNode(node.is_false); 489 | } 490 | 491 | CodeGenerator.Callbacks[ASTNode.Types.IndexSelector] = function(node) 492 | { 493 | // {index: Node.$expression} 494 | } 495 | 496 | CodeGenerator.Callbacks[ASTNode.Types.FieldSelector] = function(node) 497 | { 498 | // {selection: string} 499 | } 500 | 501 | SelectorBuilder = function(expr) { 502 | ASTVisitor.call(this); 503 | 504 | this.operand = null; 505 | this.params = []; 506 | this.visitNode(expr); 507 | } 508 | 509 | SelectorBuilder.prototype = { 510 | constructor: SelectorBuilder, 511 | __proto__: ASTVisitor.prototype, 512 | 513 | // Overrides for ASTVisitor 514 | 515 | visitorForType: function(type) 516 | { 517 | if (type in SelectorBuilder.Callbacks) 518 | return SelectorBuilder.Callbacks[type]; 519 | 520 | throw new Error("Unexpected AST node encountered by selector builder."); 521 | }, 522 | } 523 | 524 | SelectorBuilder.Callbacks = {}; 525 | 526 | SelectorBuilder.Callbacks[ASTNode.Types.IntegerLiteral] = function(node) 527 | { 528 | return this.params.push(node.value); 529 | } 530 | 531 | SelectorBuilder.Callbacks[ASTNode.Types.FloatLiteral] = function(node) 532 | { 533 | return this.params.push(node.value); 534 | } 535 | 536 | SelectorBuilder.Callbacks[ASTNode.Types.Identifier] = function(node) 537 | { 538 | return this.operand = node.name; 539 | } 540 | 541 | SelectorBuilder.Callbacks[ASTNode.Types.Operator] = function(node) 542 | { 543 | if (node.operator instanceof ASTNode) 544 | this.visitNode(node.operator); 545 | } 546 | 547 | SelectorBuilder.Callbacks[ASTNode.Types.PostfixExpression] = function(node) 548 | { 549 | // {operator: Node.Operator, expression: Node.$expression} 550 | this.visitNode(node.expression); 551 | this.visitNode(node.operator); 552 | } 553 | 554 | SelectorBuilder.Callbacks[ASTNode.Types.IndexSelector] = function(node) 555 | { 556 | // {index: Node.$expression} 557 | this.visitNode(node.index); 558 | } 559 | 560 | SelectorBuilder.Callbacks[ASTNode.Types.FieldSelector] = function(node) 561 | { 562 | // {selection: string} 563 | this.params.push("'" + node.selection + "'"); 564 | } 565 | 566 | 567 | module.exports = CodeGenerator; 568 | -------------------------------------------------------------------------------- /lib/compiler/pretty.js: -------------------------------------------------------------------------------- 1 | var ASTVisitor = require("./visitor"); 2 | 3 | PrettyPrinter = function() 4 | { 5 | ASTVisitor.call(this); 6 | 7 | this._scopes = []; 8 | this._currentIndent = ""; 9 | } 10 | 11 | PrettyPrinter.prototype = { 12 | constructor: PrettyPrinter, 13 | __proto__: ASTVisitor.prototype, 14 | 15 | // Public 16 | 17 | formattedText: function(tree) 18 | { 19 | this._lines = []; 20 | this.visitNode(tree); 21 | return this._lines.join("\n"); 22 | }, 23 | 24 | // Overrides for ASTVisitor 25 | 26 | visitorForType: function(type) 27 | { 28 | if (type in PrettyPrinter.Callbacks) 29 | return PrettyPrinter.Callbacks[type]; 30 | 31 | return ASTVisitor.prototype.visitorForType(type); 32 | }, 33 | 34 | // Private 35 | 36 | _addLine: function(line) 37 | { 38 | this._lines.push([this._currentIndent, line].join("")); 39 | }, 40 | 41 | _increaseIndent: function() 42 | { 43 | var oldIndent = this._currentIndent; 44 | this._currentIndent = [this._currentIndent, PrettyPrinter.IndentString].join(""); 45 | return oldIndent; 46 | }, 47 | }; 48 | 49 | PrettyPrinter.IndentString = " "; 50 | 51 | PrettyPrinter.Callbacks = {}; 52 | 53 | PrettyPrinter.Callbacks[ASTNode.Types.Identifier] = function(node) 54 | { 55 | return node.name; 56 | } 57 | 58 | PrettyPrinter.Callbacks[ASTNode.Types.Program] = function(node) 59 | { 60 | this.visitList(node.statements); 61 | } 62 | 63 | PrettyPrinter.Callbacks[ASTNode.Types.Preprocessor] = function(node) 64 | { 65 | if (node.directive === "#define") { 66 | var pieces = [node.directive, " ", node.identifier]; 67 | 68 | if (node.parameters) 69 | pieces.push("(" + node.parameters.join(", ") +")"); 70 | 71 | if (node.token_string) { 72 | pieces.push(" "); 73 | pieces.push(node.token_string); 74 | } 75 | 76 | this._addLine(pieces.join("")); 77 | } else { 78 | // Deduplicate any trailing #endif. We always add #endif, since we 79 | // don't have matched preprocessor directive in the AST itself. 80 | var shouldPairWithPrevious = node.directive === "#elif" || node.directive === "#else"; 81 | if (shouldPairWithPrevious && this._lines[this._lines.length - 1] === "#endif") 82 | this._lines.pop(); 83 | 84 | this._addLine(node.directive + " " + node.value); 85 | this.visitList(node.guarded_statements); 86 | this._addLine("#endif"); 87 | } 88 | } 89 | 90 | PrettyPrinter.Callbacks[ASTNode.Types.MacroCall] = function(node) 91 | { 92 | return node.macro_name + "(" + node.paremeters.join(", ") + ")"; 93 | } 94 | 95 | PrettyPrinter.Callbacks[ASTNode.Types.FunctionCall] = function(node) 96 | { 97 | var argList = this.visitList(node.parameters) || []; 98 | return node.function_name + "(" + argList.join(", ") + ")"; 99 | } 100 | 101 | PrettyPrinter.Callbacks[ASTNode.Types.FunctionDeclaration] = function(node) 102 | { 103 | var returnType = this.visitNode(node.returnType); 104 | var argList = this.visitList(node.parameters) || ["void"]; 105 | 106 | this._addLine(""); // Induce a newline before function declaration. 107 | this._addLine(returnType + " " + node.name + "(" + argList.join(", ") + ") {"); 108 | var oldIndent = this._increaseIndent(); 109 | this.visitNode(node.body); 110 | this._currentIndent = oldIndent; 111 | this._addLine("}"); 112 | } 113 | 114 | PrettyPrinter.Callbacks[ASTNode.Types.FunctionPrototype] = function(node) 115 | { 116 | var returnType = this.visitNode(node.returnType); 117 | var argList = this.visitList(node.parameters) || ["void"]; 118 | 119 | this._addLine(""); // Induce a newline before function declaration. 120 | this._addLine(returnType + " " + node.name + "(" + argList.join(", ") + ");"); 121 | } 122 | 123 | PrettyPrinter.Callbacks[ASTNode.Types.Scope] = function(node) 124 | { 125 | this._scopes.push(node); 126 | this.visitList(node.statements); 127 | this._scopes.pop(node); 128 | } 129 | 130 | PrettyPrinter.Callbacks[ASTNode.Types.IfStatement] = function(node) 131 | { 132 | this._addLine("if (" + this.visitNode(node.condition) + ") {"); 133 | var oldIndent = this._increaseIndent(); 134 | this.visitNode(node.body); 135 | this._currentIndent = oldIndent; 136 | 137 | if (node.elseBody) { 138 | this._addLine("} else {") 139 | var oldIndent = this._increaseIndent(); 140 | this.visitNode(node.elseBody); 141 | this._currentIndent = oldIndent; 142 | } 143 | 144 | this._addLine("}"); 145 | } 146 | 147 | PrettyPrinter.Callbacks[ASTNode.Types.ForStatement] = function(node) 148 | { 149 | // The declarator node is normally a statement on its own line. 150 | // So, pop it off the end if it exists. 151 | var initializer = ""; 152 | if (node.initializer) { 153 | this.visitNode(node.initializer); 154 | initializer = this._lines.pop().trim(); 155 | initializer = initializer.substr(0, initializer.length - 1); 156 | } 157 | 158 | var condition = this.visitNode(node.condition) || ""; 159 | var increment = this.visitNode(node.increment) || ""; 160 | 161 | this._addLine("for (" + [initializer, condition, increment].join("; ") + ") {"); 162 | var oldIndent = this._increaseIndent(); 163 | this.visitNode(node.body); 164 | this._currentIndent = oldIndent; 165 | this._addLine("}"); 166 | } 167 | 168 | PrettyPrinter.Callbacks[ASTNode.Types.WhileStatement] = function(node) 169 | { 170 | this._addLine("while (" + this.visitNode(node.condition) + ") {"); 171 | var oldIndent = this._increaseIndent(); 172 | this.visitNode(node.body); 173 | this._currentIndent = oldIndent; 174 | this._addLine("}"); 175 | } 176 | 177 | PrettyPrinter.Callbacks[ASTNode.Types.DoStatement] = function(node) 178 | { 179 | this._addLine("do {"); 180 | var oldIndent = this._increaseIndent(); 181 | this.visitNode(node.body); 182 | this._currentIndent = oldIndent; 183 | this._addLine("} while (" + this.visitNode(node.condition) + ");"); 184 | } 185 | 186 | PrettyPrinter.Callbacks[ASTNode.Types.ReturnStatement] = function(node) 187 | { 188 | if (node.value) 189 | this._addLine("return " + this.visitNode(node.value) + ";"); 190 | else 191 | this._addLine("return;"); 192 | } 193 | 194 | PrettyPrinter.Callbacks[ASTNode.Types.ContinueStatement] = function(node) 195 | { 196 | this._addLine("continue;"); 197 | } 198 | 199 | PrettyPrinter.Callbacks[ASTNode.Types.BreakStatement] = function(node) 200 | { 201 | this._addLine("break;"); 202 | } 203 | 204 | PrettyPrinter.Callbacks[ASTNode.Types.DiscardStatement] = function(node) 205 | { 206 | this._addLine("discard;"); 207 | } 208 | 209 | PrettyPrinter.Callbacks[ASTNode.Types.ExpressionStatement] = function(node) 210 | { 211 | this._addLine(this.visitNode(node.expression) + ";"); 212 | } 213 | 214 | PrettyPrinter.Callbacks[ASTNode.Types.Declarator] = function(node) 215 | { 216 | var type = this.visitNode(node.typeAttribute); 217 | var items = this.visitList(node.declarators); 218 | 219 | this._addLine(type + " " + items.join(", ") + ";"); 220 | } 221 | 222 | PrettyPrinter.Callbacks[ASTNode.Types.DeclaratorItem] = function(node) 223 | { 224 | var tokens = [this.visitNode(node.name)]; 225 | if (node.initializer) { 226 | tokens.push("="); 227 | tokens.push(this.visitNode(node.initializer)); 228 | } 229 | 230 | return tokens.join(" "); 231 | } 232 | 233 | PrettyPrinter.Callbacks[ASTNode.Types.Invariant] = function(node) 234 | { 235 | this._addLine("invariant " + this.visitList(node.identifiers).join(", ") + ";"); 236 | } 237 | 238 | PrettyPrinter.Callbacks[ASTNode.Types.Precision] = function(node) 239 | { 240 | return this._addLine(["precision", node.precision, node.typeName].join(" ") + ";"); 241 | } 242 | 243 | PrettyPrinter.Callbacks[ASTNode.Types.Parameter] = function(node) 244 | { 245 | var tokens = [node.type_name, node.name]; 246 | 247 | if (node.precision) 248 | tokens.unshift(node.precision); 249 | if (node.parameterQualifier) 250 | tokens.unshift(node.parameterQualifier); 251 | if (node.typeQualifier) 252 | tokens.unshift(node.typeQualifier); 253 | 254 | var result = tokens.join(" "); 255 | if (node.arraySize) 256 | result = result + "[" + this.visit(node.arraySize) + "]"; 257 | 258 | return result; 259 | } 260 | 261 | PrettyPrinter.Callbacks[ASTNode.Types.StructDefinition] = function(node) 262 | { 263 | var tokens = ["struct"]; 264 | if (node.qualifier) 265 | tokens.unshift(node.qualifier); 266 | 267 | if (node.name) 268 | tokens.push(node.name); 269 | 270 | tokens.push("{") 271 | this._addLine(tokens.join(" ")); 272 | var oldIndent = this._increaseIndent(); 273 | this.visitList(node.members); 274 | this._currentIndent = oldIndent; 275 | 276 | if (!node.declarators) { 277 | this._addLine("};"); 278 | return; 279 | } 280 | 281 | var declarators = this.visitList(node.declarators); 282 | this._addLine("} " + declarators.join(", ") + ";"); 283 | } 284 | 285 | PrettyPrinter.Callbacks[ASTNode.Types.Type] = function(node) 286 | { 287 | var tokens = [node.name]; 288 | 289 | if (node.precision) 290 | tokens.unshift(node.precision); 291 | if (node.qualifier) 292 | tokens.unshift(node.qualifier); 293 | 294 | return tokens.join(" "); 295 | } 296 | 297 | PrettyPrinter.Callbacks[ASTNode.Types.IntegerLiteral] = function(node) 298 | { 299 | return node.value; 300 | } 301 | 302 | PrettyPrinter.Callbacks[ASTNode.Types.FloatLiteral] = function(node) 303 | { 304 | return node.value; 305 | } 306 | 307 | PrettyPrinter.Callbacks[ASTNode.Types.BooleanLiteral] = function(node) 308 | { 309 | return node.value; 310 | } 311 | 312 | PrettyPrinter.Callbacks[ASTNode.Types.Operator] = function(node) 313 | { 314 | return node.operator; 315 | } 316 | 317 | PrettyPrinter.Callbacks[ASTNode.Types.PostfixExpression] = function(node) 318 | { 319 | return this.visitNode(node.expression) + this.visitNode(node.operator); 320 | } 321 | 322 | PrettyPrinter.Callbacks[ASTNode.Types.UnaryExpression] = function(node) 323 | { 324 | return this.visitNode(node.operator) + this.visitNode(node.expression); 325 | } 326 | 327 | PrettyPrinter.Callbacks[ASTNode.Types.BinaryExpression] = function(node) 328 | { 329 | var expr = [this.visitNode(node.left), this.visitNode(node.operator), this.visitNode(node.right)].join(" ") 330 | var op = node.operator.operator; 331 | if (op.indexOf("==") === -1 && op.indexOf("=") !== -1) 332 | return expr; 333 | else 334 | return "(" + expr + ")"; 335 | } 336 | 337 | PrettyPrinter.Callbacks[ASTNode.Types.TernaryExpression] = function(node) 338 | { 339 | return [this.visitNode(node.condition), "?", this.visitNode(node.is_true), ":", this.visitNode(node.is_false)].join(" "); 340 | } 341 | 342 | PrettyPrinter.Callbacks[ASTNode.Types.IndexSelector] = function(node) 343 | { 344 | return "[" + this.visitNode(node.index) + "]"; 345 | } 346 | 347 | PrettyPrinter.Callbacks[ASTNode.Types.FieldSelector] = function(node) 348 | { 349 | return "." + node.selection; 350 | } 351 | 352 | module.exports = PrettyPrinter; 353 | -------------------------------------------------------------------------------- /lib/compiler/program.js: -------------------------------------------------------------------------------- 1 | // This is the GLSL reference passed to translated shader programs. 2 | // It should only require access to the Runtime library. 3 | var GLSL_RT = {}; 4 | GLSL_RT.Runtime = require("../runtime"); 5 | 6 | var GLSL = {}; // Internal GLSL handle. 7 | GLSL.Shader = require("./shader"); 8 | GLSL.Typechecker = require("./typecheck"); 9 | GLSL.Error = require("../error"); 10 | GLSL.Object = require("../events").Object; 11 | GLSL.Builtins = require("../runtime/builtins"); 12 | GLSL.Environment = require("../runtime/environment"); 13 | 14 | 15 | GLSL.Program = function() { 16 | GLSL.Object.call(this); 17 | 18 | this.vertexShader = null; 19 | this.fragmentShader = null; 20 | 21 | this._linkerResult = null; 22 | this._programCode = null; 23 | } 24 | 25 | GLSL.Program.Event = { 26 | ShaderChanged: "program-shader-changed", 27 | Error: "program-error" 28 | }; 29 | 30 | GLSL.Program.prototype = { 31 | constructor: GLSL.Program, 32 | __proto__: GLSL.Object.prototype, 33 | 34 | // Public 35 | 36 | shaderWithType: function(type) 37 | { 38 | if (type === GLSL.Shader.Type.Vertex) 39 | return this.vertexShader; 40 | 41 | if (type === GLSL.Shader.Type.Fragment) 42 | return this.fragmentShader; 43 | 44 | console.error("Unknown shader type requested: ", type); 45 | }, 46 | 47 | // This will replace the existing vertex or fragment shader with a new shader. 48 | updateShaderWithType: function(shaderType, text) 49 | { 50 | this._linkerResult = false; 51 | this._programCode = null; 52 | 53 | try { 54 | var oldShader = this.shaderWithType(shaderType); 55 | var newShader = new GLSL.Shader(text, shaderType); 56 | } catch (e) { 57 | var errorType = null; 58 | if (shaderType === GLSL.Shader.Type.Vertex) 59 | errorType = GLSL.Error.Type.VertexShaderParsing; 60 | if (shaderType === GLSL.Shader.Type.Fragment) 61 | errorType = GLSL.Error.Type.FragmentShaderParsing; 62 | 63 | this.dispatchEventToListeners(GLSL.Program.Event.Error, {type: errorType, message: e.message}); 64 | return; 65 | } 66 | if (shaderType === GLSL.Shader.Type.Fragment) 67 | this.fragmentShader = newShader; 68 | else if (shaderType === GLSL.Shader.Type.Vertex) 69 | this.vertexShader = newShader; 70 | 71 | this.dispatchEventToListeners(GLSL.Program.Event.ShaderChanged, {oldShader: oldShader, newShader: newShader}); 72 | }, 73 | 74 | // Run the combined shader program (vertex shader + rasterization + fragment shader). 75 | renderToBuffer: function(env, buffer) 76 | { 77 | if (!this.vertexShader) 78 | throw new Error("Couldn't run shader program: no vertex shader specified."); 79 | 80 | if (!this.vertexShader.typecheck()) 81 | throw new Error("Couldn't run shader program: typechecking failed for vertex shader program."); 82 | 83 | if (!this.fragmentShader) 84 | throw new Error("Couldn't run shader program: no fragment shader specified."); 85 | 86 | if (!this.fragmentShader.typecheck()) 87 | throw new Error("Couldn't run shader program: typechecking failed for fragment shader program."); 88 | 89 | if (!this._linkerResult) 90 | this._linkerResult = this._linkShaders(); 91 | 92 | if (!this._linkerResult) 93 | throw new Error("Couldn't run shader program: linking failed."); 94 | 95 | if (!env.validateForShader(this.vertexShader)) 96 | throw new Error("Couldn't run shader program: vertex shader environment validation failed."); 97 | 98 | if (!env.validateForShader(this.fragmentShader)) 99 | throw new Error("Couldn't run shader program: vertex shader environment validation failed."); 100 | 101 | // TODO: these should be glued together with a "program" executable, which 102 | // handles rasterization etc. 103 | // TODO: Maybe we want a different entry point to run only one shader, or provide a dummy. 104 | 105 | try { 106 | env.enterRunScope(); 107 | for (var y = 0; y < buffer.height; ++y) { 108 | env.get('gl_FragCoord').set('y', y); 109 | for (var x = 0; x < buffer.width; ++x) { 110 | env.get('gl_FragCoord').set('x', x); 111 | this.fragmentShader.executable.code.call(null, GLSL_RT, env); 112 | var color = env.get('gl_FragColor'); 113 | color = GLSL.Builtins.clamp(color, 0.0, 1.0); 114 | buffer.data[(buffer.width * y + x) * 4 + 0] = color.d[0] * 255.0; 115 | buffer.data[(buffer.width * y + x) * 4 + 1] = color.d[1] * 255.0; 116 | buffer.data[(buffer.width * y + x) * 4 + 2] = color.d[2] * 255.0; 117 | buffer.data[(buffer.width * y + x) * 4 + 3] = color.d[3] * 255.0; 118 | } 119 | } 120 | env.exitRunScope(); 121 | } catch (e) { 122 | env.exitRunScope(); 123 | } 124 | }, 125 | 126 | runShaderWithType: function(shaderType, env) 127 | { 128 | var shader = this.shaderWithType(shaderType); 129 | if (!shader) 130 | return; 131 | 132 | this._runSingleShader(shader, env); 133 | }, 134 | 135 | // Run both vertex and fragment shaders in isolation, if they exist. 136 | // Do not connect them with interpolated varying variables. 137 | runShaders: function(env) 138 | { 139 | if (!this.vertexShader && !this.fragmentShader) 140 | return; 141 | 142 | // TODO: this doesn't actually enforce isolation, it just makes it more likely. 143 | // We assume that fragment shader will try to read the output of the vertex shader. 144 | if (this.fragmentShader) 145 | this._runSingleShader(this.fragmentShader, env); 146 | 147 | if (this.vertexShader) 148 | this._runSingleShader(this.vertexShader, env); 149 | }, 150 | 151 | // Private 152 | 153 | _runSingleShader: function(shader, env) 154 | { 155 | console.assert(shader instanceof GLSL.Shader, shader); 156 | console.assert(env instanceof GLSL.Environment, env); 157 | 158 | if (!shader) 159 | throw new Error("Couldn't run single shader: the shader was empty."); 160 | 161 | if (!shader.typecheck()) 162 | throw new Error("Couldn't run single shader: typechecking failed."); 163 | 164 | if (!env.validateForShader(shader)) 165 | throw new Error("Couldn't run single shader: environment validation failed."); 166 | 167 | if (shader.executable.error) { 168 | var errorType = (shader.type === GLSL.Shader.Type.Vertex) ? GLSL.Error.Type.VertexShaderTranslation : GLSL.Error.Type.FragmentShaderTranslation; 169 | var data = {type: errorType, message: shader.executable.error}; 170 | this.dispatchEventToListeners(GLSL.Program.Event.Error, data); 171 | return; 172 | } 173 | 174 | env.enterRunScope(); 175 | 176 | try { 177 | shader.executable.code.call(null, GLSL_RT, env); 178 | } catch (e) { 179 | var errorType = (shader.type === GLSL.Shader.Type.Vertex) ? GLSL.Error.Type.VertexShaderExecution : GLSL.Error.Type.FragmentShaderExecution; 180 | env.exitRunScope(); 181 | this.dispatchEventToListeners(GLSL.Program.Event.Error, {type: errorType, message: e.message}); 182 | return; 183 | } 184 | 185 | env.exitRunScope(); 186 | }, 187 | 188 | _linkShaders: function() 189 | { 190 | // TODO: check that inputs and outputs match between vertex and fragment shader 191 | 192 | return true; 193 | } 194 | }; 195 | 196 | module.exports = GLSL.Program; 197 | -------------------------------------------------------------------------------- /lib/compiler/shader.js: -------------------------------------------------------------------------------- 1 | var GLSL = {}; 2 | GLSL.Error = require("../error"); 3 | GLSL.Object = require("../events").Object; 4 | GLSL.ASTNode = require("./ast"); 5 | GLSL.ASTVisitor = require("./visitor"); 6 | GLSL.Parser = require("./parser"); 7 | GLSL.Typechecker = require("./typecheck"); 8 | GLSL.CodeGenerator = require("./codegen"); 9 | 10 | GLSL.Shader = function(text, type) 11 | { 12 | GLSL.Object.call(this); 13 | 14 | this.sourceText = text; 15 | try { 16 | this.ast = GLSL.Parser.parse(text); 17 | } catch (e) { 18 | if (type === GLSL.Shader.Type.Vertex) 19 | e.type = GLSL.Error.Type.VertexShaderParsing; 20 | if (type === GLSL.Shader.Type.Fragment) 21 | e.type = GLSL.Error.Type.FragmentShaderParsing; 22 | 23 | throw e; 24 | } 25 | 26 | this.type = type; 27 | 28 | this._typecheckResult = null; 29 | this._executable = null; 30 | 31 | this._shouldEmitDebuggerStatement = false; 32 | 33 | console.assert(type === GLSL.Shader.Type.Vertex || type === GLSL.Shader.Type.Fragment); 34 | 35 | this._uniforms = []; 36 | this._varyings = []; 37 | this._attributes = []; 38 | 39 | this._errors = []; 40 | 41 | this._extractVariables(); 42 | }; 43 | 44 | GLSL.Object.addConstructorFunctions(GLSL.Shader); 45 | 46 | GLSL.Shader.Event = { 47 | ExecutableChanged: "shader-executable-changed" 48 | }; 49 | 50 | GLSL.Shader.Type = { 51 | Vertex: "shader-type-vertex", 52 | Fragment: "shader-type-fragment" 53 | }; 54 | 55 | GLSL.Shader.prototype = { 56 | constructor: GLSL.Shader, 57 | __proto__: GLSL.Object.prototype, 58 | 59 | // Public 60 | 61 | get uniforms() { 62 | return this._uniforms.slice(); 63 | }, 64 | 65 | get attributes() { 66 | return this._attributes.slice(); 67 | }, 68 | 69 | get varyings() { 70 | return this._varyings.slice(); 71 | }, 72 | 73 | get executable() { 74 | if (!this._executable) 75 | this._executable = new GLSL.CodeGenerator(this).translateShader(); 76 | 77 | return this._executable; 78 | }, 79 | 80 | get shouldEmitDebuggerStatement() 81 | { 82 | return this._shouldEmitDebuggerStatement; 83 | }, 84 | 85 | set shouldEmitDebuggerStatement(value) 86 | { 87 | if (this._shouldEmitDebuggerStatement === value) 88 | return; 89 | 90 | this._shouldEmitDebuggerStatement = value; 91 | this._clearExecutable(); 92 | }, 93 | 94 | typecheck: function() 95 | { 96 | if (this._typecheckResult !== null) 97 | return this._typecheckResult; 98 | 99 | this._typecheckResult = new GLSL.Typechecker(this).typecheck(); 100 | return this._typecheckResult; 101 | }, 102 | 103 | // Private 104 | 105 | _extractVariables: function() 106 | { 107 | var extractor = new VariableExtractor(); 108 | extractor.extract(this); 109 | 110 | if (extractor.errors.length) { 111 | this._errors = this._errors.concat(extractor.errors); 112 | for (var i = 0; i < this._errors.length; ++i) 113 | console.error("Error extracting variables: " + JSON.stringify(this._errors[i])); 114 | 115 | throw new Error(this._errors[0].message); 116 | } 117 | 118 | this._uniforms = this._uniforms.concat(extractor.uniforms); 119 | this._attributes = this._attributes.concat(extractor.attributes); 120 | this._varyings = this._varyings.concat(extractor.varyings); 121 | }, 122 | 123 | _clearExecutable: function() 124 | { 125 | this._executable = null; 126 | this.dispatchEventToListeners(GLSL.Shader.Event.ExecutableChanged); 127 | } 128 | }; 129 | 130 | VariableExtractor = function() { 131 | GLSL.ASTVisitor.call(this, VariableExtractor.Functions); 132 | 133 | this.uniforms = []; 134 | this.attributes = []; 135 | this.varyings = []; 136 | this.errors = []; 137 | }; 138 | 139 | VariableExtractor.prototype = { 140 | __proto__: GLSL.ASTVisitor.prototype, 141 | constructor: VariableExtractor, 142 | 143 | extract: function(shader) 144 | { 145 | this.shader = shader; 146 | this.visitNode(shader.ast); 147 | 148 | // GLSL ES 1.0, Section 7.1 149 | if (this.shader.type === GLSL.Shader.Type.Vertex) { 150 | this.varyings.push({ 151 | name: "gl_Position", 152 | type: "vec4", 153 | qualifier: "varying", 154 | usage: "out", 155 | builtin: true 156 | }); 157 | 158 | this.varyings.push({ 159 | name: "gl_PointSize", 160 | type: "float", 161 | qualifier: "varying", 162 | usage: "out", 163 | builtin: true 164 | }); 165 | } 166 | 167 | // GLSL ES 1.0, Section 7.2 168 | if (this.shader.type === GLSL.Shader.Type.Fragment) { 169 | this.varyings.push({ 170 | name: "gl_FragCoord", 171 | type: "vec4", 172 | qualifier: "varying", 173 | usage: "in", 174 | builtin: true, 175 | }); 176 | 177 | this.varyings.push({ 178 | name: "gl_FrontFacing", 179 | type: "bool", 180 | qualifier: "varying", 181 | usage: "in", 182 | builtin: true, 183 | }); 184 | 185 | this.varyings.push({ 186 | name: "gl_PointCoord", 187 | type: "vec2", 188 | qualifier: "varying", 189 | usage: "in", 190 | builtin: true, 191 | }); 192 | 193 | this.varyings.push({ 194 | name: "gl_FragColor", 195 | type: "vec4", 196 | qualifier: "varying", 197 | usage: "out", 198 | builtin: true, 199 | }); 200 | 201 | this.varyings.push({ 202 | name: "gl_FragData", 203 | type: "vec4[]", 204 | qualifier: "varying", 205 | usage: "out", 206 | builtin: true, 207 | }); 208 | } 209 | 210 | this.shader = null; 211 | }, 212 | 213 | defaultVisitor: function(node) 214 | { 215 | // Do nothing. 216 | } 217 | }; 218 | 219 | VariableExtractor.Functions = {}; 220 | 221 | VariableExtractor.Functions[GLSL.ASTNode.Types.Program] = function(node) 222 | { 223 | this.visitList(node.statements); 224 | } 225 | 226 | VariableExtractor.Functions[GLSL.ASTNode.Types.DeclaratorItem] = function(node) 227 | { 228 | return this.visitNode(node.name); 229 | } 230 | 231 | VariableExtractor.Functions[GLSL.ASTNode.Types.Identifier] = function(node) 232 | { 233 | return node.name; 234 | } 235 | 236 | VariableExtractor.Functions[GLSL.ASTNode.Types.Declarator] = function(node) 237 | { 238 | var typeAttribute = node.typeAttribute; 239 | var itemNames = this.visitList(node.declarators); 240 | 241 | if (this.shader.type === GLSL.Shader.Type.Fragment && typeAttribute.qualifier === "attribute") { 242 | this.errors.push({node: node, message: "'attribute' variable not allowed in fragment shader."}); 243 | return; 244 | } 245 | var list = null; 246 | if (typeAttribute.qualifier === "varying") list = this.varyings; 247 | if (typeAttribute.qualifier === "uniform") list = this.uniforms; 248 | if (typeAttribute.qualifier === "attribute") list = this.attributes; 249 | 250 | var isInputVariable = true; 251 | if (this.shader.type === GLSL.Shader.Type.Vertex && typeAttribute.qualifier === "varying") 252 | isInputVariable = false; 253 | if (this.shader.type === GLSL.Shader.Type.Fragment && typeAttribute.name == "gl_FragColor") 254 | isInputVariable = false; 255 | 256 | for (var i = 0; i < itemNames.length; ++i) { 257 | list.push({ 258 | type: typeAttribute.name, 259 | name: itemNames[i], 260 | qualifier: typeAttribute.qualifier, 261 | usage: isInputVariable ? "in" : "out" 262 | }) 263 | } 264 | } 265 | 266 | module.exports = GLSL.Shader; 267 | -------------------------------------------------------------------------------- /lib/compiler/typecheck.js: -------------------------------------------------------------------------------- 1 | var GLSL = {}; 2 | 3 | // Check semantic properties local to a vertex or fragment shader. 4 | 5 | GLSL.Typechecker = function(shader) { 6 | // TODO: implement 7 | }; 8 | 9 | GLSL.Typechecker.prototype = { 10 | constructor: GLSL.Typechecker, 11 | 12 | // Public: 13 | 14 | typecheck: function() 15 | { 16 | return true; 17 | } 18 | 19 | } 20 | 21 | module.exports = GLSL.Typechecker; 22 | -------------------------------------------------------------------------------- /lib/compiler/visitor.js: -------------------------------------------------------------------------------- 1 | ASTVisitor = function(callbacks) 2 | { 3 | this._callbacks = callbacks || {}; 4 | for (key in this._callbacks) { // Keep braces in case this assert is stripped out. 5 | console.assert(key !== "undefined", "Visitor callback provided for node type 'undefined'. This is a bug. Function text: " + this._callbacks[key].toString()); 6 | } 7 | 8 | } 9 | 10 | ASTVisitor.prototype = { 11 | constructor: ASTVisitor, 12 | 13 | // Public 14 | 15 | // Subclasses should override this to plug in their overridden visit methods. 16 | visitorForType: function(type) 17 | { 18 | if (type in this._callbacks) 19 | return this._callbacks[type]; 20 | 21 | return this.defaultVisitor; 22 | }, 23 | 24 | visitNode: function(node) 25 | { 26 | if (!node || !node.type) 27 | return; 28 | 29 | var callback = this.visitorForType(node.type); 30 | return callback.call(this, node); 31 | }, 32 | 33 | visitList: function(nodeList) 34 | { 35 | if (!(nodeList instanceof Array) || !nodeList.length) 36 | return; 37 | 38 | var result = []; 39 | for (var i = 0; i < nodeList.length; ++i) 40 | result.push(this.visitNode(nodeList[i])); 41 | 42 | return result; 43 | }, 44 | 45 | defaultVisitor: function(node) 46 | { 47 | for (var key in node) { 48 | var val = node[key]; 49 | if (val instanceof Array) 50 | this.visitList(val); 51 | else if (val instanceof Object && val.type) 52 | this.visitNode(val); 53 | } 54 | } 55 | }; 56 | 57 | module.exports = ASTVisitor; 58 | -------------------------------------------------------------------------------- /lib/error.js: -------------------------------------------------------------------------------- 1 | var GLSL = {}; 2 | 3 | GLSL.Error = {}; 4 | GLSL.Error.Type = { 5 | VertexShaderParsing: "error-type-vertex-shader-parsing", 6 | FragmentShaderParsing: "error-type-fragment-shader-parsing", 7 | VertexShaderTranslation: "error-type-vertex-shader-translation", 8 | FragmentShaderTranslation: "error-type-fragment-shader-translation", 9 | VertexShaderExecution: "error-type-vertex-shader-execution", 10 | FragmentShaderExecution: "error-type-fragment-shader-execution", 11 | }; 12 | 13 | module.exports = GLSL.Error; 14 | -------------------------------------------------------------------------------- /lib/events.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008, 2013 Apple Inc. All Rights Reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | // Borrowed from WebKit Web Inspector's Object.js. 27 | 28 | var GLSL = {}; 29 | 30 | GLSL.Object = function() 31 | { 32 | }; 33 | 34 | GLSL.Object.addConstructorFunctions = function(subclassConstructor) 35 | { 36 | // Copies the relevant functions the subclass constructor. 37 | for (var property in GLSL.Object) { 38 | var value = GLSL.Object[property]; 39 | if (typeof value !== "function") 40 | continue; 41 | if (value === arguments.callee) 42 | continue; 43 | subclassConstructor[property] = value; 44 | } 45 | }; 46 | 47 | GLSL.Object.addEventListener = function(eventType, listener, thisObject) 48 | { 49 | thisObject = thisObject || null; 50 | 51 | console.assert(eventType, "Object.addEventListener: invalid event type ", eventType, "(listener: ", listener, "thisObject: ", thisObject, ")"); 52 | if (!eventType) 53 | return; 54 | 55 | console.assert(listener, "Object.addEventListener: invalid listener ", listener, "(event type: ", eventType, "thisObject: ", thisObject, ")"); 56 | if (!listener) 57 | return; 58 | 59 | if (!this._listeners) 60 | this._listeners = {}; 61 | 62 | var listeners = this._listeners[eventType]; 63 | if (!listeners) 64 | listeners = this._listeners[eventType] = []; 65 | 66 | // Prevent registering multiple times. 67 | for (var i = 0; i < listeners.length; ++i) { 68 | if (listeners[i].listener === listener && listeners[i].thisObject === thisObject) 69 | return; 70 | } 71 | 72 | listeners.push({thisObject: thisObject, listener: listener}); 73 | }; 74 | 75 | GLSL.Object.removeEventListener = function(eventType, listener, thisObject) 76 | { 77 | eventType = eventType || null; 78 | listener = listener || null; 79 | thisObject = thisObject || null; 80 | 81 | if (!this._listeners) 82 | return; 83 | 84 | if (!eventType) { 85 | for (eventType in this._listeners) 86 | this.removeEventListener(eventType, listener, thisObject); 87 | return; 88 | } 89 | 90 | var listeners = this._listeners[eventType]; 91 | if (!listeners) 92 | return; 93 | 94 | for (var i = listeners.length - 1; i >= 0; --i) { 95 | if (listener && listeners[i].listener === listener && listeners[i].thisObject === thisObject) 96 | listeners.splice(i, 1); 97 | else if (!listener && thisObject && listeners[i].thisObject === thisObject) 98 | listeners.splice(i, 1); 99 | } 100 | 101 | if (!listeners.length) 102 | delete this._listeners[eventType]; 103 | 104 | if (!Object.keys(this._listeners).length) 105 | delete this._listeners; 106 | }; 107 | 108 | GLSL.Object.removeAllListeners = function() 109 | { 110 | delete this._listeners; 111 | }; 112 | 113 | GLSL.Object.hasEventListeners = function(eventType) 114 | { 115 | if (!this._listeners || !this._listeners[eventType]) 116 | return false; 117 | return true; 118 | }; 119 | 120 | GLSL.Object.prototype = { 121 | constructor: GLSL.Object, 122 | 123 | addEventListener: GLSL.Object.addEventListener, 124 | 125 | removeEventListener: GLSL.Object.removeEventListener, 126 | 127 | removeAllListeners: GLSL.Object.removeAllListeners, 128 | 129 | hasEventListeners: GLSL.Object.hasEventListeners, 130 | 131 | dispatchEventToListeners: function(eventType, eventData) 132 | { 133 | var event = new GLSL.Event(this, eventType, eventData); 134 | 135 | function dispatch(object) 136 | { 137 | if (!object || !object._listeners || !object._listeners[eventType] || event._stoppedPropagation) 138 | return; 139 | 140 | // Make a copy with slice so mutations during the loop doesn't affect us. 141 | var listenersForThisEvent = object._listeners[eventType].slice(0); 142 | 143 | // Iterate over the listeners and call them. Stop if stopPropagation is called. 144 | for (var i = 0; i < listenersForThisEvent.length; ++i) { 145 | listenersForThisEvent[i].listener.call(listenersForThisEvent[i].thisObject, event); 146 | if (event._stoppedPropagation) 147 | break; 148 | } 149 | } 150 | 151 | // Dispatch to listeners of this specific object. 152 | dispatch(this); 153 | 154 | // Allow propagation again so listeners on the constructor always have a crack at the event. 155 | event._stoppedPropagation = false; 156 | 157 | // Dispatch to listeners on all constructors up the prototype chain, including the immediate constructor. 158 | var constructor = this.constructor; 159 | while (constructor) { 160 | dispatch(constructor); 161 | 162 | if (!constructor.prototype.__proto__) 163 | break; 164 | 165 | constructor = constructor.prototype.__proto__.constructor; 166 | } 167 | 168 | return event.defaultPrevented; 169 | } 170 | }; 171 | 172 | GLSL.Event = function(target, type, data) 173 | { 174 | this.target = target; 175 | this.type = type; 176 | this.data = data; 177 | this.defaultPrevented = false; 178 | this._stoppedPropagation = false; 179 | }; 180 | 181 | GLSL.Event.prototype = { 182 | constructor: GLSL.Event, 183 | 184 | stopPropagation: function() 185 | { 186 | this._stoppedPropagation = true; 187 | }, 188 | 189 | preventDefault: function() 190 | { 191 | this.defaultPrevented = true; 192 | } 193 | }; 194 | 195 | module.exports = GLSL; 196 | -------------------------------------------------------------------------------- /lib/glsl.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014, Brian Burg. 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | var GLSL = { 26 | VERSION: "0.1.0", 27 | 28 | Parser: require("./compiler/parser"), 29 | PrettyPrinter: require("./compiler/pretty"), 30 | Shader: require("./compiler/shader"), 31 | Program: require("./compiler/program"), 32 | Runtime: require("./runtime"), 33 | Environment: require("./runtime/environment"), 34 | Object: require("./events").Object, 35 | Event: require("./events").Event, 36 | Error: require("./error") 37 | }; 38 | 39 | module.exports = GLSL; 40 | -------------------------------------------------------------------------------- /lib/runtime.js: -------------------------------------------------------------------------------- 1 | var Runtime = {}; 2 | 3 | // Add built-in types 4 | var vector = require('./runtime/vector'); 5 | Runtime.vec = vector.vec; 6 | Runtime.Vec2 = vector.Vec2; 7 | Runtime.Vec3 = vector.Vec3; 8 | Runtime.Vec4 = vector.Vec4; 9 | 10 | var matrix = require("./runtime/matrix"); 11 | Runtime.mat = matrix.mat; 12 | Runtime.Mat2 = matrix.Mat2; 13 | Runtime.Mat3 = matrix.Mat3; 14 | Runtime.Mat4 = matrix.Mat4; 15 | 16 | var builtins = require('./runtime/builtins'); 17 | var operations = require("./runtime/ops"); 18 | var access = require("./runtime/access"); 19 | var texture = require("./runtime/texture"); 20 | 21 | for (var i in operations) 22 | Runtime[i] = operations[i]; 23 | 24 | for (var i in builtins) 25 | Runtime[i] = builtins[i]; 26 | 27 | for (var i in access) 28 | Runtime[i] = access[i]; 29 | 30 | for (var i in texture) 31 | Runtime[i] = texture[i]; 32 | 33 | module.exports = Runtime; 34 | -------------------------------------------------------------------------------- /lib/runtime/access.js: -------------------------------------------------------------------------------- 1 | var Runtime = {}; 2 | Runtime.vec = require('./vector').vec; 3 | Runtime.mat = require("./matrix").mat; 4 | 5 | var access = {}; 6 | 7 | access.get = function() { 8 | if (arguments.length == 0) 9 | return; 10 | 11 | var x = arguments[0]; 12 | 13 | // get from vec 14 | if (x instanceof Runtime.vec) { 15 | if (arguments.length < 2) 16 | return x; 17 | 18 | return x.get(arguments[1]); 19 | } 20 | 21 | // get from mat 22 | if (x instanceof Runtime.mat) { 23 | if (arguments.length < 2) 24 | return x; 25 | 26 | if (arguments.length == 2) 27 | return x.get(arguments[1]); 28 | 29 | return x.get(arguments[1], arguments[2]); 30 | } 31 | 32 | // get from others (self) 33 | return x; 34 | } 35 | 36 | access.set = function() { 37 | if (arguments.length == 0) 38 | return; 39 | 40 | var x = arguments[0]; 41 | 42 | // set to vec 43 | if (x instanceof Runtime.vec) { 44 | if (arguments.length < 3) 45 | return x; 46 | 47 | return x.set(arguments[1], arguments[2]); 48 | } 49 | 50 | // set to mat 51 | if (x instanceof Runtime.mat) { 52 | if (arguments.length < 3) 53 | return x; 54 | 55 | if (arguments.length == 3) 56 | return x.set(arguments[1], arguments[2]); 57 | 58 | return x.set(arguments[1], arguments[2], arguments[3]); 59 | } 60 | 61 | return x; 62 | } 63 | 64 | module.exports = access; 65 | -------------------------------------------------------------------------------- /lib/runtime/builtins.js: -------------------------------------------------------------------------------- 1 | var Runtime = {}; 2 | var vector = require('./vector'); 3 | var internal = require('./internal'); 4 | var texture = require('./texture'); 5 | Runtime.vec = vector.vec; 6 | Runtime.Vec2 = vector.Vec2; 7 | Runtime.Vec3 = vector.Vec3; 8 | Runtime.Vec4 = vector.Vec4; 9 | 10 | var Builtins = {}; 11 | 12 | // Angle & Trigonometry Functions [OpenGL ES SL 1.0, Sec 8.1] 13 | 14 | Builtins.radians = function(x) { 15 | if (x instanceof Runtime.vec) 16 | return internal._evalVec(x, Builtins.radians); 17 | return x / 180 * Math.PI; 18 | } 19 | 20 | Builtins.degrees = function(x) { 21 | if (x instanceof Runtime.vec) 22 | return internal._evalVec(x, Builtins.degrees); 23 | return x / Math.PI * 180; 24 | } 25 | 26 | Builtins.sin = function(x) { 27 | if (x instanceof Runtime.vec) 28 | return internal._evalVec(x, Builtins.sin); 29 | return Math.sin(x); 30 | } 31 | 32 | Builtins.cos = function(x) { 33 | if (x instanceof Runtime.vec) 34 | return internal._evalVec(x, Builtins.cos); 35 | return Math.cos(x); 36 | } 37 | 38 | Builtins.tan = function(x) { 39 | if (x instanceof Runtime.vec) 40 | return internal._evalVec(x, Builtins.tan); 41 | return Math.tan(x); 42 | } 43 | 44 | Builtins.asin = function(x) { 45 | if (x instanceof Runtime.vec) 46 | return internal._evalVec(x, Builtins.asin); 47 | return Math.asin(x); 48 | } 49 | 50 | Builtins.acos = function(x) { 51 | if (x instanceof Runtime.vec) 52 | return internal._evalVec(x, Builtins.acos); 53 | return Math.acos(x); 54 | } 55 | 56 | Builtins.atan = function(y, x) { 57 | if (typeof x !== "undefined") { 58 | if (y instanceof Runtime.vec) 59 | return internal._evalVec(y, x, Builtins.atan); 60 | return Math.atan2(y, x); 61 | } 62 | 63 | if (y instanceof Runtime.vec) 64 | return internal._evalVec(y, Builtins.atan); 65 | return Math.atan(y); 66 | } 67 | 68 | // Exponential Functions [OpenGL ES SL 1.0, Sec. 8.2] 69 | 70 | Builtins.pow = function(x, y) { 71 | if (x instanceof Runtime.vec) 72 | return internal._evalVec(x, y, Builtins.pow); 73 | return Math.pow(x, y); 74 | } 75 | 76 | Builtins.exp = function(x) { 77 | if (x instanceof Runtime.vec) 78 | return internal._evalVec(x, Builtins.exp); 79 | return Math.exp(x); 80 | } 81 | 82 | Builtins.log = function(x) { 83 | if (x instanceof Runtime.vec) 84 | return internal._evalVec(x, Builtins.log); 85 | return Math.log(x); 86 | } 87 | 88 | Builtins.exp2 = function(x) { 89 | if (x instanceof Runtime.vec) 90 | return internal._evalVec(x, Builtins.exp2); 91 | return Math.pow(2, x); 92 | } 93 | 94 | Builtins.log2 = function(x) { 95 | if (x instanceof Runtime.vec) 96 | return internal._evalVec(x, Builtins.log2); 97 | return Math.log(x) / Math.log(2); 98 | } 99 | 100 | Builtins.sqrt = function(x) { 101 | if (x instanceof Runtime.vec) 102 | return internal._evalVec(x, Builtins.sqrt); 103 | return Math.sqrt(x); 104 | } 105 | 106 | Builtins.inversesqrt = function(x) { 107 | if (x instanceof Runtime.vec) 108 | return internal._evalVec(x, Builtins.inversesqrt); 109 | return 1 / Math.sqrt(x); 110 | } 111 | 112 | // Common Functions [OpenGL ES SL 1.0, Sec. 8.3] 113 | 114 | Builtins.abs = function(x) { 115 | if (x instanceof Runtime.vec) 116 | return internal._evalVec(x, Builtins.abs); 117 | return x >= 0 ? x : -x; 118 | } 119 | 120 | Builtins.sign = function(x) { 121 | if (x instanceof Runtime.vec) 122 | return internal._evalVec(x, Builtins.sign); 123 | if (x == 0) return 0; 124 | return x > 0 ? 1 : -1; 125 | } 126 | 127 | Builtins.floor = function(x) { 128 | if (x instanceof Runtime.vec) 129 | return internal._evalVec(x, Builtins.floor); 130 | return Math.floor(x); 131 | } 132 | 133 | Builtins.ceil = function(x) { 134 | if (x instanceof Runtime.vec) 135 | return internal._evalVec(x, Builtins.ceil); 136 | return Math.ceil(x); 137 | } 138 | 139 | Builtins.fract = function(x) { 140 | if (x instanceof Runtime.vec) 141 | return internal._evalVec(x, Builtins.fract); 142 | return x - Builtins.floor(x); 143 | } 144 | 145 | Builtins.mod = function(x, y) { 146 | if (x instanceof Runtime.vec) 147 | return internal._evalVec(x, internal._extVec(y, x), Builtins.mod); 148 | return x - Math.floor(x / y) * y; 149 | } 150 | 151 | Builtins.min = function(x, y) { 152 | if (x instanceof Runtime.vec) 153 | return internal._evalVec(x, internal._extVec(y, x), Builtins.min); 154 | return Math.min(x, y); 155 | } 156 | 157 | Builtins.max = function(x, y) { 158 | if (x instanceof Runtime.vec) 159 | return internal._evalVec(x, internal._extVec(y, x), Builtins.max); 160 | return Math.max(x, y); 161 | } 162 | 163 | Builtins.clamp = function(x, minVal, maxVal) { 164 | if (minVal > maxVal) 165 | throw new Error("clamp(): maxVal must be larger than minVal."); 166 | 167 | if (x instanceof Runtime.vec) 168 | return internal._evalVec(x, internal._extVec(minVal, x), internal._extVec(maxVal, x), Builtins.clamp); 169 | 170 | return Builtins.min(Builtins.max(x, minVal), maxVal); 171 | } 172 | 173 | Builtins.mix = function(x, y, alpha) { 174 | if (x instanceof Runtime.vec) 175 | return internal._evalVec(x, y, internal._extVec(alpha, x), Builtins.mix); 176 | 177 | return alpha * x + (1 - alpha) * y; 178 | } 179 | 180 | Builtins.step = function(edge, x) { 181 | if (x instanceof Runtime.vec) 182 | return internal._evalVec(internal._extVec(edge, x), x, Builtins.step); 183 | return x < edge ? 0 : 1; 184 | } 185 | 186 | Builtins.smoothstep = function(edge0, edge1, x) { 187 | if (x instanceof Runtime.vec) 188 | return internal._evalVec(internal._extVec(edge0, x), internal._extVec(edge1, x), x, Builtins.smoothstep); 189 | var t = Builtins.clamp((x - edge0) / (edge1 - edge0), 0, 1); 190 | return t * t * (3 - 2 * t); 191 | } 192 | 193 | // Geometric Functions [OpenGL ES SL 1.0, Sec. 8.4] 194 | 195 | Builtins.length = function(v) { 196 | if (internal._checkNumber(v)) 197 | return Math.abs(v); 198 | 199 | if (!internal._op_check(v)) 200 | return null; 201 | 202 | return v.length(); 203 | } 204 | 205 | Builtins.distance = function(x, y) { 206 | if (internal._checkNumber(x, y)) 207 | return Math.abs(x - y); 208 | 209 | if (!internal._op_check(x, y)) 210 | return null; 211 | 212 | var r = Runtime.vec(x).subtract(y); 213 | return Builtins.length(r); 214 | } 215 | 216 | Builtins.dot = function(x, y) { 217 | if (internal._checkNumber(x, y)) 218 | return x * y; 219 | 220 | if (!internal._op_check(x, y)) 221 | return null; 222 | 223 | return x.dot(y); 224 | } 225 | 226 | Builtins.cross = function(x, y) { 227 | if (x.dimensions() != 3) 228 | throw new Error("cross(): parameters x and y must have 3 dimensions."); 229 | 230 | if (!internal._op_check(x, y)) 231 | return null; 232 | 233 | return Runtime.Vec3(x).cross(y); 234 | } 235 | 236 | Builtins.normalize = function(x) { 237 | if (internal._checkNumber(x)) { 238 | if (x == 0) 239 | return x; 240 | return x / Math.abs(x); 241 | } 242 | 243 | if (!internal._op_check(x)) 244 | return null; 245 | 246 | return x.normalize(); 247 | } 248 | 249 | // TODO make it work when arguments are float? 250 | Builtins.faceforward = function(N, I, Nref) { 251 | if (!internal._op_check(I, N, Nref)) 252 | return null; 253 | 254 | // TODO do we expect to change N? 255 | var r = Builtins.dot(Nref, I) < 0 ? Runtime.vec(N) : Runtime.vec(N).negate(); 256 | return r.cast(); 257 | } 258 | 259 | // TODO make it work when arguments are float? 260 | Builtins.reflect = function(I, N) { 261 | if (!internal._op_check(I, N)) 262 | return null; 263 | 264 | var temp = Builtins.dot(I, N) * 2; 265 | return Runtime.vec(I).subtract(Runtime.vec(N).multiply(temp)).cast(); 266 | } 267 | 268 | // TODO check the correctness 269 | // TODO make it work when arguments are float? 270 | Builtins.refract = function(I, N, eta) { 271 | if (!internal._op_check(I, N)) 272 | return null; 273 | 274 | var k = 1 - eta * eta * (1 - Builtins.dot(I, N) * Builtins.dot(I, N)); 275 | 276 | if (k < 0) 277 | return Runtime.vec(I).subtract(I); 278 | var r = eta * Builtins.dot(I, N) + Math.sqrt(k); 279 | 280 | return Runtime.vec(I).multiply(eta).subtract(Runtime.vec(N).multiply(r)).cast(); 281 | } 282 | 283 | // Matrix Functions [OpenGL ES SL 1.0, Sec. 8.5] 284 | 285 | Builtins.matrixCompMult = function(a, b) 286 | { 287 | return a.matrixCompMult(b); 288 | } 289 | 290 | // Vector Relational Functions [OpenGL ES SL 1.0, Sec. 8.6] 291 | 292 | Builtins.lessThan = function(x, y) { 293 | return internal._compare(x, y, "<"); 294 | } 295 | 296 | Builtins.lessThanEqual = function(x, y) { 297 | return internal._compare(x, y, "<="); 298 | } 299 | 300 | Builtins.greaterThan = function(x, y) { 301 | return internal._compare(x, y, ">"); 302 | } 303 | 304 | Builtins.greaterThanEqual = function(x, y) { 305 | return internal._compare(x, y, ">="); 306 | } 307 | 308 | Builtins.equal = function(x, y) { 309 | return internal._compare(x, y, "=="); 310 | } 311 | 312 | Builtins.notEqual = function(x, y) { 313 | return internal._compare(x, y, "!="); 314 | } 315 | 316 | Builtins.any = function(x) { 317 | for (var i = 0; i < x.dimensions(); i++) 318 | if (x.get(i)) 319 | return true; 320 | 321 | return false; 322 | } 323 | 324 | Builtins.all = function(x) { 325 | for (var i = 0; i < x.dimensions(); i++) 326 | if (!x.get(i)) 327 | return false; 328 | 329 | return true; 330 | } 331 | 332 | Builtins.not = function(x) { 333 | var r = Runtime.vec(x); 334 | for (var i = 0; i < x.dimensions(); i++) 335 | r.set(i, x.get(i) ? 0 : 1); 336 | 337 | return r; 338 | } 339 | 340 | Builtins.texture2DLod = function(sampler, coord, lod) { 341 | return texture.texture2D(sampler, coord, lod); 342 | } 343 | 344 | Builtins.texture2DProjLod = function(sampler, coord, lod) { 345 | return texture.texture2DProj(sampler, coord, lod); 346 | } 347 | 348 | Builtins.textureCubeLod = function(sampler, coord, lod) { 349 | return texture.textureCube(sampler, coord, lod); 350 | } 351 | 352 | Builtins.texture2D = function(sampler, coord) { 353 | if (arguments.length == 3) 354 | return texture.texture2D(sampler, coord, arguments[2]); 355 | 356 | return texture.texture2D(sampler, coord); 357 | } 358 | 359 | Builtins.texture2DProj = function(sampler, coord) { 360 | if (arguments.length == 3) 361 | return texture.texture2DProj(sampler, coord, arguments[2]); 362 | 363 | return texture.texture2DProj(sampler, coord); 364 | } 365 | 366 | Builtins.textureCube = function(sampler, coord) { 367 | if (arguments.length == 3) 368 | return texture.textureCube(sampler, coord, arguments[2]); 369 | 370 | return texture.textureCube(sampler, coord); 371 | } 372 | 373 | module.exports = Builtins; 374 | -------------------------------------------------------------------------------- /lib/runtime/environment.js: -------------------------------------------------------------------------------- 1 | GLSL = {}; 2 | GLSL.Object = require("../events").Object; 3 | 4 | GLSL.Environment = function() { 5 | GLSL.Object.call(this); 6 | 7 | this._data = {}; 8 | this._debugHooks = []; 9 | 10 | this._editScopeLevel = 0; 11 | this._runScopeLevel = 0; 12 | }; 13 | 14 | GLSL.Environment.Event = { 15 | InputChanged: "environment-input-changed", 16 | ResultChanged: "environment-result-changed", 17 | }; 18 | 19 | GLSL.Environment.prototype = { 20 | constructor: GLSL.Environment, 21 | __proto__: GLSL.Object.prototype, 22 | 23 | // Public 24 | 25 | get: function(name) 26 | { 27 | return this._data[name]; 28 | }, 29 | 30 | set: function(name, value) 31 | { 32 | console.assert(this._editScopeLevel || this._runScopeLevel, this); 33 | if (!this._editScopeLevel && !this._runScopeLevel) 34 | throw new Error("Tried to set variable " + name + " while not in a edit or run scope!"); 35 | 36 | this._data[name] = value; 37 | }, 38 | 39 | clone: function() 40 | { 41 | var result = new GLSL.Environment; 42 | result.enterEditScope(); 43 | for (var key in this._data) 44 | result.set(key, this.get(key)); 45 | result.exitEditScope(); 46 | return result; 47 | }, 48 | 49 | reset: function() 50 | { 51 | this._data = {}; 52 | }, 53 | 54 | // This is a mechanism used to delimit the scope of variable editing. 55 | // In the future, it could be used to keep track of historical changes. 56 | enterEditScope: function() 57 | { 58 | console.assert(!this._runScopeLevel, this._runScopeLevel); 59 | 60 | ++this._editScopeLevel; 61 | }, 62 | 63 | exitEditScope: function() 64 | { 65 | console.assert(this._editScopeLevel > 0, this._editScopeLevel); 66 | 67 | --this._editScopeLevel; 68 | 69 | if (!this._editScopeLevel) 70 | this.dispatchEventToListeners(GLSL.Environment.Event.InputChanged); 71 | }, 72 | 73 | // This is a mechanism used to delimit the scope of a shader execution. 74 | // In the future, it could be used to keep track of historical changes. 75 | enterRunScope: function() 76 | { 77 | console.assert(!this._editScopeLevel, this._editScopeLevel); 78 | 79 | ++this._runScopeLevel; 80 | }, 81 | 82 | exitRunScope: function() 83 | { 84 | console.assert(!this._editScopeLevel, this._editScopeLevel); 85 | console.assert(this._runScopeLevel > 0, this._runScopeLevel); 86 | 87 | --this._runScopeLevel; 88 | 89 | if (!this._runScopeLevel) 90 | this.dispatchEventToListeners(GLSL.Environment.Event.ResultChanged); 91 | }, 92 | 93 | get debugHooks() 94 | { 95 | return this._debugHooks.slice(); 96 | }, 97 | 98 | clearDebugHooks: function() 99 | { 100 | this._debugHooks = []; 101 | }, 102 | 103 | addDebugHook: function(shaderType, position, hookId, expression) 104 | { 105 | // TODO: implement this once the basic generator exists. 106 | 107 | // Each hook consists of a program point and an expression to be evaluated 108 | // at that point for its result. These results will be saved as program 109 | // outputs with a name that is correlated back to the specific hook. 110 | 111 | // This will be implemented in the generated code in a manner that's 112 | // conceptually similar to JS breakpoints. After each statement, see if 113 | // any hooks apply to the current line and evaluate if necessary. Unlike JS 114 | // breakpoints, simulator execution does not pause and resume; instead 115 | // the client will rerun the shader every time different info is needed. 116 | 117 | // A stepping debugger could be implemented by a client as follows: 118 | // - for the current debugger step, add a hook for each live variable at that program point. 119 | // - show the UI as "paused" at that location, getting runtime data from hook outputs. 120 | 121 | // We don't plan to support it now, but "edit and continue" could be implemented as 122 | // a new hook type that reapplies user modifications to the simulator state. 123 | }, 124 | 125 | validateForShader: function(shader) 126 | { 127 | // TODO: check for required shader inputs. 128 | return true; 129 | }, 130 | 131 | setDefaultValuesForShader: function(shader, suggestedVariableValueCallback) 132 | { 133 | function setDefaultValue(env, variable) { 134 | var value = null; 135 | if (suggestedVariableValueCallback instanceof Function) 136 | value = suggestedVariableValueCallback(variable); 137 | 138 | if (!value) { 139 | switch (variable.type) { 140 | case "float": value = 1.0; break; 141 | case "bool": value = true; break; 142 | case "vec2": value = GLSL.Runtime.Vec2(1, 1); break; 143 | case "vec3": value = GLSL.Runtime.Vec3(1, 1, 1); break; 144 | case "vec4": value = GLSL.Runtime.Vec4(1, 1, 1, 1); break; 145 | case "mat2": value = GLSL.Runtime.Mat2(1, 1, 146 | 1 ,1); break; 147 | case "mat3": value = GLSL.Runtime.Mat3(1, 1, 1, 148 | 1, 1, 1, 149 | 1, 1, 1); break; 150 | case "mat4": value = GLSL.Runtime.Mat4(1, 1, 1, 1, 151 | 1, 1, 1, 1, 152 | 1, 1, 1, 1, 153 | 1, 1, 1, 1); break; 154 | } 155 | } 156 | env.set(variable.name, value); 157 | } 158 | 159 | if (!shader) 160 | return; 161 | 162 | console.assert(shader instanceof GLSL.Shader, shader); 163 | 164 | this.enterEditScope(); 165 | 166 | shader.uniforms.map(setDefaultValue.bind(null, env)); 167 | shader.attributes.map(setDefaultValue.bind(null, env)); 168 | if (shader.type === GLSL.Shader.Type.Fragment) 169 | shader.varyings.map(setDefaultValue.bind(null, env)); 170 | 171 | this.exitEditScope(); 172 | } 173 | }; 174 | 175 | module.exports = GLSL.Environment; 176 | -------------------------------------------------------------------------------- /lib/runtime/internal.js: -------------------------------------------------------------------------------- 1 | var Runtime = {}; 2 | var vector = require('./vector'); 3 | Runtime.vec = vector.vec; 4 | Runtime.Vec2 = vector.Vec2; 5 | Runtime.Vec3 = vector.Vec3; 6 | Runtime.Vec4 = vector.Vec4; 7 | 8 | var Internal = {} 9 | 10 | Internal._evalVec = function() { 11 | var func = arguments[arguments.length - 1]; 12 | 13 | r = Runtime.vec(arguments[0]).cast(); 14 | for (var i = 0; i < arguments[0].dimensions(); i++) { 15 | var arr = []; 16 | for (var j = 0; j < arguments.length - 1; j++) 17 | arr.push(arguments[j].get(i)); 18 | r.set(i, func.apply(this, arr)); 19 | } 20 | return r; 21 | } 22 | 23 | Internal._extVec = function(x, ref) { 24 | if (x instanceof Runtime.vec) 25 | return x; 26 | 27 | switch (ref.dimensions()) { 28 | case 2: return Runtime.Vec2(x); 29 | case 3: return Runtime.Vec3(x); 30 | case 4: return Runtime.Vec4(x); 31 | default: 32 | } 33 | 34 | return x; 35 | } 36 | 37 | Internal._compare = function(x, y, op) { 38 | var r = Runtime.vec(x).subtract(x); 39 | if (op == "<") { 40 | for (var i = 0; i < x.dimensions(); i++) 41 | r.set(i, x.get(i) < y.get(i) ? 1 : 0); 42 | } else if (op == "<=") { 43 | for (var i = 0; i < x.dimensions(); i++) 44 | r.set(i, x.get(i) <= y.get(i) ? 1 : 0); 45 | } else if (op == ">") { 46 | for (var i = 0; i < x.dimensions(); i++) 47 | r.set(i, x.get(i) > y.get(i) ? 1 : 0); 48 | } else if (op == ">=") { 49 | for (var i = 0; i < x.dimensions(); i++) 50 | r.set(i, x.get(i) >= y.get(i) ? 1 : 0); 51 | } else if (op == "==") { 52 | for (var i = 0; i < x.dimensions(); i++) 53 | r.set(i, x.get(i) == y.get(i) ? 1 : 0); 54 | } else if (op == "!=") { 55 | for (var i = 0; i < x.dimensions(); i++) 56 | r.set(i, x.get(i) != y.get(i) ? 1 : 0); 57 | } 58 | 59 | return r; 60 | } 61 | 62 | Internal._op_check = function() { 63 | for (var i = 0; i < arguments.length; i++) { 64 | if (!(arguments[i] instanceof Runtime.vec)) 65 | throw new Error("Expected argument to be instanceof Runtime.vec!"); 66 | 67 | if (i > 0 && arguments[i].dimensions() != arguments[i - 1].dimensions()) 68 | throw new Error("Expected binary operands to have the same dimensions!"); 69 | } 70 | 71 | return true; 72 | } 73 | 74 | Internal._checkNumber = function() { 75 | for (var i = 0; i < arguments.length; i++) 76 | if (typeof arguments[i] !== 'number') 77 | return false; 78 | return true; 79 | } 80 | 81 | Internal.pos = function(x) { 82 | if (x instanceof Runtime.vec) 83 | return this._evalVec(x, Internal.pos); 84 | return +x; 85 | } 86 | 87 | Internal.neg = function(x) { 88 | if (x instanceof Runtime.vec) 89 | return this._evalVec(x, Internal.neg); 90 | return -x; 91 | } 92 | 93 | Internal.bnot = function(x) { 94 | if (x instanceof Runtime.vec) 95 | return this._evalVec(x, Internal.bnot); 96 | return ~x; 97 | } 98 | 99 | Internal.lnot = function(x) { 100 | if (x instanceof Runtime.vec) 101 | return this._evalVec(x, Internal.lnot); 102 | return !x; 103 | } 104 | 105 | Internal.mod = function(x, y) { 106 | if (x instanceof Runtime.vec) 107 | return this._evalVec(x, this._extVec(y, x), Internal.mod); 108 | return x - Math.floor(x / y) * y; 109 | } 110 | 111 | Internal.shl = function(x, y) { 112 | if (x instanceof Runtime.vec) 113 | return this._evalVec(x, y, Internal.shl); 114 | return x << y; 115 | } 116 | 117 | Internal.shr = function(x, y) { 118 | if (x instanceof Runtime.vec) 119 | return this._evalVec(x, y, Internal.shr); 120 | return x >> y; 121 | } 122 | 123 | Internal.lt = function(x, y) { 124 | if (x instanceof Runtime.vec) 125 | return this._evalVec(x, y, Internal.lt); 126 | return x < y; 127 | } 128 | 129 | Internal.gt = function(x, y) { 130 | if (x instanceof Runtime.vec) 131 | return this._evalVec(x, y, Internal.gt); 132 | return x > y; 133 | } 134 | 135 | Internal.le = function(x, y) { 136 | if (x instanceof Runtime.vec) 137 | return this._evalVec(x, y, Internal.le); 138 | return x <= y; 139 | } 140 | 141 | Internal.ge = function(x, y) { 142 | if (x instanceof Runtime.vec) 143 | return this._evalVec(x, y, Internal.ge); 144 | return x >= y; 145 | } 146 | 147 | Internal.band = function(x, y) { 148 | if (x instanceof Runtime.vec) 149 | return this._evalVec(x, y, Internal.band); 150 | return x & y; 151 | } 152 | 153 | Internal.bxor = function(x, y) { 154 | if (x instanceof Runtime.vec) 155 | return this._evalVec(x, y, Internal.bxor); 156 | return x ^ y; 157 | } 158 | 159 | Internal.bor = function(x, y) { 160 | if (x instanceof Runtime.vec) 161 | return this._evalVec(x, y, Internal.bor); 162 | return x | y; 163 | } 164 | 165 | Internal.land = function(x, y) { 166 | if (x instanceof Runtime.vec) 167 | return this._evalVec(x, y, Internal.land); 168 | return x && y; 169 | } 170 | 171 | Internal.lxor = function(x, y) { 172 | if (x instanceof Runtime.vec) 173 | return this._evalVec(x, y, Internal.lxor); 174 | return (x && !y) || (!x && y); 175 | } 176 | 177 | Internal.lor = function(x, y) { 178 | if (x instanceof Runtime.vec) 179 | return this._evalVec(x, y, Internal.lor); 180 | return x || y; 181 | } 182 | 183 | module.exports = Internal; 184 | -------------------------------------------------------------------------------- /lib/runtime/matrix.js: -------------------------------------------------------------------------------- 1 | var Runtime = {}; 2 | Runtime.vec = require('./vector').vec; 3 | 4 | Runtime.mat = function(argv) { 5 | if (!(this instanceof Runtime.mat)) { 6 | var d = []; 7 | 8 | // construct by a mat 9 | if (arguments[0] instanceof Runtime.mat) { 10 | var n = arguments[0].d.length; 11 | for (var i = 0; i < n; i++) { 12 | d.push(arguments[0].d[i].slice()); 13 | } 14 | return new Runtime.mat(d); 15 | } 16 | 17 | // construct by a set of vec 18 | if (arguments[0] instanceof Runtime.vec) { 19 | var n = arguments.length; 20 | for (var i = 0; i < n; i++) 21 | d.push([]); 22 | for (var i = 0; i < n; i++) { 23 | for (var j = 0; j < n; j++) { 24 | if (j < arguments[i].dimensions()) 25 | d[j].push(arguments[i].get(j)); 26 | else 27 | d[j].push(0); 28 | } 29 | } 30 | return new Runtime.mat(d); 31 | } 32 | 33 | // construct by numbers 34 | // we only take the first n * n numbers and ignore the rest 35 | var n = Math.floor(Math.sqrt(arguments.length)); 36 | for (var i = 0; i < n; i++) 37 | d.push([]); 38 | for (var i = 0; i < n; i++) { 39 | for (var j = 0; j < n; j++) 40 | d[j].push(arguments[i * n + j]); 41 | } 42 | return new Runtime.mat(d); 43 | } 44 | 45 | // store as a 2d array 46 | this.d = argv; 47 | return this; 48 | } 49 | 50 | Runtime.mat.prototype = { 51 | constructor: Runtime.mat, 52 | 53 | cast: function() 54 | { 55 | // same dimension casting: from mat to mat? (? = 2 or 3 or 4) 56 | if (arguments.length == 0 || arguments[0] == this.dimensions()) { 57 | switch (this.dimensions()) { 58 | case 2: return new Runtime.Mat2(this.d); 59 | case 3: return new Runtime.Mat3(this.d); 60 | case 4: return new Runtime.Mat4(this.d); 61 | default: 62 | } 63 | return this; 64 | } 65 | 66 | var dim = arguments[0]; 67 | 68 | // from high to low 69 | if (dim < this.dimensions()) { 70 | this.d.splice(dim, this.dimensions() - dim); 71 | for (var i in this.d) { 72 | this.d[i].splice(dim, this.d[i].length - dim); 73 | } 74 | return this.cast(); 75 | } 76 | 77 | // from low to high 78 | var f = this.dimensions() == 1 ? this.d[0][0] : 1; 79 | for (var i in this.d) 80 | for (var j = this.dimensions(); j < dim; j++) 81 | this.d[i].push(0); 82 | for (var i = this.dimensions(); i < dim; i++) { 83 | this.d.push([]); 84 | for (var j = 0; j < dim; j++) { 85 | if (i == j) 86 | this.d[i].push(f); 87 | else 88 | this.d[i].push(0); 89 | } 90 | } 91 | return this.cast() 92 | }, 93 | 94 | get: function() 95 | { 96 | if (arguments.length == 0) 97 | return null; 98 | 99 | // process the first argument 100 | var i = arguments[0]; 101 | if (i >= this.dimensions()) 102 | return null; 103 | 104 | var v = Runtime.vec.apply(null, this.d[i]); 105 | 106 | if (arguments.length == 1) 107 | return v.cast(); 108 | 109 | // process the second argument 110 | var j = arguments[1]; 111 | return v.get(j); 112 | }, 113 | 114 | set: function() 115 | { 116 | if (arguments.length < 2) 117 | return this; 118 | 119 | // process the first argument 120 | var i = arguments[0]; 121 | if (i >= this.dimensions()) 122 | return this; 123 | 124 | // set a vec using one argument 125 | if (arguments.length == 2) { 126 | var v = arguments[1]; 127 | 128 | for (var j = 0; j < this.dimensions(); j++) { 129 | if (j < v.dimensions()) 130 | this.d[i][j] = v.get(j); 131 | else 132 | this.d[i][j] = 0; 133 | } 134 | return this; 135 | } 136 | 137 | // set a number or vec using two arguments 138 | var j = arguments[1]; 139 | var k = arguments[2]; 140 | 141 | var v = Runtime.vec.apply(null, this.d[i]).set(j, k); 142 | 143 | if (typeof v === 'number') { 144 | this.d[i][j] = v; 145 | return this; 146 | } 147 | 148 | this.d[i] = v.d; 149 | 150 | return this; 151 | }, 152 | 153 | dimensions: function() 154 | { 155 | return this.d.length; 156 | }, 157 | 158 | equal: function(m) 159 | { 160 | if (!this._op_check(m)) 161 | return false; 162 | 163 | if (this.dimensions() != m.dimensions()) 164 | return false; 165 | 166 | for (var i = 0; i < this.dimensions(); i++) 167 | for (var j = 0; j < this.dimensions(); j++) 168 | if (this.d[i][j] != m.d[i][j]) 169 | return false; 170 | 171 | return true; 172 | }, 173 | 174 | add: function(m) 175 | { 176 | return this._op(m, "+"); 177 | }, 178 | 179 | subtract: function(m) 180 | { 181 | return this._op(m, "-"); 182 | }, 183 | 184 | matrixCompMult: function(m) 185 | { 186 | return this._op(m, "*"); 187 | }, 188 | 189 | divide: function(m) 190 | { 191 | return this._op(m, "/"); 192 | }, 193 | 194 | multiply: function(m) 195 | { 196 | // mat * mat 197 | // TODO set the resulting mat to this 198 | if (m instanceof Runtime.mat) { 199 | if (!this._op_check(m)) 200 | return this; 201 | 202 | var t = Runtime.mat(m).subtract(m); // set zero matrix 203 | for (var i = 0; i < this.dimensions(); i++) 204 | for (var j = 0; j < this.dimensions(); j++) 205 | for (var k = 0; k < this.dimensions(); k++) 206 | t.d[j][i] += this.d[k][i] * m.d[j][k]; 207 | return t; 208 | } 209 | 210 | // mat * vec 211 | if (m instanceof Runtime.vec) { 212 | if (!this._op_check_vec(v)) 213 | return this; 214 | 215 | var t = Runtime.vec(m).subtract(m); 216 | for (var i = 0; i < this.dimensions(); i++) { 217 | var f = 0; 218 | for (var j = 0; j < this.dimensions(); j++) 219 | f += this.d[j][i] * m.get(j); 220 | t.set(i, f); 221 | } 222 | return t; 223 | } 224 | 225 | // mat * number 226 | for (var i = 0; i < this.dimensions(); i++) 227 | for (var j = 0; j < this.dimensions(); j++) 228 | this.d[i][j] *= m; 229 | 230 | return this; 231 | }, 232 | 233 | _op: function(m, op) 234 | { 235 | if (!this._op_check(m)) 236 | return this; 237 | 238 | for (var i = 0; i < this.dimensions(); i++) 239 | for (var j = 0; j < this.dimensions(); j++) { 240 | if (op == "+") 241 | this.d[i][j] += m.d[i][j]; 242 | else if (op == "-") 243 | this.d[i][j] -= m.d[i][j]; 244 | else if (op == "*") 245 | this.d[i][j] *= m.d[i][j]; 246 | else if (op == "/") 247 | this.d[i][j] /= m.d[i][j]; 248 | } 249 | 250 | return this; 251 | }, 252 | 253 | _op_check: function(m) 254 | { 255 | if (!(m instanceof Runtime.mat)) { 256 | console.error("argument to mat operation is not a mat."); 257 | return false; 258 | } 259 | 260 | if (m.dimensions() != this.dimensions()) { 261 | console.error("unable to operate on two mats of different dimensions."); 262 | return false; 263 | } 264 | 265 | return true; 266 | }, 267 | 268 | _op_check_vec: function(m) 269 | { 270 | if (!(m instanceof Runtime.vec)) { 271 | console.error("argument is not a vec."); 272 | return false; 273 | } 274 | 275 | if (m.dimensions() != this.dimensions()) { 276 | console.error("unable to operate on mat and vec of different dimensions."); 277 | return false; 278 | } 279 | 280 | return true; 281 | } 282 | }; 283 | 284 | Runtime.Mat2 = function(d) { 285 | if (!(this instanceof Runtime.Mat2)) { 286 | return Runtime.mat.apply(null, arguments).cast(2); 287 | } 288 | 289 | if (d.length != 2) { 290 | console.error("2 arguments to Mat2 is expected."); 291 | return; 292 | } 293 | this.d = d; 294 | return this; 295 | }; 296 | 297 | Runtime.Mat2.prototype = { 298 | constructor: Runtime.Mat2, 299 | __proto__: Runtime.mat.prototype, 300 | }; 301 | 302 | Runtime.Mat3 = function(d) { 303 | if (!(this instanceof Runtime.Mat3)) { 304 | return Runtime.mat.apply(null, arguments).cast(3); 305 | } 306 | 307 | if (d.length != 3) { 308 | console.error("3 dimensions of Mat3 is expected."); 309 | return; 310 | } 311 | this.d = d; 312 | return this; 313 | }; 314 | 315 | Runtime.Mat3.prototype = { 316 | constructor: Runtime.Mat3, 317 | __proto__: Runtime.mat.prototype, 318 | }; 319 | 320 | Runtime.Mat4 = function(d) { 321 | if (!(this instanceof Runtime.Mat4)) { 322 | return Runtime.mat.apply(null, arguments).cast(4); 323 | } 324 | 325 | if (d.length != 4) { 326 | console.error("4 dimensions of Mat4 is expected."); 327 | return; 328 | } 329 | this.d = d; 330 | return this; 331 | }; 332 | 333 | Runtime.Mat4.prototype = { 334 | constructor: Runtime.Mat4, 335 | __proto__: Runtime.mat.prototype, 336 | }; 337 | 338 | module.exports = { 339 | "mat": Runtime.mat, 340 | "Mat2": Runtime.Mat2, 341 | "Mat3": Runtime.Mat3, 342 | "Mat4": Runtime.Mat4 343 | }; 344 | -------------------------------------------------------------------------------- /lib/runtime/ops.js: -------------------------------------------------------------------------------- 1 | var Runtime = {}; 2 | Runtime.vec = require('./vector').vec; 3 | Runtime.mat = require("./matrix").mat; 4 | internal = require('./builtins'); 5 | var internal = require('./internal'); 6 | 7 | Operations = {}; 8 | 9 | // Unary Operators [OpenGL ES SL 1.0, Sec 5.9] 10 | 11 | // + term 12 | Operations.op_pos = function(term) 13 | { 14 | return internal.pos(term); 15 | } 16 | 17 | // - term 18 | Operations.op_neg = function(term) 19 | { 20 | return internal.neg(term); 21 | } 22 | 23 | // ~ term 24 | Operations.op_bnot = function(term) 25 | { 26 | return internal.bnot(term); 27 | } 28 | 29 | // ! term 30 | Operations.op_lnot = function(term) 31 | { 32 | return internal.lnot(term); 33 | } 34 | 35 | // Binary Operators [OpenGL ES SL 1.0, Sec 5.9] 36 | 37 | // lhs == rhs 38 | Operations.op_eq = function(lhs, rhs) 39 | { 40 | if (lhs instanceof Runtime.vec && rhs instanceof Runtime.vec) 41 | return lhs.equal(rhs); 42 | 43 | if (lhs instanceof Runtime.mat && rhs instanceof Runtime.mat) 44 | return lhs.equal(rhs); 45 | 46 | if (typeof lhs != 'Object' && typeof rjs != 'Object') 47 | return lhs == rhs; 48 | 49 | return false; 50 | } 51 | 52 | // lhs != rhs 53 | Operations.op_neq = function(lhs, rhs) 54 | { 55 | return !this.op_eq(lhs, rhs); 56 | } 57 | 58 | // lhs * rhs 59 | Operations.op_mul = function(lhs, rhs) 60 | { 61 | var scalarLHS = !(lhs instanceof Runtime.vec || lhs instanceof Runtime.mat); 62 | var scalarRHS = !(rhs instanceof Runtime.vec || rhs instanceof Runtime.mat); 63 | 64 | // {i,f} * {i,f} 65 | if (scalarLHS && scalarRHS) 66 | return lhs * rhs; 67 | 68 | // {i,f} * {vec,mat} 69 | if (scalarLHS) 70 | return rhs.multiply(lhs); 71 | 72 | // {vec,mat} * {i,f} 73 | if (scalarRHS) 74 | return lhs.multiply(rhs); 75 | 76 | // {vec,mat} * {vec,mat} 77 | return lhs.multiply(rhs); 78 | } 79 | 80 | // lhs / rhs 81 | Operations.op_div = function(lhs, rhs) 82 | { 83 | if (lhs instanceof Runtime.vec && rhs instanceof Runtime.mat) 84 | throw new Error("op_div cannot handle vec / mat"); 85 | 86 | if (lhs instanceof Runtime.mat && rhs instanceof Runtime.vec) 87 | throw new Error("op_div cannot handle mat / vec"); 88 | 89 | if (lhs instanceof Runtime.vec || lhs instanceof Runtime.mat) 90 | return lhs.divide(rhs); 91 | 92 | if (rhs instanceof Runtime.vec || rhs instanceof Runtime.mat) 93 | return rhs.divide(lhs); 94 | 95 | return lhs / rhs; 96 | } 97 | 98 | // lhs % rhs 99 | Operations.op_mod = function(lhs, rhs) 100 | { 101 | return internal.mod(lhs, rhs); 102 | } 103 | 104 | // lhs + rhs 105 | Operations.op_add = function(lhs, rhs) 106 | { 107 | if (lhs instanceof Runtime.vec && rhs instanceof Runtime.mat) 108 | throw new Error("op_add cannot handle vec + mat"); 109 | 110 | if (lhs instanceof Runtime.mat && rhs instanceof Runtime.vec) 111 | throw new Error("op_add cannot handle mat + vec"); 112 | 113 | if (lhs instanceof Runtime.vec || lhs instanceof Runtime.mat) 114 | return lhs.add(rhs); 115 | 116 | if (rhs instanceof Runtime.vec || rhs instanceof Runtime.mat) 117 | return rhs.add(lhs); 118 | 119 | return lhs + rhs; 120 | } 121 | 122 | // lhs - rhs 123 | Operations.op_sub = function(lhs, rhs) 124 | { 125 | if (lhs instanceof Runtime.vec && rhs instanceof Runtime.mat) 126 | throw new Error("op_sub cannot handle vec - mat"); 127 | 128 | if (lhs instanceof Runtime.mat && rhs instanceof Runtime.vec) 129 | throw new Error("op_sub cannot handle mat - vec"); 130 | 131 | if (lhs instanceof Runtime.vec || lhs instanceof Runtime.mat) 132 | return lhs.subtract(rhs); 133 | 134 | if (rhs instanceof Runtime.vec || rhs instanceof Runtime.mat) 135 | return rhs.subtract(lhs); 136 | 137 | return lhs - rhs; 138 | } 139 | 140 | // lhs << rhs 141 | Operations.op_shl = function(lhs, rhs) 142 | { 143 | return internal.shl(lhs, rhs); 144 | } 145 | 146 | // lhs >> rhs 147 | Operations.op_shr = function(lhs, rhs) 148 | { 149 | return internal.shr(lhs, rhs); 150 | } 151 | 152 | // lhs < rhs 153 | Operations.op_lt = function(lhs, rhs) 154 | { 155 | return internal.lt(lhs, rhs); 156 | } 157 | 158 | // lhs > rhs 159 | Operations.op_gt = function(lhs, rhs) 160 | { 161 | return internal.gt(lhs, rhs); 162 | } 163 | 164 | // lhs <= rhs 165 | Operations.op_le = function(lhs, rhs) 166 | { 167 | return internal.le(lhs, rhs); 168 | } 169 | 170 | // lhs >= rhs 171 | Operations.op_ge = function(lhs, rhs) 172 | { 173 | return internal.ge(lhs, rhs); 174 | } 175 | 176 | // lhs & rhs 177 | Operations.op_band = function(lhs, rhs) 178 | { 179 | return internal.band(lhs, rhs); 180 | } 181 | 182 | // lhs ^ rhs 183 | Operations.op_bxor = function(lhs, rhs) 184 | { 185 | return internal.bxor(lhs, rhs); 186 | } 187 | 188 | // lhs | rhs 189 | Operations.op_bor = function(lhs, rhs) 190 | { 191 | return internal.bor(lhs, rhs); 192 | } 193 | 194 | // lhs && rhs 195 | Operations.op_land = function(lhs, rhs) 196 | { 197 | return internal.land(lhs, rhs); 198 | } 199 | 200 | // lhs ^^ rhs 201 | Operations.op_lxor = function(lhs, rhs) 202 | { 203 | return internal.lxor(lhs, rhs); 204 | } 205 | 206 | // lhs || rhs 207 | Operations.op_lor = function(lhs, rhs) 208 | { 209 | return internal.lor(lhs, rhs); 210 | } 211 | 212 | 213 | module.exports = Operations; 214 | -------------------------------------------------------------------------------- /lib/runtime/texture.js: -------------------------------------------------------------------------------- 1 | var Runtime = {}; 2 | Runtime.vec = require('./vector').vec; 3 | Runtime.Vec3 = require('./vector').Vec3; 4 | Runtime.Vec4 = require('./vector').Vec4; 5 | 6 | var Texture = { 7 | textures: {}, 8 | }; 9 | 10 | Texture.sampler = function(type, name) { 11 | this.type = type; 12 | this.name = name; 13 | } 14 | 15 | Texture.textureLoad = function(url) { 16 | // TODO load image from a url 17 | // call this.textureAdd to add it to the texture map 18 | } 19 | 20 | Texture.textureAdd = function(sampler, dimension, arr) { 21 | var name = sampler.name; 22 | this.textures[name] = { 23 | dimension: dimension, 24 | data: arr, 25 | nx: arr.length, 26 | ny: arr[0].length, 27 | nz: arr[0][0].length, 28 | }; 29 | } 30 | 31 | Texture.texture2D = function(sampler, coord) { 32 | if (coord.dimensions() == 2) 33 | return this._textureX(sampler, coord); 34 | throw new Error("dimension of coord is not equal to 2."); 35 | } 36 | 37 | Texture.textureCube = function(sampler, coord) { 38 | if (coord.dimensions() == 3) 39 | return this._textureX(sampler, coord); 40 | throw new Error("dimension of coord is not equal to 3."); 41 | } 42 | 43 | Texture.texture2DProj = function(sampler, coord) { 44 | if (coord.dimensions() == 3) 45 | return this._textureX(sampler, coord.get("xy").divide(coord.get(2))); 46 | 47 | if (coord.dimensions() == 4) 48 | return this._textureX(sampler, coord.get("xy").divide(coord.get(3))); 49 | } 50 | 51 | Texture._textureX = function(sampler, coord) { 52 | var texture = this.textures[sampler.name]; 53 | var d = texture.data; 54 | if (coord instanceof Runtime.vec && coord.dimensions() == 2) { 55 | var x = coord.get(0) * texture.nx - 1; 56 | var y = coord.get(1) * texture.ny - 1; 57 | 58 | // linear interpolation 59 | var xl = Math.floor(x); 60 | var yl = Math.floor(y); 61 | var xr = Math.ceil(x); 62 | var yr = Math.ceil(y); 63 | var a1 = x - xl; 64 | var a2 = xr - x; 65 | var b1 = y - yl; 66 | var b2 = yr - y; 67 | 68 | var arr = []; 69 | for (var i in d[xl][yl]) { 70 | if (yl == yr && xl == xr) 71 | arr.push(d[xl][yl][i]); 72 | else if (yl == yr) 73 | arr.push(a1 * d[xl][yl][i] + a2 * d[xr][yl][i]); 74 | else if (xl == xr) 75 | arr.push(b1 * d[xl][yl][i] + b2 * d[xl][yr][i]); 76 | else 77 | arr.push(b1 * (a1 * d[xl][yl][i] + a2 * d[xr][yl][i]) 78 | + b2 * (a1 * d[xl][yr][i] + a2 * d[xr][yr][i])); 79 | } 80 | 81 | return Runtime.Vec4.apply(null, arr); 82 | } else if (coord instanceof Runtime.vec && coord.dimensions() == 3) { 83 | var x = coord.get(0) * texture.nx - 1; 84 | var y = coord.get(1) * texture.nx - 1; 85 | var z = coord.get(2) * texture.nx - 1; 86 | 87 | // linear interpolation 88 | var xl = Math.floor(x); 89 | var yl = Math.floor(y); 90 | var zl = Math.floor(z); 91 | var xr = Math.ceil(x); 92 | var yr = Math.ceil(y); 93 | var zr = Math.ceil(z); 94 | var a1 = x - xl; 95 | var a2 = xr - x; 96 | var b1 = y - yl; 97 | var b2 = yr - y; 98 | var c1 = z - zl; 99 | var c2 = zr - z; 100 | 101 | var arr = []; 102 | for (var i in d[xl][yl][zl]) { 103 | if (xl == xr && yl == yr && zl == zr) 104 | arr.push(d[xl][yl][zl][i]); 105 | else if (xl == xr && yl == yr) 106 | arr.push(c1 * d[xl][yl][zl][i] + c2 * d[xl][yl][zr][i]); 107 | else if (yl == yr && zl == zr) 108 | arr.push(a1 * d[xl][yl][zl][i] + a2 * d[xr][yl][zl][i]); 109 | else if (zl == zr && xl == xr) 110 | arr.push(b1 * d[xl][yl][zl][i] + b2 * d[xl][yr][zl][i]); 111 | else if (xl == xr) 112 | arr.push(b1 * (c1 * d[xl][yl][zl][i] + c2 * d[xl][yl][zr][i]) 113 | + b2 * (c1 * d[xl][yr][zl][i] + c2 * d[xl][yr][zr][i])); 114 | else if (yl == yr) 115 | arr.push(a1 * (c1 * d[xl][yl][zl][i] + c2 * d[xl][yl][zr][i]) 116 | + a2 * (c1 * d[xr][yl][zl][i] + c2 * d[xr][yl][zr][i])); 117 | else if (zl == zr) 118 | arr.push(b1 * (a1 * d[xl][yl][zl][i] + a2 * d[xr][yl][zl][i]) 119 | + b2 * (a1 * d[xl][yr][zl][i] + a2 * d[xr][yr][zl][i])); 120 | else 121 | arr.push(a1 * b1 * (c1 * d[xl][yl][zl][i] + c2 * d[xl][yl][zr][i]) 122 | + a1 * b2 * (c1 * d[xl][yr][zl][i] + c2 * d[xl][yr][zr][i]) 123 | + a2 * b1 * (c1 * d[xr][yl][zl][i] + c2 * d[xr][yl][zr][i]) 124 | + a2 * b2 * (c1 * d[xr][yr][zl][i] + c2 * d[xr][yr][zr][i])); 125 | } 126 | 127 | return Runtime.Vec4.apply(null, arr); 128 | } 129 | } 130 | 131 | module.exports = Texture; 132 | -------------------------------------------------------------------------------- /lib/runtime/vector.js: -------------------------------------------------------------------------------- 1 | Runtime = {}; 2 | 3 | Runtime.vec = function(d) { 4 | if (!(this instanceof Runtime.vec)) { 5 | var arr = []; 6 | for (var i = 0; i < arguments.length; ++i) { 7 | // from vec 8 | if (arguments[i] instanceof Runtime.vec) 9 | arr = arr.concat(arguments[i].get().slice()); 10 | // from number 11 | else if (typeof arguments[i] === 'number') 12 | arr.push(arguments[i]); 13 | } 14 | return new Runtime.vec(arr); 15 | } 16 | 17 | this.d = d; 18 | return this; 19 | } 20 | 21 | Runtime.vec.prototype = { 22 | constructor: Runtime.vec, 23 | 24 | cast: function(dim) 25 | { 26 | // same dimension casting: from vec to vec? (? = 2 or 3 or 4) 27 | if (typeof dim === "undefined" || dim === this.dimensions()) { 28 | switch (this.dimensions()) { 29 | case 2: return new Runtime.Vec2(this.d); 30 | case 3: return new Runtime.Vec3(this.d); 31 | case 4: return new Runtime.Vec4(this.d); 32 | default: 33 | } 34 | return this; 35 | } 36 | 37 | // from high to low 38 | if (dim < this.dimensions()) { 39 | this.d.splice(dim, this.dimensions() - dim); 40 | return this.cast(); 41 | } 42 | 43 | // from low to high 44 | if (this.dimensions() == 1) { 45 | for (var i = 1; i < dim; ++i) 46 | this.d.push(this.d[0]); 47 | return this.cast(); 48 | } 49 | 50 | return this; 51 | }, 52 | 53 | get: function(selector) 54 | { 55 | if (typeof selector === "undefined") 56 | return this.d; 57 | 58 | // if i is a string: xyzw, rgba, or stpq, return vec 59 | if (typeof selector === 'string') { 60 | var result = []; 61 | for (var i = 0; i < selector.length; ++i) { 62 | switch (selector.charAt(i)) { 63 | case 'x': 64 | case 'r': 65 | case 's': 66 | result.push(this.d[0]); 67 | break; 68 | case 'y': 69 | case 'g': 70 | case 't': 71 | if (this.dimensions() >= 2) 72 | result.push(this.d[1]); 73 | break; 74 | case 'z': 75 | case 'b': 76 | case 'p': 77 | if (this.dimensions() >= 3) 78 | result.push(this.d[2]); 79 | break; 80 | case 'w': 81 | case 'a': 82 | case 'q': 83 | if (this.dimensions() >= 4) 84 | result.push(this.d[3]); 85 | break; 86 | default: 87 | } 88 | } 89 | if (result.length === 1) 90 | return result[0]; 91 | return Runtime.vec.apply(null, result).cast(); 92 | } 93 | 94 | // if selector is a number, return number 95 | if (selector >= this.dimensions()) 96 | return null; 97 | 98 | return this.d[selector]; 99 | }, 100 | 101 | set: function(selector, value) 102 | { 103 | if (typeof selector === 'number') { 104 | if (selector < this.dimensions()) 105 | this.d[selector] = value; 106 | 107 | return this; 108 | } 109 | 110 | if (typeof selector === 'string') { 111 | for (var i = 0; i < selector.length; ++i) { 112 | var destructuredValue = (typeof value === 'number') ? value : value.get(i); 113 | switch (selector.charAt(i)) { 114 | case 'x': 115 | case 'r': 116 | case 's': 117 | this.set(0, destructuredValue); 118 | break; 119 | case 'y': 120 | case 'g': 121 | case 't': 122 | if (this.dimensions() >= 2) 123 | this.set(1, destructuredValue); 124 | break; 125 | case 'z': 126 | case 'b': 127 | case 'p': 128 | if (this.dimensions() >= 3) 129 | this.set(2, destructuredValue); 130 | break; 131 | case 'w': 132 | case 'a': 133 | case 'q': 134 | if (this.dimensions() >= 4) 135 | this.set(3, destructuredValue); 136 | break; 137 | default: 138 | throw new Error("Unknown field or index selector character '" + selector.charAt(i) + "' for Runtime.vec"); 139 | } 140 | } 141 | return this; 142 | } 143 | 144 | throw new Error("Unknown field or index selector '" + selector + "' for Runtime.vec"); 145 | }, 146 | 147 | dimensions: function() 148 | { 149 | return this.d.length; 150 | }, 151 | 152 | add: function(v) 153 | { 154 | if (v instanceof Runtime.vec) { 155 | for (var i = 0; i < this.d.length; ++i) 156 | this.d[i] += v.d[i]; 157 | } 158 | else if (typeof v === 'number') { 159 | for (var i = 0; i < this.d.length; ++i) 160 | this.d[i] += v; 161 | } 162 | else 163 | throw new Error("Unexpected argument to vec.add"); 164 | 165 | return this; 166 | }, 167 | 168 | negate: function() 169 | { 170 | for (var i = 0; i < this.d.length; ++i) 171 | this.d[i] = -this.d[i]; 172 | 173 | return this; 174 | }, 175 | 176 | subtract: function(v) 177 | { 178 | if (v instanceof Runtime.vec) { 179 | for (var i = 0; i < this.d.length; ++i) 180 | this.d[i] -= v.d[i]; 181 | } 182 | else if (typeof v === 'number') { 183 | for (var i = 0; i < this.d.length; ++i) 184 | this.d[i] -= v; 185 | } 186 | else 187 | throw new Error("Unexpected argument to vec.subtract"); 188 | 189 | return this; 190 | }, 191 | 192 | multiply: function(v) 193 | { 194 | // vec .* vec 195 | if (v instanceof Runtime.vec) { 196 | if (!this._op_check(v)) 197 | return; 198 | 199 | for (var i = 0; i < this.d.length; ++i) 200 | this.d[i] *= v.d[i]; 201 | return this; 202 | } 203 | 204 | //if (v instanceof mat) { 205 | //} 206 | 207 | // vec * number 208 | for (var i = 0; i < this.d.length; ++i) 209 | this.d[i] *= v; 210 | 211 | return this; 212 | }, 213 | 214 | divide: function(v) 215 | { 216 | if (v instanceof Runtime.vec) { 217 | for (var i = 0; i < this.d.length; ++i) 218 | this.d[i] /= v.d[i]; 219 | } 220 | else if (typeof v === 'number') { 221 | v = v || 0.0000001; // Try to avoid NaNs from div0 222 | for (var i = 0; i < this.d.length; ++i) 223 | this.d[i] /= v; 224 | } 225 | else 226 | throw new Error("Unexpected argument to vec.add"); 227 | 228 | return this; 229 | }, 230 | 231 | equal: function(v) 232 | { 233 | if (!this._op_check(v)) 234 | return; 235 | 236 | for (var i = 0; i < this.d.length; ++i) 237 | if (this.d[i] != v.d[i]) 238 | return false; 239 | return true; 240 | }, 241 | 242 | dot: function(v) 243 | { 244 | if (!this._op_check(v)) 245 | return; 246 | 247 | var r = 0; 248 | for (var i = 0; i < this.d.length; ++i) 249 | r += this.d[i] * v.d[i]; 250 | return r; 251 | }, 252 | 253 | normalize: function() 254 | { 255 | var len = this.length(); 256 | 257 | for (var i = 0; i < this.d.length; ++i) 258 | this.d[i] /= len; 259 | 260 | return this; 261 | }, 262 | 263 | length: function() 264 | { 265 | return Math.sqrt(this.length2()); 266 | }, 267 | 268 | length2: function() 269 | { 270 | var r = 0; 271 | for (var i = 0; i < this.d.length; ++i) 272 | r += this.d[i] * this.d[i]; 273 | 274 | return r; 275 | }, 276 | 277 | _op_check: function(v) 278 | { 279 | if (!(v instanceof Runtime.vec)) { 280 | console.error("argument to vec operation is not a vec."); 281 | return false; 282 | } 283 | 284 | if (v.dimensions() != this.dimensions()) { 285 | console.error("unable to operate on two vecs of different dimensions."); 286 | return false; 287 | } 288 | 289 | return true; 290 | }, 291 | }; 292 | 293 | Runtime.Vec2 = function(d) { 294 | if (!(this instanceof Runtime.Vec2)) { 295 | return Runtime.vec.apply(null, arguments).cast(2); 296 | } 297 | 298 | if (d.length != 2) { 299 | console.error("2 arguments to Vec2 is expected."); 300 | return; 301 | } 302 | this.d = d; 303 | return this; 304 | } 305 | 306 | Runtime.Vec2.prototype = { 307 | constructor: Runtime.Vec2, 308 | __proto__: Runtime.vec.prototype, 309 | }; 310 | 311 | Runtime.Vec3 = function(d) { 312 | if (!(this instanceof Runtime.Vec3)) { 313 | return Runtime.vec.apply(null, arguments).cast(3); 314 | } 315 | 316 | if (d.length != 3) { 317 | console.error("3 arguments to Vec3 is expected."); 318 | return; 319 | } 320 | this.d = d; 321 | return this; 322 | } 323 | 324 | Runtime.Vec3.prototype = { 325 | constructor: Runtime.Vec3, 326 | __proto__: Runtime.vec.prototype, 327 | }; 328 | 329 | Runtime.Vec3.prototype.cross = function(v) { 330 | if (v.dimensions() != 3) 331 | console.error("arguments to Vec3.cross() should be Vec3."); 332 | 333 | return Runtime.Vec3( 334 | this.get(1) * v.get(2) - this.get(2) * v.get(1), 335 | this.get(2) * v.get(0) - this.get(0) * v.get(2), 336 | this.get(0) * v.get(1) - this.get(1) * v.get(0) 337 | ); 338 | } 339 | 340 | Runtime.Vec4 = function(d) { 341 | if (!(this instanceof Runtime.Vec4)) { 342 | return Runtime.vec.apply(null, arguments).cast(4); 343 | } 344 | 345 | if (d.length != 4) { 346 | console.error("4 arguments to Vec4 is expected."); 347 | return; 348 | } 349 | this.d = d; 350 | return this; 351 | } 352 | 353 | Runtime.Vec4.prototype = { 354 | constructor: Runtime.Vec4, 355 | __proto__: Runtime.vec.prototype, 356 | }; 357 | 358 | module.exports = { 359 | "vec": Runtime.vec, 360 | "Vec2": Runtime.Vec2, 361 | "Vec3": Runtime.Vec3, 362 | "Vec4": Runtime.Vec4 363 | }; 364 | -------------------------------------------------------------------------------- /test/runtime/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * run test.js using node.js 3 | */ 4 | 5 | var r = require('../../lib/runtime'); 6 | 7 | var options = { 8 | "vec": [], 9 | }; 10 | 11 | //(function(r, options) { 12 | 13 | var test = { 14 | 'count': 0, 15 | 'failed': 0, 16 | '_err': 0.02, 17 | } 18 | 19 | test.done = function() { 20 | console.log(this.failed + " / " + this.count + " test(s) failed."); 21 | } 22 | 23 | test.func = function() { 24 | this.count++; 25 | 26 | var len = arguments.length; 27 | var f = arguments[0]; 28 | 29 | // rule out invalid tests 30 | if (!r[f]) 31 | console.log("[invalid test]:[" + f + "]"); 32 | 33 | var input = []; 34 | for (var i = 1; i < len - 1; i++) 35 | input.push(arguments[i]); 36 | var output_expected = arguments[len - 1]; 37 | var output_actual = (r[f]).apply(r, input); 38 | 39 | // compare results 40 | if (this._compare(output_actual, output_expected)) { 41 | console.log("[passed]:[" + f + "]"); 42 | } else { 43 | this.failed++; 44 | console.error("[failed]:[" + f + "] expected " + output_expected + ", output " + output_actual); 45 | } 46 | } 47 | 48 | test._compare = function(x, y) { 49 | // compare x and y when they are both vec or mat 50 | if ((x instanceof r.vec) && (y instanceof r.vec) || 51 | (x instanceof r.mat) && (y instanceof r.mat)) { 52 | return x.equal(y); 53 | } 54 | 55 | // TODO add sanity checks 56 | 57 | // compare x and y when they are both numbers 58 | return x >= y - this._err && x <= y + this._err; 59 | } 60 | 61 | 62 | var v2 = r.Vec2(1, 2); 63 | v2.minus(r.Vec2(5, 2)); 64 | console.log(v2); 65 | 66 | var v3 = r.Vec3(1, 2, 3); 67 | v3.add(r.Vec3(3, 2, 1)); 68 | console.log(v3); 69 | 70 | var v4 = r.Vec4(1, 2, 3, 4); 71 | v4.multiply(v4); 72 | console.log(v4); 73 | 74 | var v31 = r.Vec3(v3).normalize(); 75 | console.log(v3, v31); 76 | console.log(r.cross(v3, v31)); 77 | 78 | console.log(r.Vec4(r.Vec2(1), 2), r.Vec2(r.Vec3(3)), r.Vec4(4)); 79 | 80 | // Angle 81 | test.func("radians", 90, 1.57); 82 | //test.func("radians", r.Vec3(90, 90, 180), r.Vec3(1.57, 1.57, 3.14)); 83 | //console.log(r.radians(r.Vec3(90, 90, 180))); 84 | 85 | // Exponential 86 | test.func("pow", 2, 3, 8); 87 | test.func("sqrt", 9, 3); 88 | test.func("inversesqrt", 16, 0.25); 89 | test.func("pow", r.Vec3(4, 3, 2), r.Vec3(1, 3, 5), r.Vec3(4, 27, 32)); 90 | test.func("exp2", r.Vec3(1, 3, 5), r.Vec3(2, 8, 32)); 91 | 92 | // Common 93 | test.func("fract", 1.5, 0.5); 94 | test.func("sign", -1.1, -1); 95 | test.func("sign", 1111.1, 1); 96 | test.func("sign", 0.0, 0); 97 | test.func("clamp", 10, 20, 22, 20); 98 | 99 | // Geometric 100 | test.func("length", r.Vec2(3, 4), 5); 101 | test.func("distance", v2, r.Vec2(v2).normalize(), 3); 102 | test.func("dot", r.Vec2(3, 4), r.Vec2(3, 4), 25); 103 | test.func("dot", 3, 3, 9); 104 | test.func("cross", r.Vec3(1, 2, 0), r.Vec3(2, 4, 0), r.Vec3(0, 0, 0)); 105 | test.func("normalize", r.Vec2(4, 0), r.Vec2(1, 0)); 106 | test.func("faceforward", r.Vec3(1, 1, 1), v3, v31, r.Vec3(-1, -1, -1)); 107 | test.func("reflect", r.Vec3(1, 1, 0), r.Vec3(0, 1, 0), r.Vec3(1, -1, 0)); 108 | test.func("refract", r.Vec3(1, 1, 0), r.Vec3(0, 1, 0), 0.5, r.Vec3(0.5, -1, 0)); 109 | //console.log(r.refract(r.Vec3(1, 1, 0), r.Vec3(0, 1, 0), 1.5)); 110 | 111 | // Vector Relational Functions 112 | test.func("lessThan", r.Vec2(1, 2), r.Vec2(3, 2), r.Vec2(1, 0)); 113 | test.func("lessThanEqual", r.Vec2(1, 2), r.Vec2(3, 2), r.Vec2(1, 1)); 114 | test.func("greaterThan", r.Vec2(1, 2), r.Vec2(3, 2), r.Vec2(0, 0)); 115 | test.func("greaterThanEqual", r.Vec2(1, 2), r.Vec2(3, 2), r.Vec2(0, 1)); 116 | test.func("equal", r.Vec2(1, 2), r.Vec2(3, 2), r.Vec2(0, 1)); 117 | test.func("notEqual", r.Vec2(1, 2), r.Vec2(3, 2), r.Vec2(1, 0)); 118 | test.func("any", r.Vec2(1, 0), true); 119 | test.func("any", r.Vec2(0, 0), false); 120 | test.func("all", r.Vec2(1, 0), false); 121 | test.func("all", r.Vec2(10, 10), true); 122 | test.func("not", r.Vec2(10, 0), r.Vec2(0, 1)); 123 | 124 | // Getters and Setters 125 | test.func("get", r.Vec2(100, 200), 0, 100); 126 | test.func("get", r.Vec3(10, 20, 30), "yywx", r.Vec3(20, 20, 10)); 127 | test.func("get", r.Vec3(10, 20, 30), "y", 20); 128 | test.func("set", r.Vec2(100, 200), 0, -100, r.Vec2(-100, 200)); 129 | test.func("set", r.Vec2(10, 20), "yywx", r.Vec4(2000, 200, 400, 100), r.Vec2(100, 200)); 130 | test.func("set", r.Vec2(10, 20), "ggar", r.Vec4(2000, 200, 400, 100), r.Vec2(100, 200)); 131 | 132 | test.func("get", r.mat(100, 200, 300, 400), 0, 1, 300); 133 | test.func("get", r.mat(100, 200, 300, 400), 0, r.Vec2(100, 300)); 134 | test.func("get", r.mat(100, 200, 300, 400), 0, "y", 300); 135 | test.func("get", r.mat(100, 200, 300, 400), 1, "xy", r.Vec2(200, 400)); 136 | test.func("set", r.mat(100, 200, 300, 400), 0, 1, -300, r.mat(100, 200, -300, 400)); 137 | test.func("set", r.mat(100, 200, 300, 400), 0, r.Vec3(-1, -3, -4), r.mat(-1, 200, -3, 400)); 138 | test.func("set", r.mat(100, 200, 300, 400), 0, "yxy", r.Vec3(-1, -3, -4), r.mat(-3, 200, -4, 400)); 139 | 140 | // ops tests 141 | test.func("op_pos", r.Vec2(10, -1.4), r.Vec2(10, -1.4)); 142 | test.func("op_neg", r.Vec2(10, -1.4), r.Vec2(-10, 1.4)); 143 | 144 | test.func("op_bnot", r.Vec2(5, 1), r.Vec2(-6, 2)); // wrong 145 | test.func("op_lnot", r.Vec2(10, -1), r.Vec2(false, true)); // wrong 146 | test.func("op_eq", r.Vec2(10, -1,4), r.Vec2(1, -1.4), false); 147 | test.func("op_neq", r.Vec2(10, -1,4), r.Vec2(1, -1.4), true); 148 | 149 | test.func("op_mod", 10.5, 2, 0.5); 150 | test.func("op_mod", r.Vec2(10.5, 4.5), r.Vec2(2, 1.5), r.Vec2(0.5, 0)); 151 | test.func("op_add", 10.5, 2, 12.5); 152 | test.func("op_add", r.Vec2(10.5, 4.5), r.Vec2(2, 1.5), r.Vec2(12.5, 6)); 153 | test.func("op_shl", 1, 2, 4); 154 | test.func("op_shl", r.Vec2(1, 6), r.Vec2(2, 4), r.Vec2(4, 96)); 155 | 156 | test.done(); 157 | 158 | var mat0 = r.Mat2( 159 | 10, 20, 160 | 30, 40 161 | ); 162 | console.log(mat0); 163 | 164 | var mat1 = r.Mat3( 165 | 1, 2, 3, 166 | 4, 8, 12, 167 | 8, 16, 24, 10 168 | ); 169 | console.log(r.Mat4(mat0)); 170 | console.log(r.Mat4(4)); 171 | 172 | var mat2 = r.mat( 173 | r.Vec3(1, 2, 3), 174 | r.Vec2(4, 8), 175 | r.Vec4(8, 16, 24, 9) 176 | ); 177 | console.log(mat2); 178 | 179 | var mat3 = r.mat(mat2); 180 | mat3.set(1, r.Vec3(4, 8, 12)); 181 | console.log(mat3, mat3.equal(mat1)); 182 | 183 | console.log("mat.add", r.mat(mat1).add(mat3)); 184 | console.log("mat.minus", r.mat(mat1).minus(mat3)); 185 | console.log("mat.divide", r.mat(mat1).divide(mat3)); 186 | console.log("mat.matrixCompMult", r.mat(mat1).matrixCompMult(mat3)); 187 | console.log("mat.multiply", r.mat(mat1).multiply(mat3)); 188 | 189 | // Testing texture lookup 190 | var arr = []; 191 | var arr1 = [] 192 | for (var i = 0; i < 100; i++) { 193 | var row = []; 194 | for (var j = 0; j < 200; j++) { 195 | var column = []; 196 | for (var k = 0; k < 100; k++) { 197 | column.push([k / 220, i / 110, 0.3, 0.1]); 198 | } 199 | row.push(column); 200 | } 201 | arr.push(row); 202 | arr1.push(row[0]); 203 | } 204 | 205 | var s1 = new r.sampler("aaa", "img1"); 206 | r.textureAdd(s1, 2, arr1); 207 | var v11 = r.texture2D(s1, r.Vec3(0.23, 0.82)); 208 | var v12 = r.texture2D(s1, r.Vec3(0.235, 0.821)); 209 | console.log(v11, v12); 210 | 211 | var s2 = new r.sampler("bbb", "img2"); 212 | r.textureAdd(s2, 3, arr); 213 | var v21 = r.textureCube(s2, r.Vec3(0.235, 0.821, 0.065)); 214 | var v22 = r.textureCube(s2, r.Vec3(0.23, 0.82, 0.06)); 215 | console.log(v21, v22); 216 | 217 | //})(GLSL.r, options) 218 | --------------------------------------------------------------------------------