├── LICENSE ├── README.md ├── examples ├── deps │ ├── CodeMirror.css │ ├── CodeMirror.js │ ├── ShaderEditor.js │ ├── glsl.js │ ├── styles.css │ ├── three.js │ └── three.min.js └── index.html └── src └── ShaderEditor.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jaume Sanchez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Live WebGL Shader Editor 2 | This is a JavaScript library that aims to provide the same functionality as *Firefox DevTools Shader Editor*: modify the source of shaders in real-time, in the browser, without reloading, easy to include and totally implementation-agnostic. Using function hooks to modify the relevant methods of *WebGLRenderingContext*, it's possible to add this features with JavaScript only. 3 | 4 | The main idea is to create a *Chrome DevTools extension*. And I have really no idea how to get that done, so if anyone is willing to help, let's do it! 5 | 6 | [![demo](http://www.clicktorelease.com/blog/shader-editor-images/snapshot.jpg)](http://www.clicktorelease.com/tmp/shader-editor) 7 | 8 | You can read more about this in [Creating a Plug'n Play Live WebGL Shader Editor](http://www.clicktorelease.com/blog/live-webgl-shader-editor) 9 | 10 | As always: forks, pull requests and code critiques are welcome! 11 | 12 | #### License #### 13 | 14 | MIT licensed 15 | 16 | Copyright (C) 2015 Jaume Sanchez Elias, http://www.clicktorelease.com -------------------------------------------------------------------------------- /examples/deps/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 | color: black; 8 | } 9 | 10 | /* PADDING */ 11 | 12 | .CodeMirror-lines { 13 | padding: 4px 0; /* Vertical padding around content */ 14 | } 15 | .CodeMirror pre { 16 | padding: 0 4px; /* Horizontal padding of content */ 17 | } 18 | 19 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 20 | background-color: white; /* The little square between H and V scrollbars */ 21 | } 22 | 23 | /* GUTTER */ 24 | 25 | .CodeMirror-gutters { 26 | border-right: 1px solid #ddd; 27 | background-color: #f7f7f7; 28 | white-space: nowrap; 29 | } 30 | .CodeMirror-linenumbers {} 31 | .CodeMirror-linenumber { 32 | padding: 0 3px 0 5px; 33 | min-width: 20px; 34 | text-align: right; 35 | color: #999; 36 | white-space: nowrap; 37 | } 38 | 39 | .CodeMirror-guttermarker { color: black; } 40 | .CodeMirror-guttermarker-subtle { color: #999; } 41 | 42 | /* CURSOR */ 43 | 44 | .CodeMirror div.CodeMirror-cursor { 45 | border-left: 1px solid black; 46 | } 47 | /* Shown when moving in bi-directional text */ 48 | .CodeMirror div.CodeMirror-secondarycursor { 49 | border-left: 1px solid silver; 50 | } 51 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursor { 52 | width: auto; 53 | border: 0; 54 | background: #7e7; 55 | } 56 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursors { 57 | z-index: 1; 58 | } 59 | 60 | .cm-animate-fat-cursor { 61 | width: auto; 62 | border: 0; 63 | -webkit-animation: blink 1.06s steps(1) infinite; 64 | -moz-animation: blink 1.06s steps(1) infinite; 65 | animation: blink 1.06s steps(1) infinite; 66 | } 67 | @-moz-keyframes blink { 68 | 0% { background: #7e7; } 69 | 50% { background: none; } 70 | 100% { background: #7e7; } 71 | } 72 | @-webkit-keyframes blink { 73 | 0% { background: #7e7; } 74 | 50% { background: none; } 75 | 100% { background: #7e7; } 76 | } 77 | @keyframes blink { 78 | 0% { background: #7e7; } 79 | 50% { background: none; } 80 | 100% { background: #7e7; } 81 | } 82 | 83 | /* Can style cursor different in overwrite (non-insert) mode */ 84 | div.CodeMirror-overwrite div.CodeMirror-cursor {} 85 | 86 | .cm-tab { display: inline-block; text-decoration: inherit; } 87 | 88 | .CodeMirror-ruler { 89 | border-left: 1px solid #ccc; 90 | position: absolute; 91 | } 92 | 93 | /* DEFAULT THEME */ 94 | 95 | .cm-s-default .cm-keyword {color: #708;} 96 | .cm-s-default .cm-atom {color: #219;} 97 | .cm-s-default .cm-number {color: #164;} 98 | .cm-s-default .cm-def {color: #00f;} 99 | .cm-s-default .cm-variable, 100 | .cm-s-default .cm-punctuation, 101 | .cm-s-default .cm-property, 102 | .cm-s-default .cm-operator {} 103 | .cm-s-default .cm-variable-2 {color: #05a;} 104 | .cm-s-default .cm-variable-3 {color: #085;} 105 | .cm-s-default .cm-comment {color: #a50;} 106 | .cm-s-default .cm-string {color: #a11;} 107 | .cm-s-default .cm-string-2 {color: #f50;} 108 | .cm-s-default .cm-meta {color: #555;} 109 | .cm-s-default .cm-qualifier {color: #555;} 110 | .cm-s-default .cm-builtin {color: #30a;} 111 | .cm-s-default .cm-bracket {color: #997;} 112 | .cm-s-default .cm-tag {color: #170;} 113 | .cm-s-default .cm-attribute {color: #00c;} 114 | .cm-s-default .cm-header {color: blue;} 115 | .cm-s-default .cm-quote {color: #090;} 116 | .cm-s-default .cm-hr {color: #999;} 117 | .cm-s-default .cm-link {color: #00c;} 118 | 119 | .cm-negative {color: #d44;} 120 | .cm-positive {color: #292;} 121 | .cm-header, .cm-strong {font-weight: bold;} 122 | .cm-em {font-style: italic;} 123 | .cm-link {text-decoration: underline;} 124 | .cm-strikethrough {text-decoration: line-through;} 125 | 126 | .cm-s-default .cm-error {color: #f00;} 127 | .cm-invalidchar {color: #f00;} 128 | 129 | /* Default styles for common addons */ 130 | 131 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 132 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 133 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 134 | .CodeMirror-activeline-background {background: #e8f2ff;} 135 | 136 | /* STOP */ 137 | 138 | /* The rest of this file contains styles related to the mechanics of 139 | the editor. You probably shouldn't touch them. */ 140 | 141 | .CodeMirror { 142 | position: relative; 143 | overflow: hidden; 144 | background: white; 145 | } 146 | 147 | .CodeMirror-scroll { 148 | overflow: scroll !important; /* Things will break if this is overridden */ 149 | /* 30px is the magic margin used to hide the element's real scrollbars */ 150 | /* See overflow: hidden in .CodeMirror */ 151 | margin-bottom: -30px; margin-right: -30px; 152 | padding-bottom: 30px; 153 | height: 100%; 154 | outline: none; /* Prevent dragging from highlighting the element */ 155 | position: relative; 156 | } 157 | .CodeMirror-sizer { 158 | position: relative; 159 | border-right: 30px solid transparent; 160 | } 161 | 162 | /* The fake, visible scrollbars. Used to force redraw during scrolling 163 | before actuall scrolling happens, thus preventing shaking and 164 | flickering artifacts. */ 165 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 166 | position: absolute; 167 | z-index: 6; 168 | display: none; 169 | } 170 | .CodeMirror-vscrollbar { 171 | right: 0; top: 0; 172 | overflow-x: hidden; 173 | overflow-y: scroll; 174 | } 175 | .CodeMirror-hscrollbar { 176 | bottom: 0; left: 0; 177 | overflow-y: hidden; 178 | overflow-x: scroll; 179 | } 180 | .CodeMirror-scrollbar-filler { 181 | right: 0; bottom: 0; 182 | } 183 | .CodeMirror-gutter-filler { 184 | left: 0; bottom: 0; 185 | } 186 | 187 | .CodeMirror-gutters { 188 | position: absolute; left: 0; top: 0; 189 | z-index: 3; 190 | } 191 | .CodeMirror-gutter { 192 | white-space: normal; 193 | height: 100%; 194 | display: inline-block; 195 | margin-bottom: -30px; 196 | /* Hack to make IE7 behave */ 197 | *zoom:1; 198 | *display:inline; 199 | } 200 | .CodeMirror-gutter-wrapper { 201 | position: absolute; 202 | z-index: 4; 203 | height: 100%; 204 | } 205 | .CodeMirror-gutter-elt { 206 | position: absolute; 207 | cursor: default; 208 | z-index: 4; 209 | } 210 | .CodeMirror-gutter-wrapper { 211 | -webkit-user-select: none; 212 | -moz-user-select: none; 213 | user-select: none; 214 | } 215 | 216 | .CodeMirror-lines { 217 | cursor: text; 218 | min-height: 1px; /* prevents collapsing before first draw */ 219 | } 220 | .CodeMirror pre { 221 | /* Reset some styles that the rest of the page might have set */ 222 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 223 | border-width: 0; 224 | background: transparent; 225 | font-family: inherit; 226 | font-size: inherit; 227 | margin: 0; 228 | white-space: pre; 229 | word-wrap: normal; 230 | line-height: inherit; 231 | color: inherit; 232 | z-index: 2; 233 | position: relative; 234 | overflow: visible; 235 | -webkit-tap-highlight-color: transparent; 236 | } 237 | .CodeMirror-wrap pre { 238 | word-wrap: break-word; 239 | white-space: pre-wrap; 240 | word-break: normal; 241 | } 242 | 243 | .CodeMirror-linebackground { 244 | position: absolute; 245 | left: 0; right: 0; top: 0; bottom: 0; 246 | z-index: 0; 247 | } 248 | 249 | .CodeMirror-linewidget { 250 | position: relative; 251 | z-index: 2; 252 | overflow: auto; 253 | } 254 | 255 | .CodeMirror-widget {} 256 | 257 | .CodeMirror-code { 258 | outline: none; 259 | } 260 | 261 | /* Force content-box sizing for the elements where we expect it */ 262 | .CodeMirror-scroll, 263 | .CodeMirror-sizer, 264 | .CodeMirror-gutter, 265 | .CodeMirror-gutters, 266 | .CodeMirror-linenumber { 267 | -moz-box-sizing: content-box; 268 | box-sizing: content-box; 269 | } 270 | 271 | .CodeMirror-measure { 272 | position: absolute; 273 | width: 100%; 274 | height: 0; 275 | overflow: hidden; 276 | visibility: hidden; 277 | } 278 | .CodeMirror-measure pre { position: static; } 279 | 280 | .CodeMirror div.CodeMirror-cursor { 281 | position: absolute; 282 | border-right: none; 283 | width: 0; 284 | } 285 | 286 | div.CodeMirror-cursors { 287 | visibility: hidden; 288 | position: relative; 289 | z-index: 3; 290 | } 291 | .CodeMirror-focused div.CodeMirror-cursors { 292 | visibility: visible; 293 | } 294 | 295 | .CodeMirror-selected { background: #d9d9d9; } 296 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 297 | .CodeMirror-crosshair { cursor: crosshair; } 298 | .CodeMirror ::selection { background: #d7d4f0; } 299 | .CodeMirror ::-moz-selection { background: #d7d4f0; } 300 | 301 | .cm-searching { 302 | background: #ffa; 303 | background: rgba(255, 255, 0, .4); 304 | } 305 | 306 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 307 | .CodeMirror span { *vertical-align: text-bottom; } 308 | 309 | /* Used to force a border model for a node */ 310 | .cm-force-border { padding-right: .1px; } 311 | 312 | @media print { 313 | /* Hide the cursor when printing */ 314 | .CodeMirror div.CodeMirror-cursors { 315 | visibility: hidden; 316 | } 317 | } 318 | 319 | /* See issue #2901 */ 320 | .cm-tab-wrap-hack:after { content: ''; } 321 | 322 | /* Help users use markselection to safely style text background */ 323 | -------------------------------------------------------------------------------- /examples/deps/ShaderEditor.js: -------------------------------------------------------------------------------- 1 | ( function() { 2 | 3 | function _h( f, c ) { 4 | return function() { 5 | var res = f.apply( this, arguments ); 6 | res = c.apply( this, [ res, arguments ] ) || res; 7 | return res; 8 | } 9 | } 10 | 11 | function _h2( f, c ) { 12 | return function() { 13 | return c.apply( this, arguments ); 14 | } 15 | } 16 | 17 | var references = {}; 18 | function keepReference( f ) { 19 | 20 | references[ f ] = WebGLRenderingContext.prototype[ f ]; 21 | 22 | } 23 | 24 | var _gl = document.createElement( 'canvas' ).getContext( 'webgl' ); 25 | 26 | keepReference( 'getUniformLocation' ); 27 | 28 | function init() { 29 | 30 | var css = document.createElement( 'link' ); 31 | css.rel = 'stylesheet' 32 | css.type = 'text/css'; 33 | css.href = 'deps/CodeMirror.css'; 34 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( css ); 35 | 36 | var css = document.createElement( 'link' ); 37 | css.rel = 'stylesheet' 38 | css.type = 'text/css'; 39 | css.href = 'deps/styles.css'; 40 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( css ); 41 | 42 | var css = document.createElement( 'link' ); 43 | css.rel = 'stylesheet' 44 | css.type = 'text/css'; 45 | css.href = 'http://fonts.googleapis.com/css?family=Droid+Sans+Mono'; 46 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( css ); 47 | 48 | var editor = document.createElement( 'div' ); 49 | editor.setAttribute( 'id', 'shaderEditor' ); 50 | document.body.appendChild( editor ); 51 | this.shaderEditor = editor; 52 | 53 | var p = document.createElement( 'p' ); 54 | p.textContent = 'Programs'; 55 | p.className = 'programs-title'; 56 | editor.appendChild( p ); 57 | 58 | var programList = document.createElement( 'ul' ); 59 | programList.setAttribute( 'id', 'programList' ); 60 | editor.appendChild( programList ); 61 | 62 | var editorContainer = document.createElement( 'ul' ); 63 | editorContainer.setAttribute( 'id', 'editorContainer' ); 64 | editor.appendChild( editorContainer ); 65 | 66 | var p = document.createElement( 'p' ); 67 | p.textContent = 'Vertex Shader'; 68 | p.className = 'vs-title'; 69 | editorContainer.appendChild( p ); 70 | 71 | var p = document.createElement( 'p' ); 72 | p.textContent = 'Fragment Shader'; 73 | p.className = 'fs-title'; 74 | editorContainer.appendChild( p ); 75 | 76 | var js = document.createElement( 'script' ); 77 | js.src = 'deps/CodeMirror.js'; 78 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( js ); 79 | js.addEventListener( 'load', function() { 80 | 81 | var js = document.createElement( 'script' ); 82 | js.src = 'deps/glsl.js'; 83 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( js ); 84 | js.addEventListener( 'load', function() { 85 | 86 | var el = document.createElement( 'style' ); 87 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( el ); 88 | el.textContent = css; 89 | 90 | this.programList = programList; 91 | 92 | var options = { 93 | lineNumbers: true, 94 | matchBrackets: true, 95 | indentWithTabs: false, 96 | tabSize: 4, 97 | indentUnit: 4, 98 | mode: "text/x-glsl", 99 | foldGutter: true, 100 | gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], 101 | }; 102 | 103 | this.vsEditor = CodeMirror( editorContainer, options ), 104 | this.fsEditor = CodeMirror( editorContainer, options ); 105 | this.vsEditor._errors = []; 106 | this.fsEditor._errors = []; 107 | 108 | this.vsEditor.getWrapperElement().setAttribute( 'id', 'vsEditor' ); 109 | this.fsEditor.getWrapperElement().setAttribute( 'id', 'fsEditor' ); 110 | 111 | var keyTimeout = 500; 112 | var vSTimeout; 113 | function scheduleUpdateVS() { 114 | 115 | if( vSTimeout ) vSTimeout = clearTimeout( vSTimeout ); 116 | vSTimeout = setTimeout( update, keyTimeout ); 117 | 118 | } 119 | 120 | var fSTimeout; 121 | function scheduleUpdateFS() { 122 | 123 | if( fSTimeout ) fSTimeout = clearTimeout( fSTimeout ); 124 | fSTimeout = setTimeout( update, keyTimeout ); 125 | 126 | } 127 | 128 | this.vsEditor.on( 'change', scheduleUpdateVS ); 129 | this.fsEditor.on( 'change', scheduleUpdateFS ); 130 | 131 | this.vsEditor.on( 'keyup', scheduleUpdateVS ); 132 | this.fsEditor.on( 'keyup', scheduleUpdateFS ); 133 | 134 | }.bind( this ) ); 135 | 136 | }.bind( this ) ); 137 | 138 | } 139 | 140 | function htmlEncode(str){ 141 | 142 | return String(str) 143 | .replace(/&/g, '&') 144 | .replace(/"/g, '"') 145 | .replace(/'/g, ''') 146 | .replace(//g, '>'); 148 | 149 | } 150 | 151 | function testShader( type, source, code ) { 152 | 153 | if( source === '' ) return; 154 | 155 | var s = _gl.createShader( type ); 156 | _gl.shaderSource( s, source ); 157 | _gl.compileShader( s ); 158 | 159 | while( code._errors.length > 0 ) { 160 | 161 | var mark = code._errors.pop(); 162 | code.removeLineWidget( mark ); 163 | 164 | } 165 | 166 | if ( _gl.getShaderParameter( s, _gl.COMPILE_STATUS ) === false ) { 167 | 168 | var error = _gl.getShaderInfoLog( s ); 169 | 170 | if( error==null ) 171 | { 172 | /*this.mForceFrame = true; 173 | if( fromScript==false ) 174 | { 175 | eleWrapper.className = "errorNo"; 176 | setTimeout(function () { eleWrapper.className = ""; }, 500 ); 177 | }*/ 178 | } 179 | else 180 | { 181 | //eleWrapper.className = "errorYes"; 182 | 183 | var lineOffset = 0;//this.mEffect.GetHeaderSize( this.mActiveDoc ); 184 | var lines = error.match(/^.*((\r\n|\n|\r)|$)/gm); 185 | for( var i=0; i1 && parts[0]!="Warning") 199 | { 200 | console.log( parts.length + " **" + lines[i] ); 201 | 202 | var txt = ""; 203 | if( parts.length==4 ) 204 | txt = parts[2] + " : " + parts[3]; 205 | else 206 | txt = "Unknown error"; 207 | 208 | var msg = document.createElement("div"); 209 | msg.appendChild(document.createTextNode( txt )); 210 | msg.className = "errorMessage"; 211 | var mark = code.addLineWidget( 0, msg, {coverGutter: false, noHScroll: true, above: true} ); 212 | code._errors.push( mark ); 213 | 214 | } 215 | } 216 | } 217 | // console.error( 'Shader couldn\'t compile.' ); 218 | return false; 219 | 220 | } 221 | 222 | if ( _gl.getShaderInfoLog( s ) !== '' ) { 223 | 224 | console.error( '_gl.getShaderInfoLog()', _gl.getShaderInfoLog( s ) ); 225 | return false; 226 | 227 | } 228 | 229 | return true; 230 | 231 | } 232 | 233 | init(); 234 | 235 | this.programs = []; 236 | this.shaders = []; 237 | var uniforms = []; 238 | var currentProgram = null; 239 | 240 | function findProgram( p ) { 241 | 242 | var f = null; 243 | programs.forEach( function( e ) { 244 | if( e.program === p ) f = e; 245 | } ); 246 | return f; 247 | 248 | } 249 | 250 | function findShader( s ) { 251 | 252 | var f = null; 253 | 254 | shaders.forEach( function( e ) { 255 | if( e.shader === s ) { 256 | f = e; 257 | } 258 | } ); 259 | 260 | return f; 261 | 262 | } 263 | 264 | function addProgram( gl, p ) { 265 | 266 | this.shaderEditor.style.display = 'block'; 267 | 268 | var li = document.createElement( 'li' ); 269 | li.textContent = 'Program'; 270 | 271 | var el = { gl: gl, program: p, shaders: [], li: li }; 272 | 273 | li.addEventListener( 'click', function( e ) { 274 | selectProgram( el ); 275 | e.preventDefault(); 276 | } ); 277 | 278 | programs.push( el ); 279 | 280 | this.programList.appendChild( li ); 281 | 282 | } 283 | 284 | function selectProgram( p ) { 285 | 286 | currentProgram = p; 287 | this.vsEditor.setValue( p.shaders[ 0 ].source ); 288 | this.vsEditor.refresh(); 289 | 290 | this.fsEditor.setValue( p.shaders[ 1 ].source ); 291 | this.fsEditor.refresh(); 292 | 293 | } 294 | 295 | function update() { 296 | 297 | if( !currentProgram ) return; 298 | 299 | var gl = currentProgram.gl, 300 | program = currentProgram.program, 301 | vertexShader = currentProgram.shaders[ 0 ].shader, 302 | fragmentShader = currentProgram.shaders[ 1 ].shader; 303 | 304 | if( currentProgram.shaders[ 0 ].type === gl.VERTEX_SHADER ) vertexShader = currentProgram.shaders[ 0 ].shader; 305 | if( currentProgram.shaders[ 0 ].type === gl.FRAGMENT_SHADER ) fragmentShader = currentProgram.shaders[ 0 ].shader; 306 | if( currentProgram.shaders[ 1 ].type === gl.VERTEX_SHADER ) vertexShader = currentProgram.shaders[ 1 ].shader; 307 | if( currentProgram.shaders[ 1 ].type === gl.FRAGMENT_SHADER ) fragmentShader = currentProgram.shaders[ 1 ].shader; 308 | 309 | //gl.detachShader( program, vertexShader ); 310 | //gl.detachShader( program, fragmentShader ); 311 | 312 | //vertexShader = gl.createShader( gl.VERTEX_SHADER ); 313 | //fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); 314 | 315 | if( !testShader( gl.VERTEX_SHADER, this.vsEditor.getValue(), this.vsEditor ) ) return false; 316 | if( !testShader( gl.FRAGMENT_SHADER, this.fsEditor.getValue(), this.fsEditor ) ) return false; 317 | 318 | gl.shaderSource( vertexShader, this.vsEditor.getValue() ); 319 | gl.shaderSource( fragmentShader, this.fsEditor.getValue() ); 320 | 321 | gl.compileShader( vertexShader ); 322 | 323 | if ( gl.getShaderParameter( vertexShader, gl.COMPILE_STATUS ) === false ) { 324 | 325 | console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); 326 | 327 | } 328 | 329 | if ( gl.getShaderInfoLog( vertexShader ) !== '' ) { 330 | 331 | console.error( 'THREE.WebGLShader:', 'gl.getShaderInfoLog()', gl.getShaderInfoLog( vertexShader ) ); 332 | 333 | } 334 | 335 | gl.compileShader( fragmentShader ); 336 | 337 | 338 | if ( gl.getShaderParameter( fragmentShader, gl.COMPILE_STATUS ) === false ) { 339 | 340 | console.error( 'Shader couldn\'t compile.' ); 341 | 342 | } 343 | 344 | if ( gl.getShaderInfoLog( fragmentShader ) !== '' ) { 345 | 346 | console.error( 'gl.getShaderInfoLog()', gl.getShaderInfoLog( fragmentShader ) ); 347 | 348 | } 349 | 350 | gl.attachShader( program, vertexShader ); 351 | gl.attachShader( program, fragmentShader ); 352 | 353 | gl.linkProgram( program ); 354 | 355 | if( !gl.getProgramParameter(program,gl.LINK_STATUS) ) { 356 | var infoLog = gl.getProgramInfoLog(program); 357 | //gl.deleteProgram( program ); 358 | console.log( infoLog ); 359 | } 360 | 361 | console.log( 'update' ); 362 | 363 | } 364 | 365 | WebGLRenderingContext.prototype.createShader = _h( 366 | WebGLRenderingContext.prototype.createShader, 367 | function( res, args ) { 368 | 369 | console.log( 'createShader', args ); 370 | shaders.push( { shader: res, type: args[ 0 ] } ); 371 | 372 | } 373 | ); 374 | 375 | 376 | WebGLRenderingContext.prototype.shaderSource = _h( 377 | WebGLRenderingContext.prototype.shaderSource, 378 | function( res, args ) { 379 | 380 | console.log( 'shaderSource', args ); 381 | var s = findShader( args[ 0 ] ); 382 | s.source = args[ 1 ]; 383 | 384 | } 385 | ); 386 | 387 | WebGLRenderingContext.prototype.compileShader = _h( 388 | WebGLRenderingContext.prototype.compileShader, 389 | function( res, args ) { 390 | 391 | console.log( 'compileShader', args ); 392 | 393 | } 394 | ); 395 | 396 | WebGLRenderingContext.prototype.createProgram = _h( 397 | WebGLRenderingContext.prototype.createProgram, 398 | function( res, args ) { 399 | 400 | console.log( 'createProgram', res, args ); 401 | addProgram( this, res ); 402 | 403 | } 404 | ); 405 | 406 | WebGLRenderingContext.prototype.attachShader = _h( 407 | WebGLRenderingContext.prototype.attachShader, 408 | function( res, args ) { 409 | 410 | console.log( 'attachShader', args ); 411 | var p = findProgram( args[ 0 ] ); 412 | var s = findShader( args[ 1 ] ); 413 | p.shaders.push( s ); 414 | 415 | } 416 | ); 417 | 418 | WebGLRenderingContext.prototype.linkProgram = _h( 419 | WebGLRenderingContext.prototype.linkProgram, 420 | function( res, args ) { 421 | 422 | console.log( 'linkProgram', args ); 423 | 424 | } 425 | ); 426 | 427 | WebGLRenderingContext.prototype.getUniformLocation = _h( 428 | WebGLRenderingContext.prototype.getUniformLocation, 429 | function( res, args ) { 430 | 431 | uniforms.push( { 432 | program: args[ 0 ], 433 | uniform: args[ 1 ], 434 | location: res, 435 | gl: this 436 | } ); 437 | console.log( 'getUniformLocation', res, args ); 438 | 439 | } 440 | ); 441 | 442 | function findProgramByLocation( location ) { 443 | 444 | var f = null; 445 | 446 | uniforms.forEach( function( e ) { 447 | if( e.location === location ) { 448 | f = e; 449 | } 450 | } ); 451 | 452 | return f; 453 | 454 | } 455 | 456 | var methods = [ 457 | 'uniform1f', 'uniform1fv', 'uniform1i', 'uniform1iv', 458 | 'uniform2f', 'uniform2fv', 'uniform2i', 'uniform2iv', 459 | 'uniform3f', 'uniform3fv', 'uniform3i', 'uniform3iv', 460 | 'uniform4f', 'uniform4fv', 'uniform4i', 'uniform4iv', 461 | 'uniformMatrix2fv', 'uniformMatrix3fv', 'uniformMatrix4fv' 462 | ]; 463 | 464 | methods.forEach( function( f ) { 465 | 466 | keepReference( f ); 467 | 468 | WebGLRenderingContext.prototype[ f ] = function() { 469 | 470 | var args = arguments; 471 | var p = findProgramByLocation( args[ 0 ] ); 472 | if( p ) { 473 | var l = references.getUniformLocation.apply( p.gl, [ p.program, p.uniform ] ); 474 | var a = []; 475 | a.push( l ); 476 | for( var j = 1; j < args.length; j++ ) { 477 | a.push( args[ j ] ); 478 | } 479 | references[ f ].apply( p.gl, a ); 480 | } 481 | 482 | } 483 | 484 | } ); 485 | 486 | /*WebGLRenderingContext.prototype.useProgram = _h( 487 | WebGLRenderingContext.prototype.useProgram, 488 | function( res, args ) { 489 | 490 | console.log( 'useProgram', args ); 491 | 492 | } 493 | );*/ 494 | 495 | } )(); -------------------------------------------------------------------------------- /examples/deps/glsl.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("glsl", function(config, parserConfig) { 2 | var indentUnit = config.indentUnit, 3 | keywords = parserConfig.keywords || {}, 4 | builtins = parserConfig.builtins || {}, 5 | blockKeywords = parserConfig.blockKeywords || {}, 6 | atoms = parserConfig.atoms || {}, 7 | hooks = parserConfig.hooks || {}, 8 | multiLineStrings = parserConfig.multiLineStrings; 9 | var isOperatorChar = /[+\-*&%=<>!?|\/]/; 10 | 11 | var curPunc; 12 | 13 | function tokenBase(stream, state) { 14 | var ch = stream.next(); 15 | if (hooks[ch]) { 16 | var result = hooks[ch](stream, state); 17 | if (result !== false) return result; 18 | } 19 | if (ch == '"' || ch == "'") { 20 | state.tokenize = tokenString(ch); 21 | return state.tokenize(stream, state); 22 | } 23 | if (/[\[\]{}\(\),;\:\.]/.test(ch)) { 24 | curPunc = ch; 25 | return "bracket"; 26 | } 27 | if (/\d/.test(ch)) { 28 | stream.eatWhile(/[\w\.]/); 29 | return "number"; 30 | } 31 | if (ch == "/") { 32 | if (stream.eat("*")) { 33 | state.tokenize = tokenComment; 34 | return tokenComment(stream, state); 35 | } 36 | if (stream.eat("/")) { 37 | stream.skipToEnd(); 38 | return "comment"; 39 | } 40 | } 41 | if (isOperatorChar.test(ch)) { 42 | stream.eatWhile(isOperatorChar); 43 | return "operator"; 44 | } 45 | stream.eatWhile(/[\w\$_]/); 46 | var cur = stream.current(); 47 | if (keywords.propertyIsEnumerable(cur)) { 48 | if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; 49 | return "keyword"; 50 | } 51 | if (builtins.propertyIsEnumerable(cur)) { 52 | return "builtin"; 53 | } 54 | if (atoms.propertyIsEnumerable(cur)) return "atom"; 55 | return "word"; 56 | } 57 | 58 | function tokenString(quote) { 59 | return function(stream, state) { 60 | var escaped = false, next, end = false; 61 | while ((next = stream.next()) != null) { 62 | if (next == quote && !escaped) {end = true; break;} 63 | escaped = !escaped && next == "\\"; 64 | } 65 | if (end || !(escaped || multiLineStrings)) 66 | state.tokenize = tokenBase; 67 | return "string"; 68 | }; 69 | } 70 | 71 | function tokenComment(stream, state) { 72 | var maybeEnd = false, ch; 73 | while (ch = stream.next()) { 74 | if (ch == "/" && maybeEnd) { 75 | state.tokenize = tokenBase; 76 | break; 77 | } 78 | maybeEnd = (ch == "*"); 79 | } 80 | return "comment"; 81 | } 82 | 83 | function Context(indented, column, type, align, prev) { 84 | this.indented = indented; 85 | this.column = column; 86 | this.type = type; 87 | this.align = align; 88 | this.prev = prev; 89 | } 90 | function pushContext(state, col, type) { 91 | return state.context = new Context(state.indented, col, type, null, state.context); 92 | } 93 | function popContext(state) { 94 | var t = state.context.type; 95 | if (t == ")" || t == "]" || t == "}") 96 | state.indented = state.context.indented; 97 | return state.context = state.context.prev; 98 | } 99 | 100 | // Interface 101 | 102 | return { 103 | startState: function(basecolumn) { 104 | return { 105 | tokenize: null, 106 | context: new Context((basecolumn || 0) - indentUnit, 0, "top", false), 107 | indented: 0, 108 | startOfLine: true 109 | }; 110 | }, 111 | 112 | token: function(stream, state) { 113 | var ctx = state.context; 114 | if (stream.sol()) { 115 | if (ctx.align == null) ctx.align = false; 116 | state.indented = stream.indentation(); 117 | state.startOfLine = true; 118 | } 119 | if (stream.eatSpace()) return null; 120 | curPunc = null; 121 | var style = (state.tokenize || tokenBase)(stream, state); 122 | if (style == "comment" || style == "meta") return style; 123 | if (ctx.align == null) ctx.align = true; 124 | 125 | if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state); 126 | else if (curPunc == "{") pushContext(state, stream.column(), "}"); 127 | else if (curPunc == "[") pushContext(state, stream.column(), "]"); 128 | else if (curPunc == "(") pushContext(state, stream.column(), ")"); 129 | else if (curPunc == "}") { 130 | while (ctx.type == "statement") ctx = popContext(state); 131 | if (ctx.type == "}") ctx = popContext(state); 132 | while (ctx.type == "statement") ctx = popContext(state); 133 | } 134 | else if (curPunc == ctx.type) popContext(state); 135 | else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement")) 136 | pushContext(state, stream.column(), "statement"); 137 | state.startOfLine = false; 138 | return style; 139 | }, 140 | 141 | indent: function(state, textAfter) { 142 | if (state.tokenize != tokenBase && state.tokenize != null) return 0; 143 | var firstChar = textAfter && textAfter.charAt(0), ctx = state.context, closing = firstChar == ctx.type; 144 | if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : indentUnit); 145 | else if (ctx.align) return ctx.column + (closing ? 0 : 1); 146 | else return ctx.indented + (closing ? 0 : indentUnit); 147 | }, 148 | 149 | electricChars: "{}" 150 | }; 151 | }); 152 | 153 | (function() { 154 | function words(str) { 155 | var obj = {}, words = str.split(" "); 156 | for (var i = 0; i < words.length; ++i) obj[words[i]] = true; 157 | return obj; 158 | } 159 | var glslKeywords = "attribute const uniform varying break continue " + 160 | "do for while if else in out inout float int void bool true false " + 161 | "lowp mediump highp precision invariant discard return mat2 mat3 " + 162 | "mat4 vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 sampler2D " + 163 | "samplerCube struct gl_FragCoord gl_FragColor"; 164 | var glslBuiltins = "radians degrees sin cos tan asin acos atan pow " + 165 | "exp log exp2 log2 sqrt inversesqrt abs sign floor ceil fract mod " + 166 | "min max clamp mix step smoothstep length distance dot cross " + 167 | "normalize faceforward reflect refract matrixCompMult lessThan " + 168 | "lessThanEqual greaterThan greaterThanEqual equal notEqual any all " + 169 | "not dFdx dFdy fwidth texture2D texture2DProj texture2DLod " + 170 | "texture2DProjLod textureCube textureCubeLod"; 171 | 172 | function cppHook(stream, state) { 173 | if (!state.startOfLine) return false; 174 | stream.skipToEnd(); 175 | return "meta"; 176 | } 177 | 178 | // C#-style strings where "" escapes a quote. 179 | function tokenAtString(stream, state) { 180 | var next; 181 | while ((next = stream.next()) != null) { 182 | if (next == '"' && !stream.eat('"')) { 183 | state.tokenize = null; 184 | break; 185 | } 186 | } 187 | return "string"; 188 | } 189 | 190 | CodeMirror.defineMIME("text/x-glsl", { 191 | name: "glsl", 192 | keywords: words(glslKeywords), 193 | builtins: words(glslBuiltins), 194 | blockKeywords: words("case do else for if switch while struct"), 195 | atoms: words("null"), 196 | hooks: {"#": cppHook} 197 | }); 198 | }()); -------------------------------------------------------------------------------- /examples/deps/styles.css: -------------------------------------------------------------------------------- 1 | #shaderEditor *{ font-family: 'Droid Sans Mono', monospace; font-size: 12px;} 2 | #shaderEditor{ box-sizing: border-box; position: absolute; left: 0; top: 0; right: 0; bottom: 0; display: none; opacity: 1;} 3 | #shaderEditor:hover{ opacity: 1 ;} 4 | #shaderEditor #programList{ position: absolute; left: 0; top: 30px; width: 200px; bottom: 0; border-right: 1px solid #dedede;} 5 | #shaderEditor #programList li{ padding: 10px; border-top: 1px solid #dedede; border-bottom: 1px solid #404040; width: 100%; cursor: pointer;} 6 | #shaderEditor #editorContainer{ position: absolute; left: 200px; top: 0; right: 0; bottom: 0 ;} 7 | #shaderEditor .cm-s-default{ position: absolute; top: 30px; bottom: 0; height: calc(100% - 30px); width: 50% ;} 8 | #shaderEditor #vsEditor, #shaderEditor #fsEditor{ border-top: 1px solid #dedede; border-right: 1px solid #dedede;} 9 | #shaderEditor #vsEditor{ left: 0; } 10 | #shaderEditor #fsEditor{ left: 50%; } 11 | 12 | #shaderEditor .programs-title, #shaderEditor .vs-title, #shaderEditor .fs-title{ left: 0; position: absolute; padding: 10px 10px 10px 35px } 13 | #shaderEditor .vs-title{ left: 0;} 14 | #shaderEditor .fs-title{ left: 50%;} 15 | 16 | .cm-s-default{ background-color: rgba( 0,0,0,.5); color: white;} 17 | .cm-s-default *{ color: white; background-color: transparent;} 18 | 19 | .cm-s-default span.cm-keyword {color: #bbf;} 20 | .cm-s-default span.cm-number {color: #fbb;} 21 | .cm-s-default span.cm-def {color: #55f;} 22 | .cm-s-default span.cm-comment {color: #8ff;} 23 | .cm-s-default span.cm-operator {color: #5b5;} 24 | .cm-s-default span.cm-meta {color: #8f8;} 25 | .cm-s-default span.cm-qualifier {color: #888;} 26 | .cm-s-default span.cm-builtin {color: #fbf;} 27 | .cm-s-default span.cm-bracket {color: #bbb;} 28 | 29 | .errorMessage{ background-color: red;} -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Shader Editor 6 | 7 | 8 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 85 | -------------------------------------------------------------------------------- /src/ShaderEditor.js: -------------------------------------------------------------------------------- 1 | ( function() { 2 | 3 | function _h( f, c ) { 4 | return function() { 5 | var res = f.apply( this, arguments ); 6 | res = c.apply( this, [ res, arguments ] ) || res; 7 | return res; 8 | } 9 | } 10 | 11 | function _h2( f, c ) { 12 | return function() { 13 | return c.apply( this, arguments ); 14 | } 15 | } 16 | 17 | var references = {}; 18 | function keepReference( f ) { 19 | 20 | references[ f ] = WebGLRenderingContext.prototype[ f ]; 21 | 22 | } 23 | 24 | var _gl = document.createElement( 'canvas' ).getContext( 'webgl' ); 25 | 26 | keepReference( 'getUniformLocation' ); 27 | 28 | function init() { 29 | 30 | var css = document.createElement( 'link' ); 31 | css.rel = 'stylesheet' 32 | css.type = 'text/css'; 33 | css.href = 'deps/CodeMirror.css'; 34 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( css ); 35 | 36 | var css = document.createElement( 'link' ); 37 | css.rel = 'stylesheet' 38 | css.type = 'text/css'; 39 | css.href = 'deps/styles.css'; 40 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( css ); 41 | 42 | var css = document.createElement( 'link' ); 43 | css.rel = 'stylesheet' 44 | css.type = 'text/css'; 45 | css.href = 'http://fonts.googleapis.com/css?family=Droid+Sans+Mono'; 46 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( css ); 47 | 48 | var editor = document.createElement( 'div' ); 49 | editor.setAttribute( 'id', 'shaderEditor' ); 50 | document.body.appendChild( editor ); 51 | this.shaderEditor = editor; 52 | 53 | var p = document.createElement( 'p' ); 54 | p.textContent = 'Programs'; 55 | p.className = 'programs-title'; 56 | editor.appendChild( p ); 57 | 58 | var programList = document.createElement( 'ul' ); 59 | programList.setAttribute( 'id', 'programList' ); 60 | editor.appendChild( programList ); 61 | 62 | var editorContainer = document.createElement( 'ul' ); 63 | editorContainer.setAttribute( 'id', 'editorContainer' ); 64 | editor.appendChild( editorContainer ); 65 | 66 | var p = document.createElement( 'p' ); 67 | p.textContent = 'Vertex Shader'; 68 | p.className = 'vs-title'; 69 | editorContainer.appendChild( p ); 70 | 71 | var p = document.createElement( 'p' ); 72 | p.textContent = 'Fragment Shader'; 73 | p.className = 'fs-title'; 74 | editorContainer.appendChild( p ); 75 | 76 | var js = document.createElement( 'script' ); 77 | js.src = 'deps/CodeMirror.js'; 78 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( js ); 79 | js.addEventListener( 'load', function() { 80 | 81 | var js = document.createElement( 'script' ); 82 | js.src = 'deps/glsl.js'; 83 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( js ); 84 | js.addEventListener( 'load', function() { 85 | 86 | var el = document.createElement( 'style' ); 87 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( el ); 88 | el.textContent = css; 89 | 90 | this.programList = programList; 91 | 92 | var options = { 93 | lineNumbers: true, 94 | matchBrackets: true, 95 | indentWithTabs: false, 96 | tabSize: 4, 97 | indentUnit: 4, 98 | mode: "text/x-glsl", 99 | foldGutter: true, 100 | gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], 101 | }; 102 | 103 | this.vsEditor = CodeMirror( editorContainer, options ), 104 | this.fsEditor = CodeMirror( editorContainer, options ); 105 | this.vsEditor._errors = []; 106 | this.fsEditor._errors = []; 107 | 108 | this.vsEditor.getWrapperElement().setAttribute( 'id', 'vsEditor' ); 109 | this.fsEditor.getWrapperElement().setAttribute( 'id', 'fsEditor' ); 110 | 111 | var keyTimeout = 500; 112 | var vSTimeout; 113 | function scheduleUpdateVS() { 114 | 115 | if( vSTimeout ) vSTimeout = clearTimeout( vSTimeout ); 116 | vSTimeout = setTimeout( update, keyTimeout ); 117 | 118 | } 119 | 120 | var fSTimeout; 121 | function scheduleUpdateFS() { 122 | 123 | if( fSTimeout ) fSTimeout = clearTimeout( fSTimeout ); 124 | fSTimeout = setTimeout( update, keyTimeout ); 125 | 126 | } 127 | 128 | this.vsEditor.on( 'change', scheduleUpdateVS ); 129 | this.fsEditor.on( 'change', scheduleUpdateFS ); 130 | 131 | this.vsEditor.on( 'keyup', scheduleUpdateVS ); 132 | this.fsEditor.on( 'keyup', scheduleUpdateFS ); 133 | 134 | }.bind( this ) ); 135 | 136 | }.bind( this ) ); 137 | 138 | } 139 | 140 | function htmlEncode(str){ 141 | 142 | return String(str) 143 | .replace(/&/g, '&') 144 | .replace(/"/g, '"') 145 | .replace(/'/g, ''') 146 | .replace(//g, '>'); 148 | 149 | } 150 | 151 | function testShader( type, source, code ) { 152 | 153 | if( source === '' ) return; 154 | 155 | var s = _gl.createShader( type ); 156 | _gl.shaderSource( s, source ); 157 | _gl.compileShader( s ); 158 | 159 | while( code._errors.length > 0 ) { 160 | 161 | var mark = code._errors.pop(); 162 | code.removeLineWidget( mark ); 163 | 164 | } 165 | 166 | if ( _gl.getShaderParameter( s, _gl.COMPILE_STATUS ) === false ) { 167 | 168 | var error = _gl.getShaderInfoLog( s ); 169 | 170 | if( error==null ) 171 | { 172 | /*this.mForceFrame = true; 173 | if( fromScript==false ) 174 | { 175 | eleWrapper.className = "errorNo"; 176 | setTimeout(function () { eleWrapper.className = ""; }, 500 ); 177 | }*/ 178 | } 179 | else 180 | { 181 | //eleWrapper.className = "errorYes"; 182 | 183 | var lineOffset = 0;//this.mEffect.GetHeaderSize( this.mActiveDoc ); 184 | var lines = error.match(/^.*((\r\n|\n|\r)|$)/gm); 185 | for( var i=0; i1 && parts[0]!="Warning") 199 | { 200 | console.log( parts.length + " **" + lines[i] ); 201 | 202 | var txt = ""; 203 | if( parts.length==4 ) 204 | txt = parts[2] + " : " + parts[3]; 205 | else 206 | txt = "Unknown error"; 207 | 208 | var msg = document.createElement("div"); 209 | msg.appendChild(document.createTextNode( txt )); 210 | msg.className = "errorMessage"; 211 | var mark = code.addLineWidget( 0, msg, {coverGutter: false, noHScroll: true, above: true} ); 212 | code._errors.push( mark ); 213 | 214 | } 215 | } 216 | } 217 | // console.error( 'Shader couldn\'t compile.' ); 218 | return false; 219 | 220 | } 221 | 222 | if ( _gl.getShaderInfoLog( s ) !== '' ) { 223 | 224 | console.error( '_gl.getShaderInfoLog()', _gl.getShaderInfoLog( s ) ); 225 | return false; 226 | 227 | } 228 | 229 | return true; 230 | 231 | } 232 | 233 | init(); 234 | 235 | this.programs = []; 236 | this.shaders = []; 237 | var uniforms = []; 238 | var currentProgram = null; 239 | 240 | function findProgram( p ) { 241 | 242 | var f = null; 243 | programs.forEach( function( e ) { 244 | if( e.program === p ) f = e; 245 | } ); 246 | return f; 247 | 248 | } 249 | 250 | function findShader( s ) { 251 | 252 | var f = null; 253 | 254 | shaders.forEach( function( e ) { 255 | if( e.shader === s ) { 256 | f = e; 257 | } 258 | } ); 259 | 260 | return f; 261 | 262 | } 263 | 264 | function addProgram( gl, p ) { 265 | 266 | this.shaderEditor.style.display = 'block'; 267 | 268 | var li = document.createElement( 'li' ); 269 | li.textContent = 'Program'; 270 | 271 | var el = { gl: gl, program: p, shaders: [], li: li }; 272 | 273 | li.addEventListener( 'click', function( e ) { 274 | selectProgram( el ); 275 | e.preventDefault(); 276 | } ); 277 | 278 | programs.push( el ); 279 | 280 | this.programList.appendChild( li ); 281 | 282 | } 283 | 284 | function selectProgram( p ) { 285 | 286 | currentProgram = p; 287 | this.vsEditor.setValue( p.shaders[ 0 ].source ); 288 | this.vsEditor.refresh(); 289 | 290 | this.fsEditor.setValue( p.shaders[ 1 ].source ); 291 | this.fsEditor.refresh(); 292 | 293 | } 294 | 295 | function update() { 296 | 297 | if( !currentProgram ) return; 298 | 299 | var gl = currentProgram.gl, 300 | program = currentProgram.program, 301 | vertexShader = currentProgram.shaders[ 0 ].shader, 302 | fragmentShader = currentProgram.shaders[ 1 ].shader; 303 | 304 | if( currentProgram.shaders[ 0 ].type === gl.VERTEX_SHADER ) vertexShader = currentProgram.shaders[ 0 ].shader; 305 | if( currentProgram.shaders[ 0 ].type === gl.FRAGMENT_SHADER ) fragmentShader = currentProgram.shaders[ 0 ].shader; 306 | if( currentProgram.shaders[ 1 ].type === gl.VERTEX_SHADER ) vertexShader = currentProgram.shaders[ 1 ].shader; 307 | if( currentProgram.shaders[ 1 ].type === gl.FRAGMENT_SHADER ) fragmentShader = currentProgram.shaders[ 1 ].shader; 308 | 309 | //gl.detachShader( program, vertexShader ); 310 | //gl.detachShader( program, fragmentShader ); 311 | 312 | //vertexShader = gl.createShader( gl.VERTEX_SHADER ); 313 | //fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); 314 | 315 | if( !testShader( gl.VERTEX_SHADER, this.vsEditor.getValue(), this.vsEditor ) ) return false; 316 | if( !testShader( gl.FRAGMENT_SHADER, this.fsEditor.getValue(), this.fsEditor ) ) return false; 317 | 318 | gl.shaderSource( vertexShader, this.vsEditor.getValue() ); 319 | gl.shaderSource( fragmentShader, this.fsEditor.getValue() ); 320 | 321 | gl.compileShader( vertexShader ); 322 | 323 | if ( gl.getShaderParameter( vertexShader, gl.COMPILE_STATUS ) === false ) { 324 | 325 | console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); 326 | 327 | } 328 | 329 | if ( gl.getShaderInfoLog( vertexShader ) !== '' ) { 330 | 331 | console.error( 'THREE.WebGLShader:', 'gl.getShaderInfoLog()', gl.getShaderInfoLog( vertexShader ) ); 332 | 333 | } 334 | 335 | gl.compileShader( fragmentShader ); 336 | 337 | 338 | if ( gl.getShaderParameter( fragmentShader, gl.COMPILE_STATUS ) === false ) { 339 | 340 | console.error( 'Shader couldn\'t compile.' ); 341 | 342 | } 343 | 344 | if ( gl.getShaderInfoLog( fragmentShader ) !== '' ) { 345 | 346 | console.error( 'gl.getShaderInfoLog()', gl.getShaderInfoLog( fragmentShader ) ); 347 | 348 | } 349 | 350 | gl.attachShader( program, vertexShader ); 351 | gl.attachShader( program, fragmentShader ); 352 | 353 | gl.linkProgram( program ); 354 | 355 | if( !gl.getProgramParameter(program,gl.LINK_STATUS) ) { 356 | var infoLog = gl.getProgramInfoLog(program); 357 | //gl.deleteProgram( program ); 358 | console.log( infoLog ); 359 | } 360 | 361 | console.log( 'update' ); 362 | 363 | } 364 | 365 | WebGLRenderingContext.prototype.createShader = _h( 366 | WebGLRenderingContext.prototype.createShader, 367 | function( res, args ) { 368 | 369 | console.log( 'createShader', args ); 370 | shaders.push( { shader: res, type: args[ 0 ] } ); 371 | 372 | } 373 | ); 374 | 375 | 376 | WebGLRenderingContext.prototype.shaderSource = _h( 377 | WebGLRenderingContext.prototype.shaderSource, 378 | function( res, args ) { 379 | 380 | console.log( 'shaderSource', args ); 381 | var s = findShader( args[ 0 ] ); 382 | s.source = args[ 1 ]; 383 | 384 | } 385 | ); 386 | 387 | WebGLRenderingContext.prototype.compileShader = _h( 388 | WebGLRenderingContext.prototype.compileShader, 389 | function( res, args ) { 390 | 391 | console.log( 'compileShader', args ); 392 | 393 | } 394 | ); 395 | 396 | WebGLRenderingContext.prototype.createProgram = _h( 397 | WebGLRenderingContext.prototype.createProgram, 398 | function( res, args ) { 399 | 400 | console.log( 'createProgram', res, args ); 401 | addProgram( this, res ); 402 | 403 | } 404 | ); 405 | 406 | WebGLRenderingContext.prototype.attachShader = _h( 407 | WebGLRenderingContext.prototype.attachShader, 408 | function( res, args ) { 409 | 410 | console.log( 'attachShader', args ); 411 | var p = findProgram( args[ 0 ] ); 412 | var s = findShader( args[ 1 ] ); 413 | p.shaders.push( s ); 414 | 415 | } 416 | ); 417 | 418 | WebGLRenderingContext.prototype.linkProgram = _h( 419 | WebGLRenderingContext.prototype.linkProgram, 420 | function( res, args ) { 421 | 422 | console.log( 'linkProgram', args ); 423 | 424 | } 425 | ); 426 | 427 | WebGLRenderingContext.prototype.getUniformLocation = _h( 428 | WebGLRenderingContext.prototype.getUniformLocation, 429 | function( res, args ) { 430 | 431 | uniforms.push( { 432 | program: args[ 0 ], 433 | uniform: args[ 1 ], 434 | location: res, 435 | gl: this 436 | } ); 437 | console.log( 'getUniformLocation', res, args ); 438 | 439 | } 440 | ); 441 | 442 | function findProgramByLocation( location ) { 443 | 444 | var f = null; 445 | 446 | uniforms.forEach( function( e ) { 447 | if( e.location === location ) { 448 | f = e; 449 | } 450 | } ); 451 | 452 | return f; 453 | 454 | } 455 | 456 | var methods = [ 457 | 'uniform1f', 'uniform1fv', 'uniform1i', 'uniform1iv', 458 | 'uniform2f', 'uniform2fv', 'uniform2i', 'uniform2iv', 459 | 'uniform3f', 'uniform3fv', 'uniform3i', 'uniform3iv', 460 | 'uniform4f', 'uniform4fv', 'uniform4i', 'uniform4iv', 461 | 'uniformMatrix2fv', 'uniformMatrix3fv', 'uniformMatrix4fv' 462 | ]; 463 | 464 | methods.forEach( function( f ) { 465 | 466 | keepReference( f ); 467 | 468 | WebGLRenderingContext.prototype[ f ] = function() { 469 | 470 | var args = arguments; 471 | var p = findProgramByLocation( args[ 0 ] ); 472 | if( p ) { 473 | var l = references.getUniformLocation.apply( p.gl, [ p.program, p.uniform ] ); 474 | var a = []; 475 | a.push( l ); 476 | for( var j = 1; j < args.length; j++ ) { 477 | a.push( args[ j ] ); 478 | } 479 | references[ f ].apply( p.gl, a ); 480 | } 481 | 482 | } 483 | 484 | } ); 485 | 486 | /*WebGLRenderingContext.prototype.useProgram = _h( 487 | WebGLRenderingContext.prototype.useProgram, 488 | function( res, args ) { 489 | 490 | console.log( 'useProgram', args ); 491 | 492 | } 493 | );*/ 494 | 495 | } )(); --------------------------------------------------------------------------------