├── .gitignore ├── LICENSE ├── RAYGL.md ├── README.md ├── assets ├── README.md └── soundcloud.png ├── dist └── rayglider.js ├── favicon.png ├── index.js ├── jardi.jpg ├── lib └── codemirror.css ├── package.json ├── r.html ├── run.html ├── shaders └── lib │ ├── anoise │ ├── blur │ ├── camera │ ├── color │ ├── debug │ ├── fxaa │ ├── hg_sdf │ ├── iq_sdf │ ├── noise │ ├── common │ ├── periodic2D │ ├── periodic3D │ ├── periodic4D │ ├── perlin2D │ ├── perlin3D │ ├── perlin4D │ ├── simplex2D │ ├── simplex3D │ ├── simplex4D │ ├── worley2D │ ├── worley3D │ └── worley4D │ ├── pack │ ├── phong │ ├── sdf │ ├── terrain │ ├── tes2.7z │ ├── tes2.txt │ ├── test.txt │ └── transform └── src ├── asset.js ├── boom.js ├── config.js ├── glsl.js ├── mouse.js ├── pads.js ├── pass.js ├── state.js ├── track.js ├── ui.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .cache 3 | .vscode 4 | *.swp 5 | build 6 | gh-pages/ 7 | node_modules 8 | npm-debug.log* 9 | package-lock.json 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Yannis Gravezas 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 | -------------------------------------------------------------------------------- /RAYGL.md: -------------------------------------------------------------------------------- 1 | # RayGL 2 | 3 | RayGL makes use of special prefixes and directives. THe main script and all dependencies are composed in a single uber shader(Multi pass is controlled with conditional defines and the pass directive). If you want to debug the final glsl output, for now, just make a small typo and click "Apply"(Shortcuts are Alt-A and Alt-Enter). The side editor will show the final shader text along with the error message. 4 | 5 | All scripts must use "$main(vec4 outputColor, vec2 fragCoords)" as the entry point for the shader execution(The one from the currently active script will be used). The use of the "$" character does the namespacing to prevent conflicts and allow for variable/function reuse in other scripts. 6 | 7 | Code modularization is performed with the following directives: 8 | 9 | 10 | ## #import 11 | 12 | In a script we define eg a function 13 | ``` 14 | vec3 $myfunc(float myArg) { 15 | return vec3(myArg); 16 | } 17 | ``` 18 | 19 | We get the editor state in a url from the Save link, and short url it to "someUrl" 20 | 21 | We can then import it in another script and namespace it using the "@" prefix as follows. 22 | ``` 23 | #import myModule < http://someUrl > 24 | 25 | $main(vec4 outputColor, vec3 fragCoords) 26 | { 27 | outputColor.rgb = @myModule.myfunc(123); 28 | } 29 | ``` 30 | 31 | ## #export 32 | 33 | Scripts can define special placeholders in a global namespace to allow for overridable behavior: 34 | 35 | ``` 36 | #export a_global_func < vec3 (vec3 col) > 37 | #export a_global_var < vec3 = vec3(0.1, 0.3, 0.2) > 38 | // We import the global namespace like this: 39 | #import GLOBAL <> 40 | 41 | vec3 $a_global_func( vec3 col ) 42 | { 43 | return col * @GLOBAL.a_global_var; 44 | } 45 | 46 | $main(vec4 outputColor, vec3 fragCoords) 47 | { 48 | outputColor.rgb = @GLOBAL.a_global_func(outputColor); 49 | } 50 | ``` 51 | 52 | Functions must be defined but variables must not, they will be automatically added on the top of the shader using the configuration. 53 | 54 | We then save/short url this script in someUrl and make a new one like: 55 | 56 | ``` 57 | #import myModule 58 | #export a_global_func < vec3 (float myArg) > 59 | 60 | vec3 $a_global_func( vec3 col ) 61 | { 62 | return col + vec3(0.1); 63 | } 64 | 65 | $main(vec4 outputColor, vec3 fragCoords) 66 | { 67 | // the $main function from the previous script will use the a_global_func defined in this script 68 | @myModule.main(outputColor, fragCoords); 69 | } 70 | ``` 71 | 72 | This way we can call code and override parts of it. Check the sdf module in the core lib for a more complex example. 73 | 74 | ## #image 75 | 76 | Fetch and use images from the web in sampler2D uniforms like this: 77 | 78 | ``` 79 | #import IMG < http://imageUrl > 80 | 81 | vec4 texel = texture2D(@IMG.tex, uv); 82 | vec4 infor = @IMG.inf.xyzw; 83 | ``` 84 | 85 | The "tex" property is a sampler2D and the inf property the following vec4: 86 | 87 | { x: imageWidth, y: imageHeight, z: 0.0, y:0.0 } 88 | 89 | ## #cube 90 | 91 | Fetch and use cubemaps in samplerCube uniforms. The image url provided must contain all six sides of the cube in 1x6, 6x1, 3x2 or 2x3 arrangement. 92 | 93 | ``` 94 | #import CUBE < http://imageUrl > 95 | 96 | vec4 texel = textureCube(@CUBE.tex, dir.xyz); 97 | vec4 infor = @CUBE.inf.xyzw; 98 | ``` 99 | 100 | The "tex" property is a samplerCube and the inf property the following vec4: 101 | 102 | { x: cubeSideWidth, y: cubeSideHeight, z: 0.0, y:0.0 } 103 | 104 | ## #video 105 | Fetch and use video frames in sampler2D uniforms. Videos are always muted and looping. 106 | 107 | ``` 108 | #import VID < http://videoUrl > 109 | 110 | vec4 texel = texture2D(@VID.tex, dir.xyz); 111 | vec4 infor = @VID.inf.xyzw; 112 | ``` 113 | 114 | The "tex" property is a sampler2D and the inf property the following vec4: 115 | 116 | { x: imageWidth, y: imageHeight, z: currentTime, y: videoDuration } 117 | 118 | ## #net 119 | Stream 2D textures from websockets. Servers just have to push arraybuffers of unsigned byte RGBA format prepended by a 2 Uint64s header width with the width/height of the current frame and a single float32 serverData which is accesible through the inf uniform for a fixed total of 8 header bytes. 120 | 121 | ``` 122 | #import NET < ws://serverUrl > 123 | 124 | vec4 texel = texture2D(@NET.tex, dir.xyz); 125 | vec4 infor = @NET.inf.xyzw; 126 | ``` 127 | 128 | The "tex" property is a sampler2D and the inf property the following vec4: 129 | 130 | { x: imageWidth, y: imageHeight, z: lastReceivedFrameTime, y: serverData } 131 | 132 | ## #pass 133 | 134 | Sets up multi pass. 135 | 136 | ``` 137 | // Pass textures are half the screen size 138 | #pass MYPASS 139 | 140 | $main(vec4 outputColor, vec3 fragCoords) 141 | { 142 | 143 | vec2 uv = fragCoords.xy / iResolution.xy; 144 | 145 | #if defined @MYPASS.PASS 146 | // This will run in the pass we defined 147 | 148 | outputColor.rg = uv.xy; 149 | 150 | #else 151 | // This will run in the main pass, read 152 | // from the texture generated by MYPASS 153 | 154 | outputColor.gbr = texture2D(@MYPASS.tex, uv).rgb; 155 | 156 | #endif 157 | 158 | } 159 | ``` 160 | 161 | Passes render directly to the screen or in framebuffer textures. Framebuffers exposed in the @PASSNAME.tex property and a .inf vec4 property like: 162 | 163 | { x: textureWidth, y: textureHeight, z: 0., y: 0. } 164 | 165 | Framebuffers are paired so the last rendered frame can be accesed through the .tex property for implementing feedback effects. 166 | 167 | Configuration of the pass is performed using a style parser in the directive text. 168 | 169 | **view** - The first two numbers are used for the width and height of the viewport, the next two for the offset. These are interpreted as pixels by default and if the rel keyword is used, relative to the screen size. 170 | 171 | The keywords supported are the following: 172 | 173 | **screen** - Don't use framebuffers, output directly on screen 174 | 175 | **rel** - Interpret provided coordinates as fractions of the screen size 176 | 177 | **func-xxx** - Set the blendFunction like this: 178 | ``` 179 | #pass PASS1 < func: one msa > 180 | #pass PASS2 < func: one msa one zero > 181 | ``` 182 | 183 | They keyword can be accompanied by 2 or 4 tokens, to use blendFunc or blendFuncSeparate respectively. Tokens are shortcuts for the following gl contants: 184 | * "one": "ONE", 185 | "zero": "ZERO", 186 | "src": "SRC_COLOR", 187 | "msc": "ONE_MINUS_SRC_COLOR", 188 | "dsc": "DST_COLOR", 189 | "mdc": "ONE_MINUS_DST_COLOR", 190 | "sra": "SRC_ALPHA", 191 | "msa": "ONE_MINUS_SRC_ALPHA", 192 | "dsa": "DST_ALPHA", 193 | "mda": "ONE_MINUS_DST_ALPHA", 194 | "sat": "SRC_ALPHA_SATURATE" 195 | 196 | **eq-xxx** Like above, takes one or two tokens and passes them to blendEquation or blendEquationSeparate. Tokens are: 197 | 198 | * "add": "FUNC_ADD", 199 | "sub": "FUNC_SUBTRACT", 200 | "rev": "FUNC_REVERSE_SUBTRACT", 201 | "min": "MIN_EXT", 202 | "max": "MAX_EXT" 203 | 204 | Mind that rayglider will try to enable the blend_minmax extension but it is not guaranteed that the min and max modes will actually be available. 205 | 206 | **wrap** Sets the wrap options for the framebuffer textures: 207 | 208 | ``` 209 | #pass P1 210 | ``` 211 | 212 | * "mirror": "MIRRORED_REPEAT", 213 | "clamp": "CLAMP_TO_EDGE", 214 | "repeat": "REPEAT" 215 | 216 | **freq** Controls how often this pass will be performed. Accepts an argument for the number of frames to skip between invocations. 217 | 218 | ``` 219 | #pass P1 220 | ``` 221 | 222 | **mipmap** Try to generate mipmaps for the framebuffer texture after render. 223 | 224 | **cube** Render a cubemap texture. In this case the texture uniform for the pass will be a samplerCube. It will perform the pass 6 times during which iFace uniform will take the values 0-6 to configure each face accordingly. 225 | 226 | **map** Reduce the texture. Will perform the number of passes provided with the second argument each time halving both dimensions. The texture exposed as the uniform for the pass will be the previous level while performing the reduce pass. For all other passes it will be the last level. 227 | 228 | **vmap** Same as map but will reduce only the height, useful for physic collision calculations 229 | 230 | **rmap** Same as map but will iterate the levels in reverse order, from smaller to bigger. Useful for implementing cone marching 231 | 232 | ## #post 233 | 234 | Sets up a render pass like **#pass** but post passes will run after all normal passes. Useful for making post process modules and gui elements 235 | 236 | ## #midi 237 | 238 | ## #track 239 | 240 | ## #band 241 | 242 | ## #boom 243 | 244 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rayglider 2 | 3 | ![El Jardí de Catalunya](./jardi.jpg) 4 | 5 | Rayglider attempts to modernize glsl development and provide a powerful medium for artistic expression on the web. 6 | 7 | Every aspect of it is driven and configured directly in the editor utilizing RayGL, a glsl variant that provides a modular shader composition mechanism, asset handling and multi pass setup. 8 | 9 | Audio reactivity gets some special love by the [Clubber](https://github.com/wizgrav/clubber) library. High quality modulators that tune to the music can be defined in the editor, also with glsl, and used to drive the visuals. 10 | 11 | The editor script can be exported as a long url that should be shortened to store, share and, especially, import in other scripts to reuse parts of and build on top of it. 12 | 13 | The limits on url length effectively contain individual scripts to ~4KB of text each but by importing scripts that import other scripts, shaders of any complexity can be composed. Equally excelent scalability for their distribution is provided by the url shortening services. 14 | 15 | ## Resources 16 | 17 | [RayGL Documentation](./RAYGL.md) 18 | 19 | [Core shader lib](./lib/) 20 | 21 | ## Examples 22 | 23 | [Techno Kids](https://tinyurl.com/y8a4wb99) 24 | 25 | [El Jardí de Catalunya](https://tinyurl.com/ybgfwulm) - [Article describing the audio reactive technique used](https://medium.com/@wizgrav/music-gradients-6b7177a97b5f) 26 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # assets -------------------------------------------------------------------------------- /assets/soundcloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wizgrav/rayglider/850feaf3814c5587607a0deeffd6ff3e9ab6dcb9/assets/soundcloud.png -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wizgrav/rayglider/850feaf3814c5587607a0deeffd6ff3e9ab6dcb9/favicon.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('./src/ui'); 2 | var twgl = require('twgl.js'); 3 | require('glsl-transpiler'); 4 | var Clubber = require('clubber'); 5 | var Config = require("./src/config"); 6 | var State = require('./src/state'); 7 | var mouse = require('./src/mouse'); 8 | var pads = require('./src/pads'); 9 | var ui = require('./src/ui'); 10 | 11 | State.canvas = document.querySelector("canvas"); 12 | State.context = twgl.getWebGLContext(State.canvas); 13 | State.clubber = new Clubber(); 14 | State.clubber.listen(document.querySelector("audio")); 15 | 16 | var boomOpts = {src: State.clubber.notes, width: 128, height: 1, format: State.context.LUMINANCE}; 17 | var arr = []; 18 | 19 | State.extensions = {}; 20 | 21 | var ext = { 22 | "OES_standard_derivatives" : ["derivatives", "#extension GL_OES_standard_derivatives : enable"], 23 | "EXT_shader_texture_lod": ["lod","#extension GL_EXT_shader_texture_lod : enable"], 24 | "OES_texture_float": ["floatSupport",""], 25 | "OES_texture_float_linear": ["floatLinear",""], 26 | "EXT_blend_minmax": ["minmax",""] 27 | } 28 | 29 | for(var ex in ext) { 30 | if(State.context.getExtension(ex)){ 31 | arr.push(ext[ex][1]); 32 | State.extensions[ext[ex][0]] = true; 33 | } 34 | } 35 | 36 | arr.push("precision highp float;"); 37 | 38 | State.header = arr.join("\n"); 39 | 40 | var lastTime = 0; 41 | 42 | function render(time) { 43 | window.requestAnimationFrame(render); 44 | 45 | if(State.text) { 46 | var text = State.text; 47 | State.text = null; 48 | delete State.error; 49 | Config(text, function (c){ 50 | if(State.error) { ui(State.error); return; } else { ui(null, c.title); } 51 | if(State.config) { 52 | State.config.prepass.forEach(function(p) { p(false); }); 53 | State.config.passes.forEach(function(p) { p(false); }); 54 | State.config.postpass.forEach(function(p) { p(false); }); 55 | } 56 | State.config = c; 57 | State.config.uniforms.iBoom = twgl.createTexture(State.context, boomOpts); 58 | // console.log(c); 59 | }); 60 | } 61 | 62 | if(!State.config || State.paused) return; 63 | 64 | 65 | State.needResize = twgl.resizeCanvasToDisplaySize(State.canvas, State.canvasScale); 66 | 67 | State.config.uniforms.iRes.set([State.canvas.width, State.canvas.height, 1]); 68 | 69 | State.currentTime = time; 70 | State.config.uniforms.iFrame = ++State.frameCount; 71 | State.config.uniforms.iDelta = (time - lastTime) / 1000; 72 | State.config.uniforms.iTime = (State.currentTime - State.offsetTime) / 1000; 73 | 74 | State.clubber.update(State.currentTime); 75 | twgl.setTextureFromArray(State.context, State.config.uniforms.iBoom, State.clubber.notes, boomOpts); 76 | 77 | mouse(); 78 | pads(); 79 | 80 | 81 | State.config.prepass.forEach(function(p) { p(); }); 82 | State.config.passes.forEach(function(p) { p(); }); 83 | State.config.postpass.forEach(function(p) { p(); }); 84 | 85 | lastTime = time; 86 | delete State.needResize; 87 | } 88 | 89 | render(0); -------------------------------------------------------------------------------- /jardi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wizgrav/rayglider/850feaf3814c5587607a0deeffd6ff3e9ab6dcb9/jardi.jpg -------------------------------------------------------------------------------- /lib/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 | direction: ltr; 9 | } 10 | 11 | /* PADDING */ 12 | 13 | .CodeMirror-lines { 14 | padding: 4px 0; /* Vertical padding around content */ 15 | } 16 | .CodeMirror pre { 17 | padding: 0 4px; /* Horizontal padding of content */ 18 | } 19 | 20 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 21 | background-color: white; /* The little square between H and V scrollbars */ 22 | } 23 | 24 | /* GUTTER */ 25 | 26 | .CodeMirror-gutters { 27 | border-right: 1px solid #ddd; 28 | background-color: #f7f7f7; 29 | white-space: nowrap; 30 | } 31 | .CodeMirror-linenumbers {} 32 | .CodeMirror-linenumber { 33 | padding: 0 3px 0 5px; 34 | min-width: 20px; 35 | text-align: right; 36 | color: #999; 37 | white-space: nowrap; 38 | } 39 | 40 | .CodeMirror-guttermarker { color: black; } 41 | .CodeMirror-guttermarker-subtle { color: #999; } 42 | 43 | /* CURSOR */ 44 | 45 | .CodeMirror-cursor { 46 | border-left: 1px solid black; 47 | border-right: none; 48 | width: 0; 49 | } 50 | /* Shown when moving in bi-directional text */ 51 | .CodeMirror div.CodeMirror-secondarycursor { 52 | border-left: 1px solid silver; 53 | } 54 | .cm-fat-cursor .CodeMirror-cursor { 55 | width: auto; 56 | border: 0 !important; 57 | background: #7e7; 58 | } 59 | .cm-fat-cursor div.CodeMirror-cursors { 60 | z-index: 1; 61 | } 62 | .cm-fat-cursor-mark { 63 | background-color: rgba(20, 255, 20, 0.5); 64 | -webkit-animation: blink 1.06s steps(1) infinite; 65 | -moz-animation: blink 1.06s steps(1) infinite; 66 | animation: blink 1.06s steps(1) infinite; 67 | } 68 | .cm-animate-fat-cursor { 69 | width: auto; 70 | border: 0; 71 | -webkit-animation: blink 1.06s steps(1) infinite; 72 | -moz-animation: blink 1.06s steps(1) infinite; 73 | animation: blink 1.06s steps(1) infinite; 74 | background-color: #7e7; 75 | } 76 | @-moz-keyframes blink { 77 | 0% {} 78 | 50% { background-color: transparent; } 79 | 100% {} 80 | } 81 | @-webkit-keyframes blink { 82 | 0% {} 83 | 50% { background-color: transparent; } 84 | 100% {} 85 | } 86 | @keyframes blink { 87 | 0% {} 88 | 50% { background-color: transparent; } 89 | 100% {} 90 | } 91 | 92 | /* Can style cursor different in overwrite (non-insert) mode */ 93 | .CodeMirror-overwrite .CodeMirror-cursor {} 94 | 95 | .cm-tab { display: inline-block; text-decoration: inherit; } 96 | 97 | .CodeMirror-rulers { 98 | position: absolute; 99 | left: 0; right: 0; top: -50px; bottom: -20px; 100 | overflow: hidden; 101 | } 102 | .CodeMirror-ruler { 103 | border-left: 1px solid #ccc; 104 | top: 0; bottom: 0; 105 | position: absolute; 106 | } 107 | 108 | /* DEFAULT THEME */ 109 | 110 | .cm-s-default .cm-header {color: blue;} 111 | .cm-s-default .cm-quote {color: #090;} 112 | .cm-negative {color: #d44;} 113 | .cm-positive {color: #292;} 114 | .cm-header, .cm-strong {font-weight: bold;} 115 | .cm-em {font-style: italic;} 116 | .cm-link {text-decoration: underline;} 117 | .cm-strikethrough {text-decoration: line-through;} 118 | 119 | .cm-s-default .cm-keyword {color: #708;} 120 | .cm-s-default .cm-atom {color: #219;} 121 | .cm-s-default .cm-number {color: #164;} 122 | .cm-s-default .cm-def {color: #00f;} 123 | .cm-s-default .cm-variable, 124 | .cm-s-default .cm-punctuation, 125 | .cm-s-default .cm-property, 126 | .cm-s-default .cm-operator {} 127 | .cm-s-default .cm-variable-2 {color: #05a;} 128 | .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} 129 | .cm-s-default .cm-comment {color: #a50;} 130 | .cm-s-default .cm-string {color: #a11;} 131 | .cm-s-default .cm-string-2 {color: #f50;} 132 | .cm-s-default .cm-meta {color: #555;} 133 | .cm-s-default .cm-qualifier {color: #555;} 134 | .cm-s-default .cm-builtin {color: #30a;} 135 | .cm-s-default .cm-bracket {color: #997;} 136 | .cm-s-default .cm-tag {color: #170;} 137 | .cm-s-default .cm-attribute {color: #00c;} 138 | .cm-s-default .cm-hr {color: #999;} 139 | .cm-s-default .cm-link {color: #00c;} 140 | 141 | .cm-s-default .cm-error {color: #f00;} 142 | .cm-invalidchar {color: #f00;} 143 | 144 | .CodeMirror-composing { border-bottom: 2px solid; } 145 | 146 | /* Default styles for common addons */ 147 | 148 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} 149 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} 150 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 151 | .CodeMirror-activeline-background {background: #e8f2ff;} 152 | 153 | /* STOP */ 154 | 155 | /* The rest of this file contains styles related to the mechanics of 156 | the editor. You probably shouldn't touch them. */ 157 | 158 | .CodeMirror { 159 | position: relative; 160 | overflow: hidden; 161 | background: white; 162 | } 163 | 164 | .CodeMirror-scroll { 165 | overflow: scroll !important; /* Things will break if this is overridden */ 166 | /* 30px is the magic margin used to hide the element's real scrollbars */ 167 | /* See overflow: hidden in .CodeMirror */ 168 | margin-bottom: -30px; margin-right: -30px; 169 | padding-bottom: 30px; 170 | height: 100%; 171 | outline: none; /* Prevent dragging from highlighting the element */ 172 | position: relative; 173 | } 174 | .CodeMirror-sizer { 175 | position: relative; 176 | border-right: 30px solid transparent; 177 | } 178 | 179 | /* The fake, visible scrollbars. Used to force redraw during scrolling 180 | before actual scrolling happens, thus preventing shaking and 181 | flickering artifacts. */ 182 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 183 | position: absolute; 184 | z-index: 6; 185 | display: none; 186 | } 187 | .CodeMirror-vscrollbar { 188 | right: 0; top: 0; 189 | overflow-x: hidden; 190 | overflow-y: scroll; 191 | } 192 | .CodeMirror-hscrollbar { 193 | bottom: 0; left: 0; 194 | overflow-y: hidden; 195 | overflow-x: scroll; 196 | } 197 | .CodeMirror-scrollbar-filler { 198 | right: 0; bottom: 0; 199 | } 200 | .CodeMirror-gutter-filler { 201 | left: 0; bottom: 0; 202 | } 203 | 204 | .CodeMirror-gutters { 205 | position: absolute; left: 0; top: 0; 206 | min-height: 100%; 207 | z-index: 3; 208 | } 209 | .CodeMirror-gutter { 210 | white-space: normal; 211 | height: 100%; 212 | display: inline-block; 213 | vertical-align: top; 214 | margin-bottom: -30px; 215 | } 216 | .CodeMirror-gutter-wrapper { 217 | position: absolute; 218 | z-index: 4; 219 | background: none !important; 220 | border: none !important; 221 | } 222 | .CodeMirror-gutter-background { 223 | position: absolute; 224 | top: 0; bottom: 0; 225 | z-index: 4; 226 | } 227 | .CodeMirror-gutter-elt { 228 | position: absolute; 229 | cursor: default; 230 | z-index: 4; 231 | } 232 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent } 233 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } 234 | 235 | .CodeMirror-lines { 236 | cursor: text; 237 | min-height: 1px; /* prevents collapsing before first draw */ 238 | } 239 | .CodeMirror pre { 240 | /* Reset some styles that the rest of the page might have set */ 241 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 242 | border-width: 0; 243 | background: transparent; 244 | font-family: inherit; 245 | font-size: inherit; 246 | margin: 0; 247 | white-space: pre; 248 | word-wrap: normal; 249 | line-height: inherit; 250 | color: inherit; 251 | z-index: 2; 252 | position: relative; 253 | overflow: visible; 254 | -webkit-tap-highlight-color: transparent; 255 | -webkit-font-variant-ligatures: contextual; 256 | font-variant-ligatures: contextual; 257 | } 258 | .CodeMirror-wrap pre { 259 | word-wrap: break-word; 260 | white-space: pre-wrap; 261 | word-break: normal; 262 | } 263 | 264 | .CodeMirror-linebackground { 265 | position: absolute; 266 | left: 0; right: 0; top: 0; bottom: 0; 267 | z-index: 0; 268 | } 269 | 270 | .CodeMirror-linewidget { 271 | position: relative; 272 | z-index: 2; 273 | padding: 0.1px; /* Force widget margins to stay inside of the container */ 274 | } 275 | 276 | .CodeMirror-widget {} 277 | 278 | .CodeMirror-rtl pre { direction: rtl; } 279 | 280 | .CodeMirror-code { 281 | outline: none; 282 | } 283 | 284 | /* Force content-box sizing for the elements where we expect it */ 285 | .CodeMirror-scroll, 286 | .CodeMirror-sizer, 287 | .CodeMirror-gutter, 288 | .CodeMirror-gutters, 289 | .CodeMirror-linenumber { 290 | -moz-box-sizing: content-box; 291 | box-sizing: content-box; 292 | } 293 | 294 | .CodeMirror-measure { 295 | position: absolute; 296 | width: 100%; 297 | height: 0; 298 | overflow: hidden; 299 | visibility: hidden; 300 | } 301 | 302 | .CodeMirror-cursor { 303 | position: absolute; 304 | pointer-events: none; 305 | } 306 | .CodeMirror-measure pre { position: static; } 307 | 308 | div.CodeMirror-cursors { 309 | visibility: hidden; 310 | position: relative; 311 | z-index: 3; 312 | } 313 | div.CodeMirror-dragcursors { 314 | visibility: visible; 315 | } 316 | 317 | .CodeMirror-focused div.CodeMirror-cursors { 318 | visibility: visible; 319 | } 320 | 321 | .CodeMirror-selected { background: #d9d9d9; } 322 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 323 | .CodeMirror-crosshair { cursor: crosshair; } 324 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 325 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 326 | 327 | .cm-searching { 328 | background-color: #ffa; 329 | background-color: rgba(255, 255, 0, .4); 330 | } 331 | 332 | /* Used to force a border model for a node */ 333 | .cm-force-border { padding-right: .1px; } 334 | 335 | @media print { 336 | /* Hide the cursor when printing */ 337 | .CodeMirror div.CodeMirror-cursors { 338 | visibility: hidden; 339 | } 340 | } 341 | 342 | /* See issue #2901 */ 343 | .cm-tab-wrap-hack:after { content: ''; } 344 | 345 | /* Help users use markselection to safely style text background */ 346 | span.CodeMirror-selectedtext { background: none; } 347 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rayglider", 3 | "version": "0.1.0", 4 | "description": "May the Boom be with you", 5 | "main": "./index.js", 6 | "local-web-server": { 7 | "port": 8100, 8 | "forbid": "*.json" 9 | }, 10 | "scripts": { 11 | "dist": "webpack --mode=development index.js -o dist/rayglider.js", 12 | "server": "ws --https" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/wizgrav/rayglider.git" 17 | }, 18 | "keywords": [ 19 | "glsl", 20 | "sandbox", 21 | "framework", 22 | "editor", 23 | "game", 24 | "music", 25 | "audio", 26 | "reactive", 27 | "webgl", 28 | "shader", 29 | "clubber", 30 | "midi", 31 | "boom" 32 | ], 33 | "author": "Yannis Gravezas ", 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/wizgrav/rayglider/issues" 37 | }, 38 | "homepage": "https://github.com/wizgrav/rayglider#readme", 39 | "dependencies": { 40 | "twgl.js": "^4.4.0", 41 | "glsl-transpiler": "^1.5.8", 42 | "codemirror": "^5.37.0", 43 | "clubber": "^1.7.1", 44 | "precedence-maps": "^0.1.3" 45 | }, 46 | "devDependencies": { 47 | "local-web-server": "^2.5.2", 48 | "webpack": "^4.6.0", 49 | "webpack-cli": "^2.0.15" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /r.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Rayglider 33 | 45 | 46 | 47 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /run.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Rayglider 35 | 36 | 37 | 411 | 412 | 413 | 414 |
415 |
416 | 417 |
418 | 437 |
438 |
439 | 440 |
441 |
442 | 443 |
444 | 458 | 459 | 460 |
461 | 462 |
Tap/Click to unmute
Drag and drop your audio files to listen
463 | 464 | 465 | -------------------------------------------------------------------------------- /shaders/lib/anoise: -------------------------------------------------------------------------------- 1 | vec3 $mod289(vec3 x) 2 | { 3 | return x - floor(x * (1.0 / 289.0)) * 289.0; 4 | } 5 | 6 | vec4 $mod289(vec4 x) 7 | { 8 | return x - floor(x * (1.0 / 289.0)) * 289.0; 9 | } 10 | 11 | vec4 $permute(vec4 x) 12 | { 13 | return mod289(((x*34.0)+1.0)*x); 14 | } 15 | 16 | vec4 $taylorInvSqrt(vec4 r) 17 | { 18 | return 1.79284291400159 - 0.85373472095314 * r; 19 | } 20 | 21 | vec2 $fade(vec2 t) { 22 | return t*t*t*(t*(t*6.0-15.0)+10.0); 23 | } 24 | 25 | // Classic Perlin noise 26 | float $noise2(vec2 P) 27 | { 28 | vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0); 29 | vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0); 30 | Pi = $mod289(Pi); // To avoid truncation effects in permutation 31 | vec4 ix = Pi.xzxz; 32 | vec4 iy = Pi.yyww; 33 | vec4 fx = Pf.xzxz; 34 | vec4 fy = Pf.yyww; 35 | 36 | vec4 i = $permute($permute(ix) + iy); 37 | 38 | vec4 gx = fract(i * (1.0 / 41.0)) * 2.0 - 1.0 ; 39 | vec4 gy = abs(gx) - 0.5 ; 40 | vec4 tx = floor(gx + 0.5); 41 | gx = gx - tx; 42 | 43 | vec2 g00 = vec2(gx.x,gy.x); 44 | vec2 g10 = vec2(gx.y,gy.y); 45 | vec2 g01 = vec2(gx.z,gy.z); 46 | vec2 g11 = vec2(gx.w,gy.w); 47 | 48 | vec4 norm = $taylorInvSqrt(vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); 49 | g00 *= norm.x; 50 | g01 *= norm.y; 51 | g10 *= norm.z; 52 | g11 *= norm.w; 53 | 54 | float n00 = dot(g00, vec2(fx.x, fy.x)); 55 | float n10 = dot(g10, vec2(fx.y, fy.y)); 56 | float n01 = dot(g01, vec2(fx.z, fy.z)); 57 | float n11 = dot(g11, vec2(fx.w, fy.w)); 58 | 59 | vec2 fade_xy = $fade(Pf.xy); 60 | vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x); 61 | float n_xy = mix(n_x.x, n_x.y, fade_xy.y); 62 | return 2.3 * n_xy; 63 | } 64 | 65 | float noise3(vec3 P) 66 | { 67 | vec3 Pi0 = floor(P); // Integer part for indexing 68 | vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1 69 | Pi0 = $mod289(Pi0); 70 | Pi1 = $mod289(Pi1); 71 | vec3 Pf0 = fract(P); // Fractional part for interpolation 72 | vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 73 | vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); 74 | vec4 iy = vec4(Pi0.yy, Pi1.yy); 75 | vec4 iz0 = Pi0.zzzz; 76 | vec4 iz1 = Pi1.zzzz; 77 | 78 | vec4 ixy = $permute($permute(ix) + iy); 79 | vec4 ixy0 = $permute(ixy + iz0); 80 | vec4 ixy1 = $permute(ixy + iz1); 81 | 82 | vec4 gx0 = ixy0 * (1.0 / 7.0); 83 | vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5; 84 | gx0 = fract(gx0); 85 | vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); 86 | vec4 sz0 = step(gz0, vec4(0.0)); 87 | gx0 -= sz0 * (step(0.0, gx0) - 0.5); 88 | gy0 -= sz0 * (step(0.0, gy0) - 0.5); 89 | 90 | vec4 gx1 = ixy1 * (1.0 / 7.0); 91 | vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5; 92 | gx1 = fract(gx1); 93 | vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); 94 | vec4 sz1 = step(gz1, vec4(0.0)); 95 | gx1 -= sz1 * (step(0.0, gx1) - 0.5); 96 | gy1 -= sz1 * (step(0.0, gy1) - 0.5); 97 | 98 | vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); 99 | vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); 100 | vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); 101 | vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); 102 | vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); 103 | vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); 104 | vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); 105 | vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); 106 | 107 | vec4 norm0 = $taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); 108 | g000 *= norm0.x; 109 | g010 *= norm0.y; 110 | g100 *= norm0.z; 111 | g110 *= norm0.w; 112 | vec4 norm1 = $taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); 113 | g001 *= norm1.x; 114 | g011 *= norm1.y; 115 | g101 *= norm1.z; 116 | g111 *= norm1.w; 117 | 118 | float n000 = dot(g000, Pf0); 119 | float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); 120 | float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); 121 | float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); 122 | float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); 123 | float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); 124 | float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); 125 | float n111 = dot(g111, Pf1); 126 | 127 | vec3 fade_xyz = fade(Pf0); 128 | vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); 129 | vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); 130 | float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); 131 | return 2.2 * n_xyz; 132 | } 133 | 134 | float pnoise2(vec2 P, vec2 rep) 135 | { 136 | vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0); 137 | vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0); 138 | Pi = mod(Pi, rep.xyxy); // To create noise with explicit period 139 | Pi = $mod289(Pi); // To avoid truncation effects in permutation 140 | vec4 ix = Pi.xzxz; 141 | vec4 iy = Pi.yyww; 142 | vec4 fx = Pf.xzxz; 143 | vec4 fy = Pf.yyww; 144 | 145 | vec4 i = $permute($permute(ix) + iy); 146 | 147 | vec4 gx = fract(i * (1.0 / 41.0)) * 2.0 - 1.0 ; 148 | vec4 gy = abs(gx) - 0.5 ; 149 | vec4 tx = floor(gx + 0.5); 150 | gx = gx - tx; 151 | 152 | vec2 g00 = vec2(gx.x,gy.x); 153 | vec2 g10 = vec2(gx.y,gy.y); 154 | vec2 g01 = vec2(gx.z,gy.z); 155 | vec2 g11 = vec2(gx.w,gy.w); 156 | 157 | vec4 norm = $taylorInvSqrt(vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); 158 | g00 *= norm.x; 159 | g01 *= norm.y; 160 | g10 *= norm.z; 161 | g11 *= norm.w; 162 | 163 | float n00 = dot(g00, vec2(fx.x, fy.x)); 164 | float n10 = dot(g10, vec2(fx.y, fy.y)); 165 | float n01 = dot(g01, vec2(fx.z, fy.z)); 166 | float n11 = dot(g11, vec2(fx.w, fy.w)); 167 | 168 | vec2 fade_xy = $fade(Pf.xy); 169 | vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x); 170 | float n_xy = mix(n_x.x, n_x.y, fade_xy.y); 171 | return 2.3 * n_xy; 172 | } 173 | 174 | float pnoise3(vec3 P, vec3 rep) 175 | { 176 | vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period 177 | vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period 178 | Pi0 = $mod289(Pi0); 179 | Pi1 = $mod289(Pi1); 180 | vec3 Pf0 = fract(P); // Fractional part for interpolation 181 | vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 182 | vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); 183 | vec4 iy = vec4(Pi0.yy, Pi1.yy); 184 | vec4 iz0 = Pi0.zzzz; 185 | vec4 iz1 = Pi1.zzzz; 186 | 187 | vec4 ixy = $permute($permute(ix) + iy); 188 | vec4 ixy0 = $permute(ixy + iz0); 189 | vec4 ixy1 = $permute(ixy + iz1); 190 | 191 | vec4 gx0 = ixy0 * (1.0 / 7.0); 192 | vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5; 193 | gx0 = fract(gx0); 194 | vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); 195 | vec4 sz0 = step(gz0, vec4(0.0)); 196 | gx0 -= sz0 * (step(0.0, gx0) - 0.5); 197 | gy0 -= sz0 * (step(0.0, gy0) - 0.5); 198 | 199 | vec4 gx1 = ixy1 * (1.0 / 7.0); 200 | vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5; 201 | gx1 = fract(gx1); 202 | vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); 203 | vec4 sz1 = step(gz1, vec4(0.0)); 204 | gx1 -= sz1 * (step(0.0, gx1) - 0.5); 205 | gy1 -= sz1 * (step(0.0, gy1) - 0.5); 206 | 207 | vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); 208 | vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); 209 | vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); 210 | vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); 211 | vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); 212 | vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); 213 | vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); 214 | vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); 215 | 216 | vec4 norm0 = $taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); 217 | g000 *= norm0.x; 218 | g010 *= norm0.y; 219 | g100 *= norm0.z; 220 | g110 *= norm0.w; 221 | vec4 norm1 = $taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); 222 | g001 *= norm1.x; 223 | g011 *= norm1.y; 224 | g101 *= norm1.z; 225 | g111 *= norm1.w; 226 | 227 | float n000 = dot(g000, Pf0); 228 | float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); 229 | float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); 230 | float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); 231 | float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); 232 | float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); 233 | float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); 234 | float n111 = dot(g111, Pf1); 235 | 236 | vec3 fade_xyz = fade(Pf0); 237 | vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); 238 | vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); 239 | float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); 240 | return 2.2 * n_xyz; 241 | } 242 | 243 | float $snoise2(vec2 v) 244 | { 245 | const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 246 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) 247 | -0.577350269189626, // -1.0 + 2.0 * C.x 248 | 0.024390243902439); // 1.0 / 41.0 249 | // First corner 250 | vec2 i = floor(v + dot(v, C.yy) ); 251 | vec2 x0 = v - i + dot(i, C.xx); 252 | 253 | // Other corners 254 | vec2 i1; 255 | //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 256 | //i1.y = 1.0 - i1.x; 257 | i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); 258 | // x0 = x0 - 0.0 + 0.0 * C.xx ; 259 | // x1 = x0 - i1 + 1.0 * C.xx ; 260 | // x2 = x0 - 1.0 + 2.0 * C.xx ; 261 | vec4 x12 = x0.xyxy + C.xxzz; 262 | x12.xy -= i1; 263 | 264 | // Permutations 265 | i = $mod289(i); // Avoid truncation effects in permutation 266 | vec3 p = $permute( $permute( i.y + vec3(0.0, i1.y, 1.0 )) 267 | + i.x + vec3(0.0, i1.x, 1.0 )); 268 | 269 | vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); 270 | m = m*m ; 271 | m = m*m ; 272 | 273 | // Gradients: 41 points uniformly over a line, mapped onto a diamond. 274 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) 275 | 276 | vec3 x = 2.0 * fract(p * C.www) - 1.0; 277 | vec3 h = abs(x) - 0.5; 278 | vec3 ox = floor(x + 0.5); 279 | vec3 a0 = x - ox; 280 | 281 | // Normalise gradients implicitly by scaling m 282 | // Approximation of: m *= inversesqrt( a0*a0 + h*h ); 283 | m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); 284 | 285 | // Compute final noise value at P 286 | vec3 g; 287 | g.x = a0.x * x0.x + h.x * x0.y; 288 | g.yz = a0.yz * x12.xz + h.yz * x12.yw; 289 | return 130.0 * dot(m, g); 290 | } 291 | 292 | float snoise3(vec3 v) 293 | { 294 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; 295 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); 296 | 297 | // First corner 298 | vec3 i = floor(v + dot(v, C.yyy) ); 299 | vec3 x0 = v - i + dot(i, C.xxx) ; 300 | 301 | // Other corners 302 | vec3 g = step(x0.yzx, x0.xyz); 303 | vec3 l = 1.0 - g; 304 | vec3 i1 = min( g.xyz, l.zxy ); 305 | vec3 i2 = max( g.xyz, l.zxy ); 306 | 307 | // x0 = x0 - 0.0 + 0.0 * C.xxx; 308 | // x1 = x0 - i1 + 1.0 * C.xxx; 309 | // x2 = x0 - i2 + 2.0 * C.xxx; 310 | // x3 = x0 - 1.0 + 3.0 * C.xxx; 311 | vec3 x1 = x0 - i1 + C.xxx; 312 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y 313 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y 314 | 315 | // Permutations 316 | i = $mod289(i); 317 | vec4 p = $permute( $permute( $permute( 318 | i.z + vec4(0.0, i1.z, i2.z, 1.0 )) 319 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) 320 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); 321 | 322 | // Gradients: 7x7 points over a square, mapped onto an octahedron. 323 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) 324 | float n_ = 0.142857142857; // 1.0/7.0 325 | vec3 ns = n_ * D.wyz - D.xzx; 326 | 327 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) 328 | 329 | vec4 x_ = floor(j * ns.z); 330 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) 331 | 332 | vec4 x = x_ *ns.x + ns.yyyy; 333 | vec4 y = y_ *ns.x + ns.yyyy; 334 | vec4 h = 1.0 - abs(x) - abs(y); 335 | 336 | vec4 b0 = vec4( x.xy, y.xy ); 337 | vec4 b1 = vec4( x.zw, y.zw ); 338 | 339 | //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; 340 | //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; 341 | vec4 s0 = floor(b0)*2.0 + 1.0; 342 | vec4 s1 = floor(b1)*2.0 + 1.0; 343 | vec4 sh = -step(h, vec4(0.0)); 344 | 345 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; 346 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; 347 | 348 | vec3 p0 = vec3(a0.xy,h.x); 349 | vec3 p1 = vec3(a0.zw,h.y); 350 | vec3 p2 = vec3(a1.xy,h.z); 351 | vec3 p3 = vec3(a1.zw,h.w); 352 | 353 | //Normalise gradients 354 | vec4 norm = $taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 355 | p0 *= norm.x; 356 | p1 *= norm.y; 357 | p2 *= norm.z; 358 | p3 *= norm.w; 359 | 360 | // Mix final noise value 361 | vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); 362 | m = m * m; 363 | return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), 364 | dot(p2,x2), dot(p3,x3) ) ); 365 | } -------------------------------------------------------------------------------- /shaders/lib/blur: -------------------------------------------------------------------------------- 1 | vec4 $blur5(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { 2 | vec4 color = vec4(0.0); 3 | vec2 off1 = vec2(1.3333333333333333) * direction; 4 | color += texture2D(image, uv) * 0.29411764705882354; 5 | color += texture2D(image, uv + (off1 / resolution)) * 0.35294117647058826; 6 | color += texture2D(image, uv - (off1 / resolution)) * 0.35294117647058826; 7 | return color; 8 | } 9 | 10 | vec4 $blur9(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { 11 | vec4 color = vec4(0.0); 12 | vec2 off1 = vec2(1.3846153846) * direction; 13 | vec2 off2 = vec2(3.2307692308) * direction; 14 | color += texture2D(image, uv) * 0.2270270270; 15 | color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162; 16 | color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162; 17 | color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703; 18 | color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703; 19 | return color; 20 | } 21 | 22 | vec4 $blur13(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { 23 | vec4 color = vec4(0.0); 24 | vec2 off1 = vec2(1.411764705882353) * direction; 25 | vec2 off2 = vec2(3.2941176470588234) * direction; 26 | vec2 off3 = vec2(5.176470588235294) * direction; 27 | color += texture2D(image, uv) * 0.1964825501511404; 28 | color += texture2D(image, uv + (off1 / resolution)) * 0.2969069646728344; 29 | color += texture2D(image, uv - (off1 / resolution)) * 0.2969069646728344; 30 | color += texture2D(image, uv + (off2 / resolution)) * 0.09447039785044732; 31 | color += texture2D(image, uv - (off2 / resolution)) * 0.09447039785044732; 32 | color += texture2D(image, uv + (off3 / resolution)) * 0.010381362401148057; 33 | color += texture2D(image, uv - (off3 / resolution)) * 0.010381362401148057; 34 | return color; 35 | } 36 | 37 | vec4 $radial(sampler2D image, vec2 uv, vec2 center, float stepSize){ 38 | vec2 delta = center - uv; 39 | float dist = length( delta ); 40 | vec2 stepv = stepSize * delta / dist; 41 | float iters = dist/stepSize; 42 | vec4 col = vec4(0.0); 43 | for ( float i = 0.0; i < @def.blur_radial_taps; i += 1.0 ) { 44 | col += ( i <= iters && uv.y < 1.0 ? texture2D( image, uv ) : vec4(0.0) ); 45 | uv += stepv; 46 | } 47 | return col/@def.blur_radial_taps; 48 | } -------------------------------------------------------------------------------- /shaders/lib/camera: -------------------------------------------------------------------------------- 1 | mat3 $lookAt( in vec3 ro, in vec3 ta, float cr ) 2 | { 3 | vec3 cw = normalize(ta - ro); 4 | vec3 cp = vec3(sin(cr), cos(cr),0.0); 5 | vec3 cu = normalize( cross(cw,cp) ); 6 | vec3 cv = normalize( cross(cu,cw) ); 7 | return mat3( cu, cv, cw ); 8 | } -------------------------------------------------------------------------------- /shaders/lib/color: -------------------------------------------------------------------------------- 1 | vec3 $rgb2hsv(vec3 c){ 2 | 3 | vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); 4 | vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); 5 | vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); 6 | 7 | float d = q.x - min(q.w, q.y); 8 | float e = 1.0e-10; 9 | return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); 10 | } 11 | 12 | vec3 $hsv2rgb(vec3 c) 13 | { 14 | vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 15 | vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 16 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 17 | } 18 | 19 | #export color_lut_clamp 20 | #export color_lut_flip 21 | 22 | vec4 $lut(vec4 textureColor, sampler2D lut) { 23 | if($color_lut_clamp){ 24 | textureColor = clamp(textureColor, 0.0, 1.0); 25 | } 26 | 27 | mediump float blueColor = textureColor.b * 63.0; 28 | 29 | mediump vec2 quad1; 30 | quad1.y = floor(floor(blueColor) / 8.0); 31 | quad1.x = floor(blueColor) - (quad1.y * 8.0); 32 | 33 | mediump vec2 quad2; 34 | quad2.y = floor(ceil(blueColor) / 8.0); 35 | quad2.x = ceil(blueColor) - (quad2.y * 8.0); 36 | 37 | highp vec2 texPos1; 38 | texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); 39 | texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); 40 | 41 | if($color_lut_flip){ 42 | texPos1.y = 1.0 - texPos1.y; 43 | } 44 | 45 | highp vec2 texPos2; 46 | texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); 47 | texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); 48 | 49 | if($color_lut_flip){ 50 | texPos2.y = 1.0-texPos2.y; 51 | } 52 | 53 | lowp vec4 newColor1 = texture2D(lut, texPos1); 54 | lowp vec4 newColor2 = texture2D(lut, texPos2); 55 | 56 | lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor)); 57 | return newColor; 58 | } -------------------------------------------------------------------------------- /shaders/lib/debug: -------------------------------------------------------------------------------- 1 | vec4 $bars(vec4 m, vec2 uv) { 2 | vec4 color = vec4(0.); 3 | float inc = 1. / 4.; 4 | for(float j=0.;j<4.;j++) { 5 | float v = m.x; 6 | m.xyzw = m.yzwx; 7 | color.xyzw = color.yzwx; 8 | if(uv.x < j * inc || uv.x >= (j + 1.) * inc) { 9 | continue; 10 | } 11 | 12 | if(uv.y > v){ 13 | color.w = 0.2; 14 | continue; 15 | } 16 | color.w = 1.0; 17 | } 18 | if(color.a == 0.2) color.rgb = vec3(0.2); 19 | else if(color.a == 1.0) color.rgb = vec3(1.); 20 | color.a = 1.0; 21 | return color; 22 | } -------------------------------------------------------------------------------- /shaders/lib/fxaa: -------------------------------------------------------------------------------- 1 | uniform vec2 resolution; 2 | 3 | uniform sampler2D tex; 4 | 5 | void main() { 6 | vec2 fragCoord = v_texCoord0 * resolution; 7 | gl_FragColor = fxaa(u_texture0, fragCoord, resolution); 8 | } -------------------------------------------------------------------------------- /shaders/lib/hg_sdf: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Simple test/port of Mercury's SDF GLSL library: http://mercury.sexy/hg_sdf/ 3 | // by Tom '2015 4 | // Disclaimer: 5 | // The library is done by Mercury team for OpenGL 4+ (look below), 6 | // not me, and this is just an unofficial port. 7 | //----------------------------------------------------------------------------- 8 | 9 | //////////////////////////////////////////////////////////////// 10 | // 11 | // HG_SDF 12 | // 13 | // GLSL LIBRARY FOR BUILDING SIGNED DISTANCE BOUNDS 14 | // 15 | // version 2015-12-15 (initial release) 16 | // 17 | // Check http://mercury.sexy/hg_sdf for updates 18 | // and usage examples. Send feedback to spheretracing@mercury.sexy. 19 | // 20 | // Brought to you by MERCURY http://mercury.sexy 21 | // 22 | // 23 | // 24 | // Released as Creative Commons Attribution-NonCommercial (CC BY-NC) 25 | // 26 | //////////////////////////////////////////////////////////////// 27 | 28 | 29 | //////////////////////////////////////////////////////////////// 30 | // 31 | // HELPER FUNCTIONS/MACROS 32 | // 33 | //////////////////////////////////////////////////////////////// 34 | 35 | // Sign function that doesn't return 0 36 | float $sgn(float x) { 37 | return (x<0.)?-1.:1.; 38 | } 39 | 40 | float $square (float x) { 41 | return x*x; 42 | } 43 | 44 | vec2 $square (vec2 x) { 45 | return x*x; 46 | } 47 | 48 | vec3 $square (vec3 x) { 49 | return x*x; 50 | } 51 | 52 | float $lengthSqr(vec3 x) { 53 | return dot(x, x); 54 | } 55 | 56 | 57 | // Maximum/minumum elements of a vector 58 | float $vmax(vec2 v) { 59 | return max(v.x, v.y); 60 | } 61 | 62 | float $vmax(vec3 v) { 63 | return max(max(v.x, v.y), v.z); 64 | } 65 | 66 | float $vmax(vec4 v) { 67 | return max(max(v.x, v.y), max(v.z, v.w)); 68 | } 69 | 70 | float $vmin(vec2 v) { 71 | return min(v.x, v.y); 72 | } 73 | 74 | float $vmin(vec3 v) { 75 | return min(min(v.x, v.y), v.z); 76 | } 77 | 78 | float $vmin(vec4 v) { 79 | return min(min(v.x, v.y), min(v.z, v.w)); 80 | } 81 | 82 | 83 | 84 | 85 | //////////////////////////////////////////////////////////////// 86 | // 87 | // PRIMITIVE DISTANCE FUNCTIONS 88 | // 89 | //////////////////////////////////////////////////////////////// 90 | // 91 | // Conventions: 92 | // 93 | // Everything that is a distance function is called fSomething. 94 | // The first argument is always a point in 2 or 3-space called

. 95 | // Unless otherwise noted, (if the object has an intrinsic "up" 96 | // side or direction) the y axis is "up" and the object is 97 | // centered at the origin. 98 | // 99 | //////////////////////////////////////////////////////////////// 100 | 101 | float $fSphere(vec3 p, float r) { 102 | return length(p) - r; 103 | } 104 | 105 | // Plane with normal n (n is normalized) at some distance from the origin 106 | float $fPlane(vec3 p, vec3 n, float distanceFromOrigin) { 107 | return dot(p, n) + distanceFromOrigin; 108 | } 109 | 110 | // Cheap Box: distance to corners is overestimated 111 | float $fBoxCheap(vec3 p, vec3 b) { //cheap box 112 | return $vmax(abs(p) - b); 113 | } 114 | 115 | // Box: correct distance to corners 116 | float $fBox(vec3 p, vec3 b) { 117 | vec3 d = abs(p) - b; 118 | return length(max(d, vec3(0))) + $vmax(min(d, vec3(0))); 119 | } 120 | 121 | // Same as above, but in two dimensions (an endless box) 122 | float $fBox2Cheap(vec2 p, vec2 b) { 123 | return $vmax(abs(p)-b); 124 | } 125 | 126 | float $fBox2(vec2 p, vec2 b) { 127 | vec2 d = abs(p) - b; 128 | return length(max(d, vec2(0))) + $vmax(min(d, vec2(0))); 129 | } 130 | 131 | 132 | // Endless "corner" 133 | float $fCorner (vec2 p) { 134 | return length(max(p, vec2(0))) + $vmax(min(p, vec2(0))); 135 | } 136 | 137 | // Blobby ball object. You've probably seen it somewhere. This is not a correct distance bound, beware. 138 | float fBlob(vec3 p) { 139 | p = abs(p); 140 | if (p.x < max(p.y, p.z)) p = p.yzx; 141 | if (p.x < max(p.y, p.z)) p = p.yzx; 142 | float b = max(max(max( 143 | dot(p, normalize(vec3(1, 1, 1))), 144 | dot(p.xz, normalize(vec2(PHI+1., 1)))), 145 | dot(p.yx, normalize(vec2(1, PHI)))), 146 | dot(p.xz, normalize(vec2(1, PHI)))); 147 | float l = length(p); 148 | return l - 1.5 - 0.2 * (1.5 / 2.)* cos(min(sqrt(1.01 - b / l)*(PI / 0.25), PI)); 149 | } 150 | 151 | // Cylinder standing upright on the xz plane 152 | float $fCylinder(vec3 p, float r, float height) { 153 | float d = length(p.xz) - r; 154 | d = max(d, abs(p.y) - height); 155 | return d; 156 | } 157 | 158 | // Capsule: A Cylinder with round caps on both sides 159 | float $fCapsule(vec3 p, float r, float c) { 160 | return mix(length(p.xz) - r, length(vec3(p.x, abs(p.y) - c, p.z)) - r, step(c, abs(p.y))); 161 | } 162 | 163 | // Distance to line segment between and , used for fCapsule() version 2below 164 | float $fLineSegment(vec3 p, vec3 a, vec3 b) { 165 | vec3 ab = b - a; 166 | float t = saturate(dot(p - a, ab) / dot(ab, ab)); 167 | return length((ab*t + a) - p); 168 | } 169 | 170 | // Capsule version 2: between two end points and with radius r 171 | float $fCapsule(vec3 p, vec3 a, vec3 b, float r) { 172 | return $fLineSegment(p, a, b) - r; 173 | } 174 | 175 | // Torus in the XZ-plane 176 | float $fTorus(vec3 p, float smallRadius, float largeRadius) { 177 | return length(vec2(length(p.xz) - largeRadius, p.y)) - smallRadius; 178 | } 179 | 180 | // A circle line. Can also be used to make a torus by subtracting the smaller radius of the torus. 181 | float $fCircle(vec3 p, float r) { 182 | float l = length(p.xz) - r; 183 | return length(vec2(p.y, l)); 184 | } 185 | 186 | // A circular disc with no thickness (i.e. a cylinder with no height). 187 | // Subtract some value to make a flat disc with rounded edge. 188 | float $fDisc(vec3 p, float r) { 189 | float l = length(p.xz) - r; 190 | return l < 0. ? abs(p.y) : length(vec2(p.y, l)); 191 | } 192 | 193 | // Hexagonal prism, circumcircle variant 194 | float $fHexagonCircumcircle(vec3 p, vec2 h) { 195 | vec3 q = abs(p); 196 | return max(q.y - h.y, max(q.x*sqrt(3.)*0.5 + q.z*0.5, q.z) - h.x); 197 | //this is mathematically equivalent to this line, but less efficient: 198 | //return max(q.y - h.y, max(dot(vec2(cos(PI/3), sin(PI/3)), q.zx), q.z) - h.x); 199 | } 200 | 201 | // Hexagonal prism, incircle variant 202 | float $fHexagonIncircle(vec3 p, vec2 h) { 203 | return $fHexagonCircumcircle(p, vec2(h.x*sqrt(3.)*0.5, h.y)); 204 | } 205 | 206 | // Cone with correct distances to tip and base circle. Y is up, 0 is in the middle of the base. 207 | float $fCone(vec3 p, float radius, float height) { 208 | vec2 q = vec2(length(p.xz), p.y); 209 | vec2 tip = q - vec2(0, height); 210 | vec2 mantleDir = normalize(vec2(height, radius)); 211 | float mantle = dot(tip, mantleDir); 212 | float d = max(mantle, -q.y); 213 | float projected = dot(tip, vec2(mantleDir.y, -mantleDir.x)); 214 | 215 | // distance to tip 216 | if ((q.y > height) && (projected < 0.)) { 217 | d = max(d, length(tip)); 218 | } 219 | 220 | // distance to base ring 221 | if ((q.x > radius) && (projected > length(vec2(height, radius)))) { 222 | d = max(d, length(q - vec2(radius, 0))); 223 | } 224 | return d; 225 | } 226 | 227 | // 228 | // "Generalized Distance Functions" by Akleman and Chen. 229 | // see the Paper at https://www.viz.tamu.edu/faculty/ergun/research/implicitmodeling/papers/sm99.pdf 230 | // 231 | // This set of constants is used to construct a large variety of geometric primitives. 232 | // Indices are shifted by 1 compared to the paper because we start counting at Zero. 233 | // Some of those are slow whenever a driver decides to not unroll the loop, 234 | // which seems to happen for fIcosahedron und fTruncatedIcosahedron on nvidia 350.12 at least. 235 | // Specialized implementations can well be faster in all cases. 236 | // 237 | 238 | // Macro based version for GLSL 1.2 / ES 2.0 by Tom 239 | 240 | #define $GDFVector0 vec3(1, 0, 0) 241 | #define $GDFVector1 vec3(0, 1, 0) 242 | #define $GDFVector2 vec3(0, 0, 1) 243 | 244 | #define $GDFVector3 normalize(vec3(1, 1, 1 )) 245 | #define $GDFVector4 normalize(vec3(-1, 1, 1)) 246 | #define $GDFVector5 normalize(vec3(1, -1, 1)) 247 | #define $GDFVector6 normalize(vec3(1, 1, -1)) 248 | 249 | #define $GDFVector7 normalize(vec3(0, 1, PHI+1.)) 250 | #define $GDFVector8 normalize(vec3(0, -1, PHI+1.)) 251 | #define $GDFVector9 normalize(vec3(PHI+1., 0, 1)) 252 | #define $GDFVector10 normalize(vec3(-PHI-1., 0, 1)) 253 | #define $GDFVector11 normalize(vec3(1, PHI+1., 0)) 254 | #define $GDFVector12 normalize(vec3(-1, PHI+1., 0)) 255 | 256 | #define $GDFVector13 normalize(vec3(0, PHI, 1)) 257 | #define $GDFVector14 normalize(vec3(0, -PHI, 1)) 258 | #define $GDFVector15 normalize(vec3(1, 0, PHI)) 259 | #define $GDFVector16 normalize(vec3(-1, 0, PHI)) 260 | #define $GDFVector17 normalize(vec3(PHI, 1, 0)) 261 | #define $GDFVector18 normalize(vec3(-PHI, 1, 0)) 262 | 263 | #define $fGDFBegin float d = 0.; 264 | 265 | // Version with variable exponent. 266 | // This is slow and does not produce correct distances, but allows for bulging of objects. 267 | #define $fGDFExp(v) d += pow(abs(dot(p, v)), e); 268 | 269 | // Version with without exponent, creates objects with sharp edges and flat faces 270 | #define $fGDF(v) d = max(d, abs(dot(p, v))); 271 | 272 | #define $fGDFExpEnd return pow(d, 1./e) - r; 273 | #define $fGDFEnd return d - r; 274 | 275 | // Primitives follow: 276 | 277 | float $fOctahedron(vec3 p, float r, float e) { 278 | $fGDFBegin 279 | $fGDFExp($GDFVector3) $fGDFExp($GDFVector4) $fGDFExp($GDFVector5) $fGDFExp($GDFVector6) 280 | $fGDFExpEnd 281 | } 282 | 283 | float $fDodecahedron(vec3 p, float r, float e) { 284 | $fGDFBegin 285 | $fGDFExp($GDFVector13) $fGDFExp($GDFVector14) $fGDFExp($GDFVector15) $fGDFExp($GDFVector16) 286 | $fGDFExp($GDFVector17) $fGDFExp($GDFVector18) 287 | $fGDFExpEnd 288 | } 289 | 290 | float $fIcosahedron(vec3 p, float r, float e) { 291 | $fGDFBegin 292 | $fGDFExp($GDFVector3) $fGDFExp($GDFVector4) $fGDFExp($GDFVector5) $fGDFExp($GDFVector6) 293 | $fGDFExp($GDFVector7) $fGDFExp($GDFVector8) $fGDFExp($GDFVector9) $fGDFExp($GDFVector10) 294 | $fGDFExp($GDFVector11) $fGDFExp($GDFVector12) 295 | $fGDFExpEnd 296 | } 297 | 298 | float $fTruncatedOctahedron(vec3 p, float r, float e) { 299 | $fGDFBegin 300 | $fGDFExp($GDFVector0) $fGDFExp($GDFVector1) $fGDFExp($GDFVector2) $fGDFExp($GDFVector3) 301 | $fGDFExp($GDFVector4) $fGDFExp($GDFVector5) $fGDFExp($GDFVector6) 302 | $fGDFExpEnd 303 | } 304 | 305 | float $fTruncatedIcosahedron(vec3 p, float r, float e) { 306 | $fGDFBegin 307 | $fGDFExp($GDFVector3) $fGDFExp($GDFVector4) $fGDFExp($GDFVector5) $fGDFExp($GDFVector6) 308 | $fGDFExp($GDFVector7) $fGDFExp($GDFVector8) $fGDFExp($GDFVector9) $fGDFExp($GDFVector10) 309 | $fGDFExp($GDFVector11) $fGDFExp($GDFVector12) $fGDFExp($GDFVector13) $fGDFExp($GDFVector14) 310 | $fGDFExp($GDFVector15) $fGDFExp($GDFVector16) $fGDFExp($GDFVector17) $fGDFExp($GDFVector18) 311 | $fGDFExpEnd 312 | } 313 | 314 | float $fOctahedron(vec3 p, float r) { 315 | $fGDFBegin 316 | $fGDF($GDFVector3) $fGDF($GDFVector4) $fGDF($GDFVector5) $fGDF($GDFVector6) 317 | $fGDFEnd 318 | } 319 | 320 | float $fDodecahedron(vec3 p, float r) { 321 | $fGDFBegin 322 | $fGDF($GDFVector13) $fGDF($GDFVector14) $fGDF($GDFVector15) $fGDF($GDFVector16) 323 | $fGDF($GDFVector17) $fGDF($GDFVector18) 324 | $fGDFEnd 325 | } 326 | 327 | float $fIcosahedron(vec3 p, float r) { 328 | $fGDFBegin 329 | $fGDF($GDFVector3) $fGDF($GDFVector4) $fGDF($GDFVector5) $fGDF($GDFVector6) 330 | $fGDF($GDFVector7) $fGDF($GDFVector8) $fGDF($GDFVector9) $fGDF($GDFVector10) 331 | $fGDF($GDFVector11) $fGDF($GDFVector12) 332 | $fGDFEnd 333 | } 334 | 335 | float $fTruncatedOctahedron(vec3 p, float r) { 336 | $fGDFBegin 337 | $fGDF($GDFVector0) $fGDF($GDFVector1) $fGDF($GDFVector2) $fGDF($GDFVector3) 338 | $fGDF($GDFVector4) $fGDF($GDFVector5) $fGDF($GDFVector6) 339 | $fGDFEnd 340 | } 341 | 342 | float $fTruncatedIcosahedron(vec3 p, float r) { 343 | $fGDFBegin 344 | $fGDF($GDFVector3) $fGDF($GDFVector4) $fGDF($GDFVector5) $fGDF($GDFVector6) 345 | $fGDF($GDFVector7) $fGDF($GDFVector8) $fGDF($GDFVector9) $fGDF($GDFVector10) 346 | $fGDF($GDFVector11) $fGDF($GDFVector12) $fGDF($GDFVector13) $fGDF($GDFVector14) 347 | $fGDF($GDFVector15) $fGDF($GDFVector16) $fGDF($GDFVector17) $fGDF($GDFVector18) 348 | $fGDFEnd 349 | } 350 | 351 | 352 | //////////////////////////////////////////////////////////////// 353 | // 354 | // DOMAIN MANIPULATION OPERATORS 355 | // 356 | //////////////////////////////////////////////////////////////// 357 | // 358 | // Conventions: 359 | // 360 | // Everything that modifies the domain is named pSomething. 361 | // 362 | // Many operate only on a subset of the three dimensions. For those, 363 | // you must choose the dimensions that you want manipulated 364 | // by supplying e.g. or 365 | // 366 | // is always the first argument and modified in place. 367 | // 368 | // Many of the operators partition space into cells. An identifier 369 | // or cell index is returned, if possible. This return value is 370 | // intended to be optionally used e.g. as a random seed to change 371 | // parameters of the distance functions inside the cells. 372 | // 373 | // Unless stated otherwise, for cell index 0,

is unchanged and cells 374 | // are centered on the origin so objects don't have to be moved to fit. 375 | // 376 | // 377 | //////////////////////////////////////////////////////////////// 378 | 379 | 380 | 381 | // Rotate around a coordinate axis (i.e. in a plane perpendicular to that axis) by angle . 382 | // Read like this: R(p.xz, a) rotates "x towards z". 383 | // This is fast if is a compile-time constant and slower (but still practical) if not. 384 | void $pR(inout vec2 p, float a) { 385 | p = cos(a)*p + sin(a)*vec2(p.y, -p.x); 386 | } 387 | 388 | // Shortcut for 45-degrees rotation 389 | void $pR45(inout vec2 p) { 390 | p = (p + vec2(p.y, -p.x))*sqrt(0.5); 391 | } 392 | 393 | // Repeat space along one axis. Use like this to repeat along the x axis: 394 | // - using the return value is optional. 395 | float $pMod1(inout float p, float size) { 396 | float halfsize = size*0.5; 397 | float c = floor((p + halfsize)/size); 398 | p = mod(p + halfsize, size) - halfsize; 399 | return c; 400 | } 401 | 402 | // Same, but mirror every second cell so they match at the boundaries 403 | float $pModMirror1(inout float p, float size) { 404 | float halfsize = size*0.5; 405 | float c = floor((p + halfsize)/size); 406 | p = mod(p + halfsize,size) - halfsize; 407 | p *= mod(c, 2.0)*2. - 1.; 408 | return c; 409 | } 410 | 411 | // Repeat the domain only in positive direction. Everything in the negative half-space is unchanged. 412 | float $pModSingle1(inout float p, float size) { 413 | float halfsize = size*0.5; 414 | float c = floor((p + halfsize)/size); 415 | if (p >= 0.) 416 | p = mod(p + halfsize, size) - halfsize; 417 | return c; 418 | } 419 | 420 | // Repeat only a few times: from indices to (similar to above, but more flexible) 421 | float $pModInterval1(inout float p, float size, float start, float stop) { 422 | float halfsize = size*0.5; 423 | float c = floor((p + halfsize)/size); 424 | p = mod(p+halfsize, size) - halfsize; 425 | if (c > stop) { //yes, this might not be the best thing numerically. 426 | p += size*(c - stop); 427 | c = stop; 428 | } 429 | if (c = (repetitions/2.)) c = abs(c); 449 | return c; 450 | } 451 | 452 | // Repeat in two dimensions 453 | vec2 $pMod2(inout vec2 p, vec2 size) { 454 | vec2 c = floor((p + size*0.5)/size); 455 | p = mod(p + size*0.5,size) - size*0.5; 456 | return c; 457 | } 458 | 459 | // Same, but mirror every second cell so all boundaries match 460 | vec2 $pModMirror2(inout vec2 p, vec2 size) { 461 | vec2 halfsize = size*0.5; 462 | vec2 c = floor((p + halfsize)/size); 463 | p = mod(p + halfsize, size) - halfsize; 464 | p *= mod(c,vec2(2))*2. - vec2(1); 465 | return c; 466 | } 467 | 468 | // Same, but mirror every second cell at the diagonal as well 469 | vec2 $pModGrid2(inout vec2 p, vec2 size) { 470 | vec2 c = floor((p + size*0.5)/size); 471 | p = mod(p + size*0.5, size) - size*0.5; 472 | p *= mod(c,vec2(2))*2. - vec2(1); 473 | p -= size/2.; 474 | if (p.x > p.y) p.xy = p.yx; 475 | return floor(c/2.); 476 | } 477 | 478 | // Repeat in three dimensions 479 | vec3 $pMod3(inout vec3 p, vec3 size) { 480 | vec3 c = floor((p + size*0.5)/size); 481 | p = mod(p + size*0.5, size) - size*0.5; 482 | return c; 483 | } 484 | 485 | // Mirror at an axis-aligned plane which is at a specified distance from the origin. 486 | float $pMirror (inout float p, float dist) { 487 | float s = sign(p); 488 | p = abs(p)-dist; 489 | return s; 490 | } 491 | 492 | // Mirror in both dimensions and at the diagonal, yielding one eighth of the space. 493 | // translate by dist before mirroring. 494 | vec2 $pMirrorOctant (inout vec2 p, vec2 dist) { 495 | vec2 s = sign(p); 496 | $pMirror(p.x, dist.x); 497 | $pMirror(p.y, dist.y); 498 | if (p.y > p.x) 499 | p.xy = p.yx; 500 | return s; 501 | } 502 | 503 | // Reflect space at a plane 504 | float $pReflect(inout vec3 p, vec3 planeNormal, float offset) { 505 | float t = dot(p, planeNormal)+offset; 506 | if (t < 0.) { 507 | p = p - (2.*t)*planeNormal; 508 | } 509 | return sign(t); 510 | } 511 | 512 | 513 | //////////////////////////////////////////////////////////////// 514 | // 515 | // OBJECT COMBINATION OPERATORS 516 | // 517 | //////////////////////////////////////////////////////////////// 518 | // 519 | // We usually need the following boolean operators to combine two objects: 520 | // Union: OR(a,b) 521 | // Intersection: AND(a,b) 522 | // Difference: AND(a,!b) 523 | // (a and b being the distances to the objects). 524 | // 525 | // The trivial implementations are min(a,b) for union, max(a,b) for intersection 526 | // and max(a,-b) for difference. To combine objects in more interesting ways to 527 | // produce rounded edges, chamfers, stairs, etc. instead of plain sharp edges we 528 | // can use combination operators. It is common to use some kind of "smooth minimum" 529 | // instead of min(), but we don't like that because it does not preserve Lipschitz 530 | // continuity in many cases. 531 | // 532 | // Naming convention: since they return a distance, they are called fOpSomething. 533 | // The different flavours usually implement all the boolean operators above 534 | // and are called fOpUnionRound, fOpIntersectionRound, etc. 535 | // 536 | // The basic idea: Assume the object surfaces intersect at a right angle. The two 537 | // distances and constitute a new local two-dimensional coordinate system 538 | // with the actual intersection as the origin. In this coordinate system, we can 539 | // evaluate any 2D distance function we want in order to shape the edge. 540 | // 541 | // The operators below are just those that we found useful or interesting and should 542 | // be seen as examples. There are infinitely more possible operators. 543 | // 544 | // They are designed to actually produce correct distances or distance bounds, unlike 545 | // popular "smooth minimum" operators, on the condition that the gradients of the two 546 | // SDFs are at right angles. When they are off by more than 30 degrees or so, the 547 | // Lipschitz condition will no longer hold (i.e. you might get artifacts). The worst 548 | // case is parallel surfaces that are close to each other. 549 | // 550 | // Most have a float argument to specify the radius of the feature they represent. 551 | // This should be much smaller than the object size. 552 | // 553 | // Some of them have checks like "if ((-a < r) && (-b < r))" that restrict 554 | // their influence (and computation cost) to a certain area. You might 555 | // want to lift that restriction or enforce it. We have left it as comments 556 | // in some cases. 557 | // 558 | // usage example: 559 | // 560 | // float fTwoBoxes(vec3 p) { 561 | // float box0 = fBox(p, vec3(1)); 562 | // float box1 = fBox(p-vec3(1), vec3(1)); 563 | // return fOpUnionChamfer(box0, box1, 0.2); 564 | // } 565 | // 566 | //////////////////////////////////////////////////////////////// 567 | 568 | 569 | // The "Chamfer" flavour makes a 45-degree chamfered edge (the diagonal of a square of size ): 570 | float $fOpUnionChamfer(float a, float b, float r) { 571 | float m = min(a, b); 572 | //if ((a < r) && (b < r)) { 573 | return min(m, (a - r + b)*sqrt(0.5)); 574 | //} else { 575 | return m; 576 | //} 577 | } 578 | 579 | // Intersection has to deal with what is normally the inside of the resulting object 580 | // when using union, which we normally don't care about too much. Thus, intersection 581 | // implementations sometimes differ from union implementations. 582 | float $fOpIntersectionChamfer(float a, float b, float r) { 583 | float m = max(a, b); 584 | if (r <= 0.) return m; 585 | if (((-a < r) && (-b < r)) || (m < 0.)) { 586 | return max(m, (a + r + b)*sqrt(0.5)); 587 | } else { 588 | return m; 589 | } 590 | } 591 | 592 | // Difference can be built from Intersection or Union: 593 | float $fOpDifferenceChamfer (float a, float b, float r) { 594 | return $fOpIntersectionChamfer(a, -b, r); 595 | } 596 | 597 | // The "Round" variant uses a quarter-circle to join the two objects smoothly: 598 | float $fOpUnionRound(float a, float b, float r) { 599 | float m = min(a, b); 600 | if ((a < r) && (b < r) ) { 601 | return min(m, r - sqrt((r-a)*(r-a) + (r-b)*(r-b))); 602 | } else { 603 | return m; 604 | } 605 | } 606 | 607 | float $fOpIntersectionRound(float a, float b, float r) { 608 | float m = max(a, b); 609 | if ((-a < r) && (-b < r)) { 610 | return max(m, -(r - sqrt((r+a)*(r+a) + (r+b)*(r+b)))); 611 | } else { 612 | return m; 613 | } 614 | } 615 | 616 | float $fOpDifferenceRound (float a, float b, float r) { 617 | return $fOpIntersectionRound(a, -b, r); 618 | } 619 | 620 | 621 | // The "Columns" flavour makes n-1 circular columns at a 45 degree angle: 622 | float $fOpUnionColumns(float a, float b, float r, float n) { 623 | if ((a < r) && (b < r)) { 624 | vec2 p = vec2(a, b); 625 | float columnradius = r*sqrt(2.)/((n-1.)*2.+sqrt(2.)); 626 | $pR45(p); 627 | p.x -= sqrt(2.)/2.*r; 628 | p.x += columnradius*sqrt(2.); 629 | if (mod(n,2.) == 1.) { 630 | p.y += columnradius; 631 | } 632 | // At this point, we have turned 45 degrees and moved at a point on the 633 | // diagonal that we want to place the columns on. 634 | // Now, repeat the domain along this direction and place a circle. 635 | $pMod1(p.y, columnradius*2.); 636 | float result = length(p) - columnradius; 637 | result = min(result, p.x); 638 | result = min(result, a); 639 | return min(result, b); 640 | } else { 641 | return min(a, b); 642 | } 643 | } 644 | 645 | float $fOpDifferenceColumns(float a, float b, float r, float n) { 646 | a = -a; 647 | float m = min(a, b); 648 | //avoid the expensive computation where not needed (produces discontinuity though) 649 | if ((a < r) && (b < r)) { 650 | vec2 p = vec2(a, b); 651 | float columnradius = r*sqrt(2.)/n/2.0; 652 | columnradius = r*sqrt(2.)/((n-1.)*2.+sqrt(2.)); 653 | 654 | $pR45(p); 655 | p.y += columnradius; 656 | p.x -= sqrt(2.)/2.*r; 657 | p.x += -columnradius*sqrt(2.)/2.; 658 | 659 | if (mod(n,2.) == 1.) { 660 | p.y += columnradius; 661 | } 662 | $pMod1(p.y,columnradius*2.); 663 | 664 | float result = -length(p) + columnradius; 665 | result = max(result, p.x); 666 | result = min(result, a); 667 | return -min(result, b); 668 | } else { 669 | return -m; 670 | } 671 | } 672 | 673 | float $fOpIntersectionColumns(float a, float b, float r, float n) { 674 | return $fOpDifferenceColumns(a,-b,r, n); 675 | } 676 | 677 | // The "Stairs" flavour produces n-1 steps of a staircase: 678 | float $fOpUnionStairs(float a, float b, float r, float n) { 679 | float d = min(a, b); 680 | vec2 p = vec2(a, b); 681 | $pR45(p); 682 | p = p.yx - vec2((r-r/n)*0.5*sqrt(2.)); 683 | p.x += 0.5*sqrt(2.)*r/n; 684 | float x = r*sqrt(2.)/n; 685 | $pMod1(p.x, x); 686 | d = min(d, p.y); 687 | $pR45(p); 688 | return min(d, $vmax(p -vec2(0.5*r/n))); 689 | } 690 | 691 | // We can just call Union since stairs are symmetric. 692 | float $fOpIntersectionStairs(float a, float b, float r, float n) { 693 | return -$fOpUnionStairs(-a, -b, r, n); 694 | } 695 | 696 | float $fOpDifferenceStairs(float a, float b, float r, float n) { 697 | return -$fOpUnionStairs(-a, b, r, n); 698 | } 699 | 700 | // This produces a cylindical pipe that runs along the intersection. 701 | // No objects remain, only the pipe. This is not a boolean operator. 702 | float $fOpPipe(float a, float b, float r) { 703 | return length(vec2(a, b)) - r; 704 | } 705 | -------------------------------------------------------------------------------- /shaders/lib/iq_sdf: -------------------------------------------------------------------------------- 1 | /* 2 | Taking IQ's https://www.shadertoy.com/view/Xds3zN and commenting it extensively to make it easier to learn from. 3 | */ 4 | 5 | // Created by inigo quilez - iq/2013 6 | // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. 7 | 8 | // A list of usefull distance function to simple primitives, and an example on how to 9 | // do some interesting boolean operations, repetition and displacement. 10 | // 11 | // More info here: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm 12 | 13 | // Antialiasing: number of samples in x and y dimensions 14 | #define AA 1 // make this 1 if your machine is too slow 15 | 16 | //------------------------------------------------------------------ 17 | // Distance functions 18 | // 19 | // Each function gives the distance from p to a primitive centered at the origin. 20 | // The position and orientation of the primitive is fixed. To simulate moving 21 | // the primitive, transform p in the opposite way. 22 | // 23 | // The s and u prefixes tell whether the result is signed or unsigned. 24 | // A signed function will return negative values for p inside the primitive; 25 | // an unsigned function will return 0. 26 | 27 | // Distance from p to plane (at y = 0) 28 | float $sdPlane( vec3 p ) 29 | { 30 | return p.y; 31 | } 32 | 33 | // Distance from p to sphere of radius s (centered at origin) 34 | float $sdSphere( vec3 p, float s ) 35 | { 36 | return length(p)-s; 37 | } 38 | 39 | // Distance from p to box whose half-dimensions are b.x, b.y, b.z 40 | float $sdBox( vec3 p, vec3 b ) 41 | { 42 | vec3 d = abs(p) - b; 43 | return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0)); 44 | } 45 | 46 | // Distance from p to ellipsoid the length of whose semi-principal axes is r.x, r.y, r.z 47 | float $sdEllipsoid( in vec3 p, in vec3 r ) 48 | { 49 | return (length( p/r ) - 1.0) * min(min(r.x,r.y),r.z); 50 | } 51 | 52 | // Distance from p to box of half-dimensions b.x,y,z plus buffer radius r 53 | float $udRoundBox( vec3 p, vec3 b, float r ) 54 | { 55 | return length(max(abs(p)-b,0.0))-r; 56 | } 57 | 58 | // Distance from p to horizontal torus with major radius t.x and minor radius t.y 59 | float $sdTorus( vec3 p, vec2 t ) 60 | { 61 | return length( vec2(length(p.xz)-t.x, p.y)) - t.y; 62 | } 63 | 64 | float $sdHexPrism( vec3 p, vec2 h ) 65 | { 66 | vec3 q = abs(p); 67 | #if 0 68 | return max(q.z-h.y,max((q.x*0.866025+q.y*0.5),q.y)-h.x); 69 | #else 70 | float d1 = q.z-h.y; 71 | float d2 = max((q.x*0.866025+q.y*0.5),q.y)-h.x; 72 | return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); 73 | #endif 74 | } 75 | 76 | // Distance from p to cylinder of radius r with spherical ends centered at a and b. 77 | // This is a rare exception to the rule that all primitives are centered at the origin. 78 | float $sdCapsule( vec3 p, vec3 a, vec3 b, float r ) 79 | { 80 | vec3 pa = p-a, ba = b-a; 81 | float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); 82 | return length( pa - ba*h ) - r; 83 | } 84 | 85 | float $sdTriPrism( vec3 p, vec2 h ) 86 | { 87 | vec3 q = abs(p); 88 | #if 0 89 | return max(q.z-h.y,max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5); 90 | #else 91 | float d1 = q.z-h.y; 92 | float d2 = max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5; 93 | return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); 94 | #endif 95 | } 96 | 97 | // Distance from p to cylinder with radius h.x and half-length h.y. 98 | float $sdCylinder( vec3 p, vec2 h ) 99 | { 100 | vec2 d = abs(vec2(length(p.xz),p.y)) - h; 101 | return min(max(d.x,d.y),0.0) + length(max(d,0.0)); 102 | } 103 | 104 | // Distance from p to a cone of height c.z whose vertex is at the origin 105 | // and is pointing up. 106 | float $sdCone( in vec3 p, in vec3 c ) 107 | { 108 | vec2 q = vec2(length(p.xz), p.y ); 109 | float d1 = -q.y - c.z; 110 | float d2 = max(dot(q,c.xy), q.y); 111 | return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); 112 | } 113 | 114 | float $sdConeSection( in vec3 p, in float h, in float r1, in float r2 ) 115 | { 116 | float d1 = -p.y - h; 117 | float q = p.y - h; 118 | float si = 0.5*(r1-r2)/h; 119 | float d2 = max( sqrt( dot(p.xz,p.xz)*(1.0-si*si)) + q*si - r2, q ); 120 | return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); 121 | } 122 | 123 | float $sdPryamid4(vec3 p, vec3 h ) // h = { cos a, sin a, height } 124 | { 125 | // Tetrahedron = Octahedron - Cube 126 | float box = $sdBox( p - vec3(0,-2.0*h.z,0), vec3(2.0*h.z) ); 127 | 128 | float d = 0.0; 129 | d = max( d, abs( dot(p, vec3( -h.x, h.y, 0 )) )); 130 | d = max( d, abs( dot(p, vec3( h.x, h.y, 0 )) )); 131 | d = max( d, abs( dot(p, vec3( 0, h.y, h.x )) )); 132 | d = max( d, abs( dot(p, vec3( 0, h.y,-h.x )) )); 133 | float octa = d - h.z; 134 | return max(-box,octa); // Subtraction 135 | } 136 | 137 | // Euclidean distance function (same as builtin length(p)?) 138 | float $length2( vec2 p ) 139 | { 140 | return sqrt( p.x*p.x + p.y*p.y ); 141 | } 142 | 143 | // Non-Euclidean distance function, so the sphere "length6(p) = k" is squarish. 144 | float $length6( vec2 p ) 145 | { 146 | p = p*p*p; p = p*p; 147 | return pow( p.x + p.y, 1.0/6.0 ); 148 | } 149 | 150 | // Non-Euclidean distance function, so the sphere "length8(p) = k" is more squarish. 151 | float $length8( vec2 p ) 152 | { 153 | p = p*p; p = p*p; p = p*p; 154 | return pow( p.x + p.y, 1.0/8.0 ); 155 | } 156 | 157 | // A torus with a squarish minor cross section, using non-Euclidean distance function. 158 | float $sdTorus82( vec3 p, vec2 t ) 159 | { 160 | vec2 q = vec2($length2(p.xz)-t.x,p.y); 161 | return $length8(q)-t.y; 162 | } 163 | 164 | // A torus with squarish major and minor cross sections, using non-Euclidean distance function. 165 | float $sdTorus88( vec3 p, vec2 t ) 166 | { 167 | vec2 q = vec2($length8(p.xz)-t.x,p.y); 168 | return $length8(q)-t.y; 169 | } 170 | 171 | // a cylinder with squarish horizontal cross-section, with radius h.x and half-length h.y. 172 | float $sdCylinder6( vec3 p, vec2 h ) 173 | { 174 | return max( $length6(p.xz)-h.x, abs(p.y)-h.y ); 175 | } 176 | 177 | //------------------------------------------------------------------ 178 | // CSG operations 179 | 180 | // subtract primitive 2 from primitive 1, where d1 is distance to primitive 1. 181 | float $opS( float d1, float d2 ) 182 | { 183 | return max(-d2,d1); 184 | } 185 | 186 | // union primitives 1 and 2 187 | // d1 is a vec2 where .x is the distance, and .y is the color/material code. 188 | vec2 $opU( vec2 d1, vec2 d2 ) 189 | { 190 | return (d1.x 2 | #export sdf_map 3 | #export sdf_ray_steps 4 | #export sdf_min_dist 5 | #export sdf_max_dist 6 | #export sdf_shadow_steps 7 | 8 | vec2 $sdf_map(in vec3 p) { 9 | return vec2(length(p)-3., 0.); 10 | } 11 | 12 | vec2 $cast( in vec3 ro, in vec3 rd ) 13 | { 14 | float t = @def.sdf_min_dist; 15 | float m = -1.0; 16 | for( int i=0; i < @def.sdf_ray_steps; i++ ) 17 | { 18 | float precis = 0.0005*t; 19 | vec2 res = @def.sdf_map( ro+rd*t ); 20 | if( res.x < precis || t > @def.sdf_max_dist ) break; 21 | t += res.x; 22 | m = res.y; 23 | } 24 | 25 | if( t > @def.sdf_max_dist ) m=-1.0; 26 | return vec2( t, m ); 27 | } 28 | 29 | vec3 $norms( in vec3 pos ) 30 | { 31 | vec2 e = vec2(1.0,-1.0)*0.5773*0.0005; 32 | return normalize( e.xyy*@def.sdf_map( pos + e.xyy ).x + 33 | e.yyx*@def.sdf_map( pos + e.yyx ).x + 34 | e.yxy*@def.sdf_map( pos + e.yxy ).x + 35 | e.xxx*@def.sdf_map( pos + e.xxx ).x ); 36 | } 37 | 38 | vec3 $normals( in vec3 pos ) 39 | { 40 | vec3 eps = vec3( 0.0005, 0.0, 0.0 ); 41 | vec3 nor = vec3( 42 | @def.sdf_map(pos+eps.xyy).x - @def.sdf_map(pos-eps.xyy).x, 43 | @def.sdf_map(pos+eps.yxy).x - @def.sdf_map(pos-eps.yxy).x, 44 | @def.sdf_map(pos+eps.yyx).x - @def.sdf_map(pos-eps.yyx).x ); 45 | return normalize(nor); 46 | } 47 | 48 | 49 | float $shadow( in vec3 point, in vec3 rd, in float mint, in float tmax ) 50 | { 51 | vec3 ro = point; 52 | float res = 1.0; 53 | float t = mint; 54 | for( int i=0; i < @def.sdf_shadow_steps; i++ ) 55 | { 56 | float h = @def.sdf_map( ro + rd*t ).x; 57 | res = min( res, 8.0*h/t ); 58 | t += clamp( h, 0.02, 0.10 ); 59 | if( h<0.001 || t>tmax ) break; 60 | } 61 | return clamp( res, 0.0, 1.0 ); 62 | } 63 | 64 | float $occlusion( vec3 pos, vec3 nor ) 65 | { 66 | float oc = 0.0; 67 | float sca = 1.0; 68 | for( int i=0; i<5; i++ ) 69 | { 70 | float hr = 0.01 + 0.12*float(i)/4.0; 71 | vec3 aopos = nor * hr + pos; 72 | float dd = @def.sdf_map( aopos ).x; 73 | oc += -(dd-hr)*sca; 74 | sca *= 0.95; 75 | } 76 | return clamp( 1.0 - 3.0*oc, 0.0, 1.0 ); 77 | } -------------------------------------------------------------------------------- /shaders/lib/terrain: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wizgrav/rayglider/850feaf3814c5587607a0deeffd6ff3e9ab6dcb9/shaders/lib/terrain -------------------------------------------------------------------------------- /shaders/lib/tes2.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wizgrav/rayglider/850feaf3814c5587607a0deeffd6ff3e9ab6dcb9/shaders/lib/tes2.7z -------------------------------------------------------------------------------- /shaders/lib/tes2.txt: -------------------------------------------------------------------------------- 1 | #import C < color > 2 | #import SDF < sdf > 3 | #import IQ < iq_sdf > 4 | #import CAM < camera > 5 | #import PHO < phong > 6 | 7 | #track - < https://wizgrav.github.io/rayglider/assets/MGMT-Kids_She_XCL_RMX.mp3 > 8 | #boom B1 < mix($B1, pow(length($0.xz) * 0.7, 4), 0.2) > 9 | #boom B2 < mix($B2,pow(length($1.xz) * 0.7, 4),0.2) > 10 | #band 0 11 | #band 1 12 | #export sdf_map 13 | #export sdf_ray_steps 14 | #export sdf_max_dist 15 | 16 | float $iter = 0.; 17 | 18 | vec3 $CMOD; 19 | vec2 $sdf_map(in vec3 p) { 20 | $iter++; 21 | vec3 c = $CMOD; 22 | vec3 q = mod(p,c)-0.5 * c; 23 | vec3 r = vec3(0.1 + 3. * $B1, 0.1 + 2. * $B2, 2. * $B1 + 2. * $B2); 24 | vec2 res = vec2(@IQ.sdSphere(q, r.x * 0.9), 0.); 25 | res = @IQ.opU(res, vec2(@IQ.sdTorus( q, r.xy * 0.8 ), 1.)); 26 | return res; 27 | } 28 | 29 | void $main(inout vec4 fragColor, vec2 fragCoord) { 30 | $CMOD = vec3(8. + $B1 * 4.,8. + $B2 * 4.,8. + $B1 * 4.); 31 | vec3 ro = vec3(28., 10. + sin(iTime * 0.2 + $B2) * 40., 30.); 32 | mat3 cam = @CAM.lookAt(ro, vec3(0.,$B1 ,0.), 0.66 * cos(iTime + $B2)); 33 | vec2 p = (-iRes.xy + 2.0*fragCoord)/iRes.y; 34 | vec3 rd = cam * normalize( vec3(p.xy,1.0) ); 35 | vec2 res = @SDF.cast(ro, rd); 36 | vec3 pt = ro + res.x * rd; 37 | vec3 sky = mix(vec3(0.4), vec3(0.6 + 0.4 * $B2),rd.y); 38 | vec3 col; 39 | vec3 nor = @SDF.normals(pt); 40 | vec3 hc = floor( (pt + nor )/ $CMOD ); 41 | float fg = exp( -0.00001*res.x*res.x*res.x ); 42 | if(res.x >= $sdf_max_dist) { 43 | col = sky + vec3($iter / 60.) * fg; 44 | } else { 45 | @PHO.Surface s; 46 | 47 | vec3 sc = res.y < 0.5 ? @C.rgb2hsv(vec3(hc.zxy * 0.66)): vec3(0.66); 48 | s.diffuse = sc; 49 | s.specular = sc; 50 | s.shiny = 12.; 51 | s.normal = nor; 52 | vec3 l = @PHO.direct(s, vec3(0.2 + 0.3 * $B2), vec3(1., 5., 3.)); 53 | l += @PHO.direct(s, vec3(0.5), vec3(-2., -5., 2.)); 54 | float ao = @SDF.occlusion(pt, nor); 55 | 56 | col = vec3(mix(0.1, 0.15 + 0.15 * $B2, s.normal.y)) * ao + l; 57 | 58 | col = mix( col, sky, 1.0-fg); 59 | } 60 | 61 | fragColor.rgb = col; 62 | } 63 | 64 | #pass ass < 0.5 0.5 float rel clamp repeat cube vmap map mips nomips noblend blend screen nop> 65 | #cube ass 66 | #image ass 67 | #video ass 68 | #import S < sdf > 69 | #export sdf_minDist < const float = 1.0 > 70 | #boom bass < mix(@, smoothstep(0., 0.1, abs($0.x - $0.x)), 0.2) > 71 | #band 0 < t: [0,1,2,3], s: [0.01, 0.01, 0.01, 0.01], b: [1, 23, 100, 110], a: [0.5, 0.3, 0.4, 0.4] > 72 | #track asas <> 73 | #net asas <> 74 | 75 | 76 | #import P < phong > 77 | #import IQ < iq_sdf > 78 | #export sdf_map < vec2 (vec3 p) > 79 | #export sdf_steps < const int = 100 > 80 | #export state < void (inout mat4[4]) > 81 | 82 | 83 | vec2 $sdf_map(vec3 p) { 84 | vec2 r = vec2( @IQ.sdTorus(p, vec2(10., 1.)), 1.0); 85 | r = @IQ.opU(r, @IQ.sdBox(p, vec3(1. 5. 8.))); 86 | return r; 87 | } 88 | 89 | void $main(out vec4 c, in vec2 uv) { 90 | @P.Surface s; 91 | vec2 dm = @S.march(vec3(0.), vRay, s.point); 92 | s.normals = @S.norm(s.point); 93 | s.specular = vec3(0., 0.5,0.2); 94 | s.diffuse = vec3(0., 0.5,0.2); 95 | s.shiny = 60.; 96 | vec3 l = @P.direct(s, vec3(0.33, 0.22, 0.3), vec3(0.33, 0.22, 0.3)); 97 | c.xyz = l * @S.shadow(s.point); 98 | c.xyz += vec3(0.2) * @S.ao(); 99 | } 100 | 101 | #import C < color > 102 | 103 | void $main(out vec4 c, in vec2 fc) { 104 | vec2 uv = fc / iRes.xy; 105 | c.rgb = @C.hsv2rgb(vec3( uv, 0.66 )); 106 | } 107 | -------------------------------------------------------------------------------- /shaders/lib/test.txt: -------------------------------------------------------------------------------- 1 | // El Jardí de Catalunya 2 | // by El Greco 3 | // https://tinyurl.com/y8sbqhz7 4 | #import C < color > 5 | #import SDF < sdf > 6 | #import CAM < camera > 7 | #import PHO < phong > 8 | #import WOR < noise/worley2D > 9 | #export sdf_map 10 | #export sdf_ray_steps 11 | #export sdf_max_dist 12 | #boom B0 < mix($B0, smoothstep(0.0, 0.2, length(abs($0.yw - $0.xz))), 0.08) > 13 | #boom B1 < mix($B1, smoothstep(0.0, 0.2, length(abs($1.xz - $1.yw))), 0.08)> 14 | #boom B2 < mix($B2, smoothstep(0.0, 0.1, length(abs($2.xz - $2.yw))), 0.06) > 15 | #boom B3 < mix($B3, smoothstep(0.0, 0.1, length(abs($3.xz - $3.yw))), 0.06) > 16 | #band 0 < b: 12 48 64 128;t: 0 0 4 4;s: 0.1 -0.1 0.1 -0.1;a: 1 1 1 1 > 17 | #band 1 < b: 36 60 48 128;t: 0 0 4 4;s: 0.1 -0.1 0.1 -0.1;a: 1 1 1 1 > 18 | #band 2 < b: 60 96 16 128;t: 0 0 4 4;s: 0.1 -0.1 0.1 -0.1;a: 1 1 1 1 > 19 | #band 3 < b: 84 108 1 96;t: 0 0 4 4;s: 0.1 -0.1 0.1 -0.1;a: 1 1 1 1 > 20 | #import DB < debug > 21 | #post PD < view: 0.2 0.2 0.4 0.1; screen; rel; blend; func: sra msa > 22 | 23 | vec3 $CMOD; 24 | 25 | vec4 $rand(vec2 n) { 26 | return fract( vec4(1.0, 255.0, 65025.0, 16581375.0) * fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453)); 27 | } 28 | 29 | float $smin( float a, float b, float k ) 30 | { 31 | float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 ); 32 | return mix( b, a, h ) - k*h*(1.0-h); 33 | } 34 | 35 | vec2 $sdf_map(in vec3 p) { 36 | p.xyz = p.xzy; 37 | vec3 c = $CMOD; 38 | vec3 q = mod(p,c)-0.5 * c; 39 | vec3 z = q; 40 | vec3 dz=vec3(0.0); 41 | vec3 fl = floor((p-q) / $CMOD) * $CMOD; 42 | vec4 r4 = $rand( fl.xy ); 43 | float power = 8.0 + 2. * floor(4. * r4.x); 44 | float r, theta, phi; 45 | float dr = 1.0; 46 | float t0 = 1.0; 47 | for(int i = 0; i < 4; ++i) { 48 | r = length(z); 49 | if(r > 2.0) continue; 50 | theta = atan(z.y / z.x) ; 51 | phi = asin(z.z / r) + iTime * 0.3 + 0.9 * mix($B0,1. - $B1, 0.66 + r4.x * 0.33); 52 | 53 | 54 | dr = pow(r, power - 1.) * dr * power + 1. ; 55 | 56 | r = pow(r, power); 57 | theta = theta * power; 58 | phi = phi * power; 59 | 60 | z = r * vec3(cos(theta)*cos(phi), sin(theta)*cos(phi), sin(phi)) + q; 61 | power += 2. * floor(4. * r4.y) + r4.x * 0.5; 62 | t0 = min(t0, r); 63 | r4.xyzw = r4.yzwx; 64 | } 65 | float rz = 0.5 * log(r) * r / dr; 66 | rz = $smin(q.z , rz, 0.24); 67 | return vec2(rz, mix( 0.1, t0, abs(q.z) * 0.96 )); 68 | } 69 | 70 | float fbm(vec2 uv) { 71 | float amp = 1., freq = 6., asum = 0., res = 0.; 72 | for(int i=0; i < 3; i++) { 73 | res += @WOR.noise(uv * freq , 1., false).x * amp; 74 | asum += amp; 75 | freq *= 2.; 76 | amp *= 0.5; 77 | } 78 | return res / asum; 79 | } 80 | 81 | void $main(inout vec4 fragColor, vec2 fragCoord) { 82 | vec4 bms = vec4($B0,$B1,$B2, $B3); 83 | #if defined @PD.PASS 84 | fragColor = @DB.bars(bms, (fragCoord - iView.xy) / iView.zw ); 85 | fragColor.a = 0.66; 86 | #else 87 | 88 | $CMOD = vec3(2.9 ,2.9,0. ); 89 | vec3 ro = vec3(4. + iTime * 0.33, 1.7 , 2.9); 90 | mat3 cam = @CAM.lookAt(ro, ro + vec3(4.,-6. ,0.), 0.); 91 | vec2 p = (-iRes.xy + 2.0*fragCoord)/iRes.y; 92 | vec3 rd = cam * normalize( vec3(p.xy,1.0) ); 93 | vec2 res = @SDF.cast(ro, rd); 94 | vec3 pt = ro + res.x * rd; 95 | vec3 sky = mix(vec3(0.4), vec3(0.6 + 0.4),rd.y); 96 | vec3 col; 97 | vec3 nor = @SDF.normals(pt); 98 | vec3 hc = floor((pt + nor )/ $CMOD ); 99 | if(res.x >= $sdf_max_dist) { 100 | col = sky; 101 | } else { 102 | @PHO.Surface s; 103 | float z = 0.; 104 | float mz = $sdf_map(vec3( pt.x, 1.0, pt.z)).x; 105 | float q = fbm(pt.xz * 0.1); 106 | z = fbm(pt.xz * 0.2 + vec2(0.01, 0.02) * iTime + vec2(0.2 * $B1,0.2 * $B2) + q); 107 | vec3 p = vec3(pt.x, 0.025 * z, pt.z ); 108 | vec3 nor2 = normalize(cross(dFdx(p),dFdy(p))); 109 | float f = smoothstep(0., 0.1, pt.y); 110 | nor = mix(nor2, nor, f); 111 | s.diffuse = vec3(@C.hsv2rgb(vec3(pow(res.y, 2.) , 1. - res.y, 0.66 - z * (1. - f) ))); 112 | s.specular = vec3(0.9); 113 | s.shiny = 32. * res.y; 114 | s.normal = nor; 115 | vec3 nv = normalize(vec3(2., 6., 2.)); 116 | float att = max(0.33, length(pow(bms,vec4(1.33)))); 117 | vec3 l = @PHO.direct(s, vec3(att), nv); 118 | float ao = @SDF.occlusion(pt, nor); 119 | col = mix(#001111, #333233 , s.normal.y) * ao * att + l * @SDF.shadow( pt, nv, 0.1, 4.0 ); 120 | 121 | col = pow(col, vec3(1./2.2)); 122 | 123 | } 124 | 125 | fragColor.rgb = col; 126 | #endif 127 | } 128 | -------------------------------------------------------------------------------- /shaders/lib/transform: -------------------------------------------------------------------------------- 1 | mat4 $rotate(vec3 v, float angle) 2 | { 3 | float c = cos(radians(angle)); 4 | float s = sin(radians(angle)); 5 | 6 | return mat4(c + (1.0 - c) * v.x * v.x, (1.0 - c) * v.x * v.y - s * v.z, (1.0 - c) * v.x * v.z + s * v.y, 0.0, 7 | (1.0 - c) * v.x * v.y + s * v.z, c + (1.0 - c) * v.y * v.y, (1.0 - c) * v.y * v.z - s * v.x, 0.0, 8 | (1.0 - c) * v.x * v.z - s * v.y, (1.0 - c) * v.y * v.z + s * v.x, c + (1.0 - c) * v.z * v.z, 0.0, 9 | 0.0, 0.0, 0.0, 1.0); 10 | } 11 | 12 | mat4 $translate(vec3 v) { 13 | return mat4(1.0,0.0,0.0,0.0, 14 | 0.0,1.0,0.0,0.0, 15 | 0.0,0.0,1.0,0.0, 16 | v.x,v.y,v.z,1.0); 17 | } 18 | 19 | mat4 $scale(vec3 v) { 20 | return mat4(v.x,0.0,0.0,0.0, 21 | 0.0,v.y,0.0,0.0, 22 | 0.0,0.0,v.z,0.0, 23 | 0.0,0.0,0.0,1.0); 24 | } -------------------------------------------------------------------------------- /src/asset.js: -------------------------------------------------------------------------------- 1 | var State = require("./state"); 2 | var twgl = require("twgl.js"); 3 | var util = require("./util"); 4 | 5 | twgl.setDefaults({crossOrigin: ""}); 6 | 7 | var cache = {}; 8 | 9 | 10 | function parseAsset(a, gl){ 11 | if(a.type === "net") { 12 | return { 13 | src: new Uint8Array(4) 14 | } 15 | } else { 16 | return { 17 | flipY: true, 18 | src: a.type === "video" ? null : a.data, 19 | target: a.type === "cube" ? gl.TEXTURE_CUBE_MAP: gl.TEXTURE_2D 20 | } 21 | } 22 | } 23 | 24 | module.exports = function (asset, config) { 25 | var gl = State.context; 26 | 27 | var uniforms = config.uniforms; 28 | 29 | var us1 = util.resolve("$tex", asset); 30 | 31 | if(!uniforms[us1]) return false; 32 | 33 | var us2 = util.resolve("$inf", asset); 34 | 35 | var tex = cache[asset.data]; 36 | 37 | var opts = parseAsset(asset, gl); 38 | 39 | var inf = new Float32Array(4); 40 | 41 | if( !tex){ 42 | var cb; 43 | if(asset.type === "video") { 44 | var el = document.createElement("video"); 45 | el.muted = true; 46 | el.loop = true; 47 | el.autoplay = true; 48 | el.crossOrigin = ""; 49 | el.playsinline = true; 50 | el.src = asset.data; 51 | tex = twgl.createTexture(gl, opts); 52 | tex.source = el; 53 | } else { 54 | tex = twgl.createTexture(gl, opts, function(err, img){ 55 | if(!err) return; 56 | tex.source = img; 57 | inf.set([img.naturalWidth, img.naturalHeight, 0, 0]) 58 | }); 59 | } 60 | cache[asset.data] = tex; 61 | } 62 | 63 | var ws = null, wsd = null; 64 | 65 | if(asset.type === "net") { 66 | ws = new WebSocket(asset.data, "rayglider"); 67 | ws.binaryType = 'arraybuffer'; 68 | ws.onmessage = function () { 69 | var data = e.data; 70 | var dv = new DataView(data); 71 | var w = Math.min(2048, dv.getUint16(0)); 72 | var h = Math.min(2048, dv.getUint16(2)); 73 | wsd = new Uint8Array(data, 4); 74 | inf.set([w,h,0,0]); 75 | } 76 | } 77 | uniforms[us1] = tex; 78 | 79 | uniforms[us2] = inf; 80 | 81 | return function (t) { 82 | if(ws) { 83 | if(t === false) { 84 | ws.close(); 85 | } else { 86 | twgl.setTextureFromArray(gl, tex, wsd, opts); 87 | } 88 | } else if(asset.type === "video"){ 89 | var ts = tex.source; 90 | if(t === false) { 91 | ts.pause(); 92 | } else if(ts.paused) { 93 | ts.play(); 94 | } else { 95 | if(ts.readyState && inf[2] !== ts.currentTime) { 96 | opts.width = ts.videoWidth; 97 | opts.height = ts.videoHeight; 98 | twgl.setTextureFromElement(gl, tex, ts, opts); 99 | inf.set([ts.videoWidth, ts.videoHeight, ts.currentTime, ts.duration]); 100 | } 101 | } 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /src/boom.js: -------------------------------------------------------------------------------- 1 | var State = require("./state"); 2 | var twgl = require("twgl.js"); 3 | var util = require("./util"); 4 | var GLSL = require('glsl-transpiler'); 5 | 6 | var resolve = util.resolve; 7 | 8 | var transpile = GLSL({ 9 | uniform: function (name) { 10 | return "uniforms."+name; 11 | } 12 | }); 13 | 14 | var head = [ 15 | "uniform float iTime;", 16 | "", 17 | "uniform float iDelta;", 18 | "", 19 | "uniform vec4 iMouse;", 20 | "", 21 | "uniform vec4 iPad0;", 22 | "", 23 | "uniform vec4 iPad1;", 24 | "", 25 | "uniform vec4 iPad2;", 26 | "", 27 | "uniform vec4 iPad3;", 28 | "" 29 | ]; 30 | 31 | var currentTime; 32 | var ret = { bands: [], booms: []}, lret; 33 | 34 | module.exports = function (asset, config) { 35 | var uniforms = config.uniforms; 36 | var head = [ 37 | "uniform float iTime;", 38 | "", 39 | "uniform float iDelta;", 40 | "", 41 | "uniform vec4 iMouse;", 42 | "", 43 | "uniform vec2 iTrack;", 44 | "", 45 | "uniform vec4 iPad0;", 46 | "", 47 | "uniform vec4 iPad1;", 48 | "", 49 | "uniform vec4 iPad2;", 50 | "", 51 | "uniform vec4 iPad3;", 52 | "" 53 | ]; 54 | 55 | var bands = {}; 56 | asset.ds.band.forEach(function(b){ 57 | var rep = {s: "smooth", a: "adapt", t: "template", b: "bounds"}; 58 | var obj = util.parseStyle(b[2]); 59 | for(var k in rep) obj[rep[k]] = obj[k]; 60 | 61 | if(obj.bounds) { 62 | obj.from = obj.bounds[0]; 63 | obj.to = obj.bounds[1]; 64 | obj.low = obj.bounds[2]; 65 | obj.high = obj.bounds[3]; 66 | } 67 | 68 | var us = util.resolve("$" + b[1], asset); 69 | head.push(["uniform vec4 ", us ,";"].join("")); 70 | bands[us] = State.clubber.band(obj); 71 | bands[us].target = uniforms[us] = new Float32Array(4); 72 | }); 73 | 74 | 75 | var used = false; 76 | 77 | var exec = []; 78 | 79 | var extra = {}; 80 | 81 | asset.ds.boom.forEach(function(b){ 82 | var arr = []; 83 | var us = resolve("$" + b[1], asset); 84 | var ub = resolve(b[2], asset, arr); 85 | if(uniforms[us]) used = true; 86 | uniforms[us] = 1; 87 | arr.forEach(function(v){ extra[v] = true }); 88 | head.push("float f" + b[1] + "(void){ return " + ub + ";}"); 89 | exec.push("uniforms." + us + " = f" + b[1] + "();"); 90 | }); 91 | 92 | Object.keys(extra).forEach(function (v) { head.unshift(["uniform float ", v,";"].join("")); }); 93 | 94 | if(used){ 95 | 96 | Object.keys(bands).forEach(function(b) { ret.bands.push(bands[b]); }) 97 | 98 | var src = [transpile( head.join("\n") ), exec.join("\n")].join("\n"); 99 | 100 | ret.booms.push(new Function("uniforms", "clubber", src)); 101 | } 102 | 103 | 104 | 105 | return function (t) { 106 | if(t === false || !lret) { 107 | lret = ret; 108 | ret = { bands: [], booms: []}; 109 | currentTime = 0; 110 | } 111 | var cl = State.clubber, step = 1000 / cl.fps; 112 | 113 | if(!currentTime || Math.abs(currentTime - cl.time) > 1000) currentTime = cl.time; 114 | 115 | var tmax = cl.time; 116 | cl.time = currentTime; 117 | 118 | for ( ; cl.time < tmax; cl.time += step) { 119 | lret.bands.forEach(function(b){ b(b.target); }); 120 | lret.booms.forEach(function(b){ b(uniforms, cl); }); 121 | } 122 | 123 | currentTime = cl.time; 124 | } 125 | } -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | var util = require("./util"); 2 | precedence = require('precedence-maps'); 3 | var pass = require("./pass"); 4 | var boom = require("./boom"); 5 | var track = require("./track"); 6 | var asset = require("./asset"); 7 | var lzma = util.lzma; 8 | var State = require("./state"); 9 | 10 | var config; 11 | 12 | function createConfig() { 13 | return { 14 | title: null, 15 | imports: {}, 16 | exports: {}, 17 | assets: {}, 18 | urls:{}, 19 | prepass:[], 20 | passes: [], 21 | postpass: [], 22 | uniforms: { 23 | iTime: 0, 24 | iDelta: 0, 25 | iFrame: 0, 26 | iFace: 0, 27 | iTrack: new Float32Array(2), 28 | iRes: new Float32Array(3), 29 | iView: new Float32Array(4), 30 | iMouse: new Float32Array(4), 31 | iPad0: new Float32Array(4), 32 | iPad1: new Float32Array(4), 33 | iPad2: new Float32Array(4), 34 | iPad3: new Float32Array(4), 35 | iCam: new Float32Array(16), 36 | iBoom: null 37 | }, 38 | assetCount: 1, 39 | text: "" 40 | } 41 | } 42 | 43 | var resolve = util.resolve; 44 | var rext = util.rext; 45 | 46 | var left = 0; 47 | 48 | function createObj(sid) { 49 | return { 50 | outgoing: [], 51 | ds: { 52 | import:[], export:[], pass:[], post: [], cube: [], image: [], video: [], boom: [], band: [], track: [], net: [] 53 | }, 54 | ns: {}, 55 | exports: {}, 56 | passes: [], 57 | post: [], 58 | booms: {}, 59 | bands: {}, 60 | text: "", 61 | type: "import", 62 | sid: sid 63 | } 64 | } 65 | 66 | function parse(t, u) { 67 | if(!t) return; 68 | var obj = config.imports[u], arr, ds; 69 | if(!obj) { 70 | obj = createObj(u); 71 | } 72 | if(!obj.text) { 73 | var ta = []; 74 | obj.origText = t; 75 | var firstLine = true; 76 | t.match(/[^\r\n]+/g).forEach(function(m) { 77 | if(!u && !config.title && m.substr(0,2) === "//" && firstLine) { 78 | config.title = m.substr(2); 79 | } 80 | 81 | firstLine = false; 82 | var sm = m.match(rext); 83 | if(!sm) { ta.push(m); return; } 84 | sm.shift(); 85 | var dsm = obj.ds[sm[0]]; 86 | if(!dsm) return; 87 | sm[2] = sm[2].trim(); 88 | dsm.push(sm); 89 | }); 90 | obj.text = ta.join("\n"); 91 | } 92 | 93 | if(!u) config.imports[u] = obj; 94 | 95 | if(!obj.processed){} 96 | var ds = obj.ds; 97 | 98 | ["cube", "image", "video", "net"].forEach(function(k) { 99 | obj.ds[k].forEach(function(sm) { 100 | var ou = config.urls[sm[2]]; 101 | if(!ou) { 102 | ou = config.urls[sm[2]] = { 103 | data: sm[2], 104 | text: "uniform " + (k === "cube" ? "samplerCube":"sampler2D") + " $tex;\nuniform vec4 $inf;\n\n", 105 | type: k 106 | } 107 | } 108 | if(!config.assets[sm[2]]) { 109 | config.assetCount++; 110 | config.assets[sm[2]] = ou; 111 | } 112 | obj.ns[sm[1]] = ou; 113 | }); 114 | }); 115 | 116 | ds.export.forEach(function(sm) { 117 | obj.exports[sm[1]] = sm[2]; 118 | }); 119 | 120 | ds.pass.forEach(function(sm) { 121 | var p = {data: sm[2], nop: sm[1] === "void", main: sm[1] === "main"}; 122 | obj.passes.push(p); 123 | obj.ns[sm[1]] = p; 124 | }); 125 | 126 | ds.post.forEach(function(sm) { 127 | var p = {data: sm[2], nop: sm[1] === "void", main: sm[1] === "main"}; 128 | obj.post.push(p); 129 | obj.ns[sm[1]] = p; 130 | }); 131 | 132 | ds.import.forEach(function(m){ 133 | 134 | var sm = m[2]; 135 | 136 | var imp = config.imports; 137 | if(imp[sm]){ 138 | obj.ns[m[1]] = imp[sm]; 139 | if(sm) imp[sm].outgoing.push(u); 140 | return; 141 | } 142 | 143 | left++; 144 | 145 | imp[sm] = obj.ns[m[1]] = createObj(sm); 146 | 147 | imp[sm].outgoing.push(u); 148 | 149 | if(sm.split(".").length === 1) { 150 | if(State.cache[sm]) { 151 | parse(State.cache[sm], sm); 152 | } else { 153 | fetch("shaders/lib/" + sm).then(function(r){ 154 | r.text().then(function(s) { 155 | State.cache[sm] = s; 156 | parse(s, sm); 157 | } ); 158 | }); 159 | } 160 | } else { 161 | var ss = window.localStorage.getItem(sm); 162 | if(ss) { 163 | decomparse(ss, sm); 164 | } else { 165 | var iframe = document.createElement("iframe"); 166 | iframe.setAttribute("name", "raygl$" + sm); 167 | iframe.style.display = "none"; 168 | document.body.appendChild(iframe); 169 | iframe.src = sm; 170 | imp[sm].iframe = iframe; 171 | } 172 | } 173 | }); 174 | 175 | left--; 176 | if(!left) finalize(); 177 | } 178 | 179 | function decomparse(s, name) { 180 | lzma.decompress(util.atob(s), function(s, error) { 181 | var c = config.imports[name]; 182 | if(c.iframe) document.body.removeChild(c.iframe); 183 | c.iframe = null; 184 | parse(s, name); 185 | }, function(percent) {}); 186 | } 187 | 188 | window.addEventListener("message", function(e){ 189 | var s = e.data.data; 190 | if(!e.data.raygl) return; 191 | var ss = util.getParameterByName("s", s); 192 | window.localStorage.setItem(e.data.name, ss); 193 | decomparse(ss, e.data.name); 194 | }, false); 195 | 196 | var Cb; 197 | 198 | function finalize() { 199 | precedence.setGraph("imports", { map: config.imports }); 200 | var order = precedence.getOrder("imports"); 201 | var idx = 1; 202 | 203 | var head = []; 204 | var body = []; 205 | 206 | var passes = []; 207 | var post = []; 208 | 209 | config.imports[""].exported = config.exports; 210 | 211 | Object.keys(config.assets).forEach(function(o, i) { 212 | var a = config.assets[o]; 213 | a.idx = idx++; 214 | head.push(resolve(a.text,a)); 215 | }); 216 | 217 | order.reverse().forEach(function(o, i) { 218 | var a = config.imports[o]; 219 | a.idx = o ? idx++ : ""; 220 | Object.keys(a.exports).forEach(function(v){ 221 | if(config.exports[v]) { 222 | return; 223 | } 224 | config.exports[v] = a; 225 | }); 226 | }); 227 | 228 | order.reverse().forEach(function(o, i) { 229 | var a = config.imports[o]; 230 | a.outgoing = []; 231 | if(a.passes.length) passes.push({data: "noblend", nop: true}); 232 | var mainPass = {data: "view: 1 1 0 0; rel; screen; noblend", idx: a.idx, parent: a}; 233 | 234 | function passFn(p){ 235 | p.idx = idx++; 236 | p.parent = a; 237 | if(!p.nop){ 238 | head.push("\n// tex: " + o); 239 | head.push(resolve("uniform sampler2D $tex;\n\n", p)); 240 | } 241 | } 242 | 243 | a.passes.forEach(function(p){ 244 | passFn(p); 245 | if(p.main){ mainPass.data = p.data; return; }; 246 | passes.push(p); 247 | }); 248 | 249 | a.post.forEach(function(p){ 250 | passFn(p); 251 | post.push(p); 252 | }); 253 | 254 | if(a.ds.boom.length) head.push("\n// boom: " + o); 255 | 256 | a.ds.boom.forEach(function(v) { 257 | head.push(resolve("uniform float $" + v[1] + ";\n\n", a)); 258 | }); 259 | body.push("\n// body: " + o); 260 | body.push(resolve(a.text,a)); 261 | if(o === ""){ 262 | passes.push(mainPass); 263 | } 264 | }); 265 | 266 | Object.keys(config.exports).forEach(function(k) { 267 | var o = config.exports[k]; 268 | var s = o.exports[k]; 269 | var a = s.split("("); 270 | head.push("\n// exported: " + o.sid); 271 | if(a.length > 1) { 272 | head.push([a[0], resolve("$" + k, o) + "(", a[1], ";\n"].join(" ")); 273 | return; 274 | } 275 | a = s.split("="); 276 | if(a.length > 1) { 277 | head.push([a[0], resolve("$" + k, o), "=", a[1], ";\n"].join(" ")); 278 | return; 279 | } 280 | }); 281 | 282 | config.text = [head.join("\n"), body.join("\n")].join("\n"); 283 | 284 | passes.forEach(function(p){ 285 | config.passes.push(pass(p, config)); 286 | }); 287 | 288 | post.forEach(function(p){ 289 | config.postpass.push(pass(p, config)); 290 | }); 291 | 292 | Object.keys(config.assets).forEach(function(o, i) { 293 | var a = config.assets[o]; 294 | var ast = asset(a, config); 295 | if(!ast) return; 296 | config.prepass.push(ast); 297 | }); 298 | 299 | var bm; 300 | order.forEach(function(o, i) { 301 | var a = config.imports[o]; 302 | bm = boom(a, config); 303 | }); 304 | 305 | if(bm) config.prepass.push(bm); 306 | 307 | var trk = track(config.imports[""], config); 308 | 309 | if(trk) config.prepass.push(trk); 310 | 311 | if(!State.error) { 312 | window.localStorage.setItem("", config.imports[""].origText); 313 | } 314 | 315 | Cb(config); 316 | 317 | return; 318 | } 319 | 320 | module.exports = function(t, cb) { 321 | config = createConfig(); 322 | Cb = cb; 323 | left = 1; 324 | parse(t, ""); 325 | } -------------------------------------------------------------------------------- /src/glsl.js: -------------------------------------------------------------------------------- 1 | var CodeMirror = require("codemirror"); 2 | CodeMirror.defineMode("glsl", function(config, parserConfig) { 3 | var indentUnit = config.indentUnit, 4 | keywords = parserConfig.keywords || {}, 5 | builtins = parserConfig.builtins || {}, 6 | blockKeywords = parserConfig.blockKeywords || {}, 7 | atoms = parserConfig.atoms || {}, 8 | hooks = parserConfig.hooks || {}, 9 | multiLineStrings = parserConfig.multiLineStrings; 10 | var isOperatorChar = /[+\-*&%=<>!?|\/]/; 11 | 12 | var curPunc; 13 | 14 | function tokenBase(stream, state) { 15 | if (state.startOfLine) { delete state.type2; state.tinc = 0;} 16 | var ch = stream.next(); 17 | if (hooks[ch]) { 18 | var result = hooks[ch](stream, state); 19 | if (result !== false) return result; 20 | } 21 | if (ch == '"' || ch == "'") { 22 | state.tokenize = tokenString(ch); 23 | return state.tokenize(stream, state); 24 | } 25 | if(state.type2 && state.tinc && ch !== "<" && ch !== ">") { 26 | stream.eatWhile(/:\w\.\//); 27 | return "link"; 28 | } 29 | if (/[\[\]{}\(\),;\:\.]/.test(ch)) { 30 | curPunc = ch; 31 | return "bracket"; 32 | } 33 | if (/\d/.test(ch)) { 34 | stream.eatWhile(/[\w\.]/); 35 | return "number"; 36 | } 37 | if (ch == "/") { 38 | if (stream.eat("*")) { 39 | state.tokenize = tokenComment; 40 | return tokenComment(stream, state); 41 | } 42 | if (stream.eat("/")) { 43 | stream.skipToEnd(); 44 | return "comment"; 45 | } 46 | } 47 | if (isOperatorChar.test(ch)) { 48 | stream.eatWhile(isOperatorChar); 49 | return "operator"; 50 | } 51 | stream.eatWhile(/[\#\w\$_]/); 52 | var cur = stream.current(); 53 | if (keywords.propertyIsEnumerable(cur)) { 54 | if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; 55 | return "keyword"; 56 | } 57 | if (builtins.propertyIsEnumerable(cur)) { 58 | return "builtin"; 59 | } 60 | if (atoms.propertyIsEnumerable(cur)) return "atom"; 61 | 62 | if(cur[0] === "@") { 63 | return "link nslink"; 64 | } 65 | if(cur[0] === "#") { 66 | state.type2 = cur; return "atom"; 67 | } else if(state.type2 && state.type2 in {"#video":1, "#image":1, "#cube":1, "#import":1, "#track":1, "#net":1}){ 68 | //console.log(cur, state.context.type, state.type2, ++state.tinc); 69 | state.tinc++; 70 | } 71 | return "word"; 72 | } 73 | 74 | function tokenString(quote) { 75 | return function(stream, state) { 76 | var escaped = false, next, end = false; 77 | while ((next = stream.next()) != null) { 78 | if (next == quote && !escaped) {end = true; break;} 79 | escaped = !escaped && next == "\\"; 80 | } 81 | if (end || !(escaped || multiLineStrings)) 82 | state.tokenize = tokenBase; 83 | return "string"; 84 | }; 85 | } 86 | 87 | function tokenComment(stream, state) { 88 | var maybeEnd = false, ch; 89 | while (ch = stream.next()) { 90 | if (ch == "/" && maybeEnd) { 91 | state.tokenize = tokenBase; 92 | break; 93 | } 94 | maybeEnd = (ch == "*"); 95 | } 96 | return "comment"; 97 | } 98 | 99 | function Context(indented, column, type, align, prev) { 100 | this.indented = indented; 101 | this.column = column; 102 | this.type = type; 103 | this.align = align; 104 | this.prev = prev; 105 | } 106 | function pushContext(state, col, type) { 107 | return state.context = new Context(state.indented, col, type, null, state.context); 108 | } 109 | function popContext(state) { 110 | var t = state.context.type; 111 | if (t == ")" || t == "]" || t == "}") 112 | state.indented = state.context.indented; 113 | return state.context = state.context.prev; 114 | } 115 | 116 | // Interface 117 | 118 | return { 119 | startState: function(basecolumn) { 120 | return { 121 | tokenize: null, 122 | context: new Context((basecolumn || 0) - indentUnit, 0, "top", false), 123 | indented: 0, 124 | startOfLine: true 125 | }; 126 | }, 127 | 128 | token: function(stream, state) { 129 | var ctx = state.context; 130 | if (stream.sol()) { 131 | if (ctx.align == null) ctx.align = false; 132 | state.indented = stream.indentation(); 133 | state.startOfLine = true; 134 | } 135 | if (stream.eatSpace()) return null; 136 | curPunc = null; 137 | var style = (state.tokenize || tokenBase)(stream, state); 138 | if (ctx.align == null) ctx.align = true; 139 | if (style == "comment" || style == "meta") return style; 140 | if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state); 141 | else if (curPunc == "{") pushContext(state, stream.column(), "}"); 142 | else if (curPunc == "[") pushContext(state, stream.column(), "]"); 143 | else if (curPunc == "(") pushContext(state, stream.column(), ")"); 144 | else if (curPunc == "}") { 145 | while (ctx.type == "statement") ctx = popContext(state); 146 | if (ctx.type == "}") ctx = popContext(state); 147 | while (ctx.type == "statement") ctx = popContext(state); 148 | } 149 | else if (curPunc == ctx.type) popContext(state); 150 | else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement")) 151 | pushContext(state, stream.column(), "statement"); 152 | state.startOfLine = false; 153 | return style; 154 | }, 155 | 156 | indent: function(state, textAfter) { 157 | if (state.tokenize != tokenBase && state.tokenize != null) return 0; 158 | var firstChar = textAfter && textAfter.charAt(0), ctx = state.context, closing = firstChar == ctx.type; 159 | if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : indentUnit); 160 | else if (ctx.align) return ctx.column + (closing ? 0 : 1); 161 | else return ctx.indented + (closing ? 0 : indentUnit); 162 | }, 163 | 164 | electricChars: "{}", 165 | fold: "brace" 166 | }; 167 | }); 168 | 169 | (function() { 170 | function words(str) { 171 | var obj = {}, words = str.split(" "); 172 | for (var i = 0; i < words.length; ++i) obj[words[i]] = true; 173 | return obj; 174 | } 175 | var glslKeywords = "const uniform break continue " + 176 | "do for while if else switch case in out inout float int uint void bool true false " + 177 | "invariant discard return mat2 mat3 mat2x2 mat2x3 mat2x4 mat3x2 mat3x3 mat3x4 mat2x4 mat4x3 mat4x4 " + 178 | "mat4 vec2 vec3 vec4 ivec2 ivec3 ivec4 uvec2 uvec3 uvec4 bvec2 bvec3 bvec4 sampler2D " + 179 | "samplerCube sampler3D struct"; 180 | var glslBuiltins = "iTime iDelta iFrame iPass iFace iMouse iBoom iTrack iView iPad0 iPad1 iPad2 iPad3 iRes radians degrees sin cos tan asin acos atan pow sinh cosh tanh asinh acosh atanh " + 181 | "exp log exp2 log2 sqrt inversesqrt abs sign floor ceil round trunc fract mod modf " + 182 | "min max clamp mix step smoothstep length distance dot cross " + 183 | "determinant inverse normalize faceforward reflect refract matrixCompMult outerProduct transpose lessThan " + 184 | "lessThanEqual greaterThan greaterThanEqual equal notEqual any all not packUnorm2x16 unpackUnorm2x16 packSnorm2x16 unpackSnorm2x16 packHalf2x16 unpackHalf2x16 " + 185 | "dFdx dFdy fwidth textureSize texture textureProj textureLod textureGrad texelFetch texelFetchOffset " + 186 | "textureProjLod textureLodOffset textureGradOffset textureProjLodOffset textureProjGrad intBitsToFloat uintBitsToFloat floatBitsToInt floatBitsToUint isnan isinf"; 187 | 188 | function cppHook(stream, state) { 189 | if (!state.startOfLine) return false; 190 | stream.skipToEnd(); 191 | return "meta"; 192 | } 193 | 194 | // C#-style strings where "" escapes a quote. 195 | function tokenAtString(stream, state) { 196 | var next; 197 | while ((next = stream.next()) != null) { 198 | if (next == '"' && !stream.eat('"')) { 199 | state.tokenize = null; 200 | break; 201 | } 202 | } 203 | return "string"; 204 | } 205 | 206 | CodeMirror.defineMIME("text/x-glsl", { 207 | name: "glsl", 208 | keywords: words(glslKeywords), 209 | builtins: words(glslBuiltins), 210 | blockKeywords: words("case do else for if switch while struct"), 211 | atoms: words("null") 212 | }); 213 | }()); -------------------------------------------------------------------------------- /src/mouse.js: -------------------------------------------------------------------------------- 1 | var util = require("./util"); 2 | var State = require("./state"); 3 | 4 | var clicked = false, data = new Float32Array(4); 5 | 6 | window.addEventListener("mousemove", function(e) { 7 | if(!clicked) return; 8 | data[0] = e.clientX; 9 | data[1] = e.clientY; 10 | }); 11 | 12 | window.addEventListener("mousedown", function(e) { 13 | data[2] = e.clientX; 14 | data[3] = e.clientY; 15 | clicked = true; 16 | }); 17 | 18 | window.addEventListener("mouseup", function(e) { 19 | clicked = false; 20 | data[2] = data[3] = 0; 21 | }); 22 | 23 | module.exports = function () { 24 | State.config.uniforms.iMouse.set(data); 25 | } -------------------------------------------------------------------------------- /src/pads.js: -------------------------------------------------------------------------------- 1 | var util = require("./util"); 2 | var State = require("./state"); 3 | 4 | State.netData = new Float32Array(4 * 6); 5 | 6 | var pads, active=[true,false,false,false]; 7 | 8 | function select() { 9 | 10 | var a = ["Provide up to 4, space separated, of the ids below to use as the active pads:",""]; 11 | var gm = navigator.getGamepads(), sm = []; 12 | pads = [false, true]; 13 | Array.prototype.forEach.call(gm, function(g, i){ if(g) pads.push(g)}); 14 | pads.forEach(function(v,i){ 15 | if(v === false) { 16 | a.push("0: Disable"); 17 | } else if(v === true) { 18 | a.push("1: Keyboard(Arrows/wasd + R + F)"); 19 | } else { 20 | a.push(i+": "+v.id); 21 | } 22 | }); 23 | var s = prompt(a.join("\n")); 24 | if(!s) return; 25 | active = s.split(" ").map(function(v) { 26 | var idx = parseInt(v); 27 | return pads[idx] ? pads[idx] : false; 28 | }); 29 | 30 | while(active.length < 4) { 31 | active.push(false); 32 | } 33 | } 34 | 35 | var keyMap = { 36 | "ArrowDown": false, 37 | "ArrowUp": false, 38 | "ArrowLeft": false, 39 | "ArrowRight": false, 40 | "w": false, 41 | "a": false, 42 | "s": false, 43 | "d": false, 44 | "e": false, 45 | "r": false, 46 | "c": false, 47 | "v": false 48 | } 49 | var keys = [0, 0, 0, 0]; 50 | 51 | window.addEventListener("keydown", function(e){ 52 | // console.log(e.key); 53 | if(e.key in keyMap) keyMap[e.key] = true; 54 | }); 55 | 56 | window.addEventListener("keyup", function(e){ 57 | if(e.key in keyMap) keyMap[e.key] = false; 58 | }); 59 | 60 | module.exports = function (t) { 61 | if(t===false) { select(); return;} 62 | keys[0] = keyMap["ArrowLeft"] || keyMap["a"] ? -1 : (keyMap["ArrowRight"] || keyMap["d"] ? 1 : 0); 63 | keys[1] = keyMap["ArrowDown"] || keyMap["s"] ? -1 : (keyMap["ArrowUp"] || keyMap["w"] ? 1 : 0); 64 | keys[2] = keyMap["c"] ? -1 : (keyMap["e"] ? 1 : 0); 65 | keys[3] = keyMap["v"] ? -1 : (keyMap["r"] ? 1 : 0); 66 | 67 | var u = State.config.uniforms; 68 | var gm = navigator.getGamepads(); 69 | for(var i=0; i< 4; i++){ 70 | var v = active[i]; 71 | var uv = u["iPad"+i]; 72 | if(v === true) { 73 | uv.set(keys); 74 | } else if(v.id) { 75 | var g = gm[v.index]; 76 | 77 | uv[0] = g.axes[0]; 78 | uv[1] = g.axes[1]; 79 | var btp = [false, false], btv = [0, 0], btm = [1, 1]; 80 | for(var j=0; j < g.buttons.length; j++) { 81 | var bt = g.buttons[j]; 82 | if(bt.pressed) { 83 | btp[j & 1] = true; 84 | btv[j & 1] = bt.value; 85 | btm[j & 1] = (j & 3) > 1 ? -1 : 1; 86 | } 87 | } 88 | uv[2] = btp[0] ? btv[0] * btm[0]: 0; 89 | uv[3] = btp[1] ? btv[1] * btm[1]: 0; 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /src/pass.js: -------------------------------------------------------------------------------- 1 | var State = require("./state"); 2 | var twgl = require("twgl.js"); 3 | var util = require("./util"); 4 | var resolve = util.resolve; 5 | var buf; 6 | 7 | var vs = [ 8 | "attribute vec2 pos;", 9 | "void main(void) ", 10 | "{", 11 | " vec4 p = vec4(0.,0.,1.,1.);", 12 | " gl_Position = vec4(pos.xy * p.zw + p.xy,0.0,1.0);", 13 | "}" 14 | ].join("\n"); 15 | 16 | var fsHead = [ 17 | "#define PI 3.14159265359", 18 | "#define PI2 6.28318530718", 19 | "#define PHI 1.618033988749895", 20 | "", 21 | "#define saturate(x) clamp(x, 0., 1.)", 22 | "", 23 | "uniform float iTime;", 24 | "", 25 | "uniform float iDelta;", 26 | "", 27 | "uniform int iFrame;", 28 | "", 29 | "uniform int iFace;", 30 | "", 31 | "uniform int iPass;", 32 | "", 33 | "uniform vec2 iTrack;", 34 | "", 35 | "uniform vec4 iView;", 36 | "", 37 | "uniform vec3 iRes;", 38 | "", 39 | "uniform mat4 iCam;", 40 | "", 41 | "uniform vec4 iMouse;", 42 | "", 43 | "uniform vec4 iPad0;", 44 | "", 45 | "uniform vec4 iPad1;", 46 | "", 47 | "uniform vec4 iPad2;", 48 | "", 49 | "uniform vec4 iPad3;", 50 | "", 51 | "uniform sampler2D iBoom;", 52 | "" 53 | ].join("\n"); 54 | 55 | var fsFoot = [ 56 | "void main( void )", 57 | "{ ", 58 | " vec4 color = vec4(0., 0., 0., 1.);", 59 | " $main(color, gl_FragCoord.xy);", 60 | " gl_FragColor = color;", 61 | "}" 62 | ].join("\n"); 63 | 64 | var Eqs = { 65 | "add": "FUNC_ADD", 66 | "sub": "FUNC_SUBTRACT", 67 | "rev": "FUNC_REVERSE_SUBTRACT", 68 | "min": "MIN_EXT", 69 | "max": "MAX_EXT" 70 | } 71 | 72 | var Funcs = { 73 | "one": "ONE", 74 | "zero": "ZERO", 75 | "src": "SRC_COLOR", 76 | "msc": "ONE_MINUS_SRC_COLOR", 77 | "dsc": "DST_COLOR", 78 | "mdc": "ONE_MINUS_DST_COLOR", 79 | "sra": "SRC_ALPHA", 80 | "msa": "ONE_MINUS_SRC_ALPHA", 81 | "dsa": "DST_ALPHA", 82 | "mda": "ONE_MINUS_DST_ALPHA", 83 | "sat": "SRC_ALPHA_SATURATE" 84 | } 85 | 86 | var Wrap = { 87 | "mirror": "MIRRORED_REPEAT", 88 | "clamp": "CLAMP_TO_EDGE", 89 | "repeat": "REPEAT" 90 | } 91 | 92 | module.exports = function (asset, config) { 93 | var uniforms = config.uniforms; 94 | var gl = State.context; 95 | var us1 = util.resolve("$tex", asset); 96 | var us2 = util.resolve("$inf", asset); 97 | var arr = asset.data.split(" "); 98 | 99 | var opts = util.parseStyle(asset.data), coords = [], funcs, eqs, clear, fmin, fmag, wrap, mips = 0, vmap = false, rmap = false, vray=false; 100 | 101 | Object.keys(opts).forEach(function (k) { 102 | var v = opts[k]; 103 | switch(k) { 104 | case "view": coords = v; break; 105 | case "func": funcs = v.map(function(v){ return gl[Funcs[v]]; }); break; 106 | case "eq": eqs = v.map(function(v){ return gl[Eqs[v]]; }); break; 107 | case "mag": fmag = v[0].toUpperCase(); break; 108 | case "min": fmin = v.map(function (s) { return s.toUpperCase(); }); break; 109 | case "wrap": wrap = v.map(function(v){ return gl[Wrap[v]]; }); break; 110 | case "map": mips = parseInt(v[0]); break; 111 | case "vmap": mips = parseInt(v[0]); vmap = true; break; 112 | case "rmap": mips = parseInt(v[0]); rmap = true; break; 113 | case "clear": clear = v.map(function(v){ return parseFloat(v); }); break; 114 | case "vray": vray = parseInt(v[0]); break; 115 | default: break; 116 | } 117 | 118 | }); 119 | 120 | if(!fmin) { 121 | fmin = ["LINEAR", "LINEAR"]; 122 | } else if(fmin.length < 2) { 123 | fmin.push(fmin[0]); 124 | } 125 | 126 | if(!fmag) { 127 | fmag = "LINEAR"; 128 | } 129 | 130 | if(!wrap) { 131 | wrap = [gl.CLAMP_TO_EDGE, gl.CLAMP_TO_EDGE]; 132 | } 133 | 134 | if(!opts.rel) { 135 | coords = coords.map(function(n) { return Math.floor(n); }); 136 | } 137 | 138 | if(!coords.length){ 139 | coords = [1,1,0,0]; 140 | } else if(coords.length == 1) { 141 | coords.push(coords[0]); 142 | } 143 | 144 | if(coords.length < 4) coords[2] = coords[3] = 0; 145 | 146 | var fbs = [], mfbs = [], inf = {}; 147 | inf[us2] = new Float32Array(4); 148 | 149 | if(!opts.screen && !opts.nop) { 150 | var flmin = gl[opts.mips ? fmin.join("_MIPMAP_"): fmin[0]]; 151 | var ft = opts.float ? gl.FLOAT : gl.UNSIGNED_BYTE; 152 | var att = [ 153 | {type: ft, min:flmin, mag:gl[fmag], wrapS: wrap[0], wrapT: wrap[1], flipY: opts.flip, auto: opts.mips, target: opts.cube ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D} 154 | ] 155 | for(var i=0; i < mips; i++) { 156 | mfbs.push(twgl.createFramebufferInfo(gl, att)); 157 | } 158 | 159 | fbs.push(twgl.createFramebufferInfo(gl, att)); 160 | fbs.push(twgl.createFramebufferInfo(gl, att)); 161 | 162 | } 163 | 164 | var width = 0, height = 0, offsetX, offsetY; 165 | 166 | if(!opts.rel){ 167 | width = coords[0]; 168 | height = coords[1]; 169 | if(!fbs.length) { 170 | offsetX = coords[2]; 171 | offsetY = coords[3]; 172 | } 173 | mfbs.forEach(function(fb) { 174 | twgl.resizeFramebufferInfo(gl, fb, att, width, height); 175 | if(!vmap) width = Math.floor(width * 0.5); 176 | height = Math.floor(height * 0.5); 177 | }); 178 | fbs.forEach(function(fb){ 179 | twgl.resizeFramebufferInfo(gl, fb, att, width, height); 180 | }); 181 | } else { 182 | resize(); 183 | } 184 | 185 | var prog = null; 186 | 187 | if(!asset.nop) { 188 | var defines = [ "#define " + resolve("$PASS", asset) + " 1" ]; 189 | if(vray) defines.push("#define ENABLE_VRAY 1"); 190 | 191 | fs = [ 192 | State.header, 193 | defines.join("\n"), 194 | fsHead, config.text, 195 | resolve(fsFoot, asset.parent) 196 | ].join("\n"); 197 | // console.log(fs); 198 | 199 | prog = twgl.createProgramInfo(gl, [vs, fs], function(msg){ 200 | State.error = msg; 201 | }); 202 | if(prog) { 203 | Object.keys(prog.uniformSetters).forEach(function (k) { 204 | if(!(k in uniforms)) uniforms[k] = 1; 205 | }) 206 | } 207 | } 208 | 209 | function resize() { 210 | width = Math.floor(gl.canvas.width * coords[0]); 211 | height = Math.floor(gl.canvas.height * coords[1]); 212 | if(!fbs.length) { 213 | offsetX = Math.floor(gl.canvas.width * coords[2]); 214 | offsetY = Math.floor(gl.canvas.height * coords[3]); 215 | } 216 | fbs.forEach(function(fb){ 217 | twgl.resizeFramebufferInfo(gl, fb, att, width, height); 218 | }); 219 | } 220 | 221 | return function (t) { 222 | if(t === false && prog) { 223 | gl.deleteProgram(prog.program); 224 | fbs.forEach(function(fb) { 225 | gl.deleteTexture(fb.attachments[0]); 226 | gl.deleteFramebuffer(fb.framebuffer); 227 | }); 228 | return; 229 | } 230 | 231 | if(opts.noblend) gl.disable(gl.BLEND); 232 | if(opts.blend) gl.enable(gl.BLEND); 233 | 234 | if(eqs) { 235 | if(eqs.length === 1) { 236 | gl.blendEquation(eqs[0]); 237 | } else if(eqs.length === 2) { 238 | gl.blendEquationSeparate(eqs[0], eqs[1]); 239 | } 240 | } 241 | 242 | if(funcs) { 243 | if(funcs.length === 2) { 244 | gl.blendFunc(funcs[0], funcs[1]); 245 | } else if(funcs.length === 4) { 246 | gl.blendFuncSeparate(funcs[0], funcs[1], funcs[2], funcs[3]); 247 | } 248 | } 249 | 250 | if(asset.nop) return; 251 | 252 | if(!buf) { 253 | buf = twgl.createBufferInfoFromArrays(gl, {pos: [-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0]}); 254 | } 255 | 256 | if(opts.rel && State.needResize) { 257 | resize(); 258 | } 259 | 260 | if(opts.rel && State.needResize) { 261 | resize(); 262 | } 263 | 264 | if(clear) { 265 | gl.clearColor.apply(gl, clear); 266 | gl.clear(gl.COLOR_BUFFER_BIT); 267 | } 268 | 269 | var fc = uniforms.iFrame; 270 | gl.useProgram(prog.program); 271 | var view = [offsetX, offsetY, width, height] 272 | gl.viewport(view[0], view[1], view[2], view[3]); 273 | uniforms.iView.set(view); 274 | twgl.setBuffersAndAttributes(gl, prog, buf); 275 | twgl.setUniforms(prog, uniforms); 276 | 277 | var lastFb = null; 278 | 279 | mfbs.forEach(function(fb, i) { 280 | gl.bindFramebuffer(gl.FRAMEBUFFER, fb.framebuffer); 281 | twgl.setUniforms(prog, { iPass: i }); 282 | draw(fb.attachments[0]); 283 | uniforms[us1] = fb.attachments[0]; 284 | }); 285 | 286 | var fb = fbs.length ? fbs[fc & 1] : null; 287 | gl.bindFramebuffer(gl.FRAMEBUFFER, fb ? fb.framebuffer : null); 288 | draw(fb ? fb.attachments[0] : null); 289 | 290 | if(fbs.length) { 291 | uniforms[us1] = fb.attachments[0]; 292 | if(opts.mips) { 293 | var tr = opts.cube ? gl.TEXTURE_CUBE_MAP: gl.TEXTURE_2D; 294 | gl.bindTexture(tr, uniforms[us1]); 295 | gl.generateMipmaps(tr); 296 | } 297 | } 298 | 299 | function draw(tex) { 300 | if(opts.cube && tex) { 301 | for(var i=0; i < 6; i++){ 302 | twgl.setUniforms(prog, { iFace: i }); 303 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X+i, tex, 0); 304 | twgl.drawBufferInfo(gl, buf); 305 | } 306 | } else { 307 | twgl.drawBufferInfo(gl, buf); 308 | } 309 | } 310 | 311 | } 312 | } -------------------------------------------------------------------------------- /src/state.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | canvasScale: 1, 3 | currentTime: 0, 4 | offsetTime: 0, 5 | frameCount: 0, 6 | fpsTime: 0, 7 | fpsCount: 0, 8 | contextId: 0, 9 | context: null, 10 | paused: false, 11 | config: null, 12 | cache: {}, 13 | text: null 14 | }; -------------------------------------------------------------------------------- /src/track.js: -------------------------------------------------------------------------------- 1 | var State = require("./state"); 2 | var audio = document.querySelector("audio"); 3 | audio.crossOrigin = "anonymous"; 4 | 5 | var lastObjectUrl = null; 6 | var CLIENT_ID = "56c4f3443da0d6ce6dcb60ba341c4e8d"; 7 | var stream = null; 8 | 9 | var tapEl = document.querySelector("#tap"); 10 | tapEl.userTapped = false; 11 | tapEl.addEventListener("click", function() { 12 | if(tapEl.userTapped) return; 13 | audio.removeAttribute("muted"); 14 | tapEl.style.display = "none"; 15 | tapEl.userTapped = true; 16 | audio.muted = false; 17 | State.clubber.context.resume(); 18 | document.body.classList.remove("muted"); 19 | }); 20 | 21 | function play (src, dropped) { 22 | var clubber = State.clubber; 23 | 24 | if(audio.src == src) return; 25 | 26 | if(lastObjectUrl){ 27 | URL.revokeObjectURL(lastObjectUrl); 28 | lastObjectUrl = null; 29 | } 30 | if(stream) { 31 | var track = stream.getTracks()[0]; 32 | track.stop(); 33 | stream = null; 34 | } 35 | if(src instanceof MediaStream) { 36 | clubber.listen(clubber.context.createMediaStreamSource(src)); 37 | stream = src; 38 | return; 39 | } else { 40 | clubber.listen(audio); 41 | clubber.muted = false; 42 | if(dropped) { 43 | lastObjectUrl = src; 44 | } 45 | } 46 | audio.src=src; 47 | audio.play(); 48 | 49 | } 50 | 51 | audio.onerror = function () { 52 | console.warn( 53 | audio.currentSrc.match("blob:") ? 54 | "Bad audio file" 55 | : 56 | "Soundcloud API limit reached, try again later.\n\n" 57 | ); 58 | fallback(); 59 | } 60 | 61 | var info = document.querySelector("#info"); 62 | 63 | function updateInfo(url, text) { 64 | info.innerHTML = "Listening to " + text + ""; 65 | } 66 | 67 | function soundcloud(url) { 68 | fetch("//api.soundcloud.com/resolve?url=" + encodeURIComponent(url.split("?")[0]) + "&client_id=" + CLIENT_ID) 69 | .then(function (resp) { 70 | resp.json().then(function(data){ 71 | if (data.kind !== "track"){ 72 | console.warn( "Please provide a track url, " + data.kind + " urls are not supported."); 73 | fallback(); 74 | return; 75 | } 76 | updateInfo(data.permalink_url, data.title+" by "+data.user.username); 77 | play('//api.soundcloud.com/tracks/'+data.id+'/stream?client_id=' + CLIENT_ID); 78 | }); 79 | }) 80 | } 81 | 82 | var handleDragOver = function(e) { 83 | e.preventDefault(); 84 | e.stopPropagation(); 85 | } 86 | 87 | var handleDrop = function(e) { 88 | e.preventDefault(); 89 | e.stopPropagation(); 90 | var objectUrl = URL.createObjectURL(e.dataTransfer.files[0]); 91 | updateInfo("#", e.dataTransfer.files[0].name); 92 | play(objectUrl, true); 93 | } 94 | 95 | 96 | document.body.addEventListener('drop', handleDrop, false); 97 | document.body.addEventListener('dragover', handleDragOver, false); 98 | 99 | function mic(){ 100 | var clubber = State.clubber; 101 | navigator.mediaDevices.getUserMedia({audio: { deviceId: { exact: "default" } }}).then(function(stream) { 102 | play(stream); 103 | clubber.muted = true; 104 | }); 105 | } 106 | 107 | var tracks = [], activeTrack = null; 108 | 109 | function fallback() { 110 | if(!tracks.length) return; 111 | document.body.classList.remove("soundcloud"); 112 | activeTrack = tracks.splice(Math.floor(Math.random() * tracks.length),1)[0]; 113 | if(activeTrack === "") { 114 | updateInfo("#", "live input"); 115 | mic(); 116 | } else if(activeTrack.match(/soundcloud.com/)){ 117 | document.body.classList.add("soundcloud"); 118 | soundcloud(activeTrack); 119 | } else { 120 | updateInfo(activeTrack, decodeURIComponent(activeTrack.split("/").pop())); 121 | play(activeTrack); 122 | } 123 | } 124 | module.exports = function (asset, config) { 125 | var uniforms = config.uniforms; 126 | tracks = []; 127 | asset.ds.track.forEach(function(tr){ 128 | tracks.push(tr[2]); 129 | }); 130 | if(!tapEl.userTapped) document.body.classList.add("muted"); 131 | fallback(); 132 | return function (t) { 133 | uniforms.iTrack[0] = audio.currentTime; 134 | uniforms.iTrack[1] = audio.duration; 135 | } 136 | } -------------------------------------------------------------------------------- /src/ui.js: -------------------------------------------------------------------------------- 1 | 2 | var CM = require("codemirror"); 3 | require("codemirror/addon/hint/show-hint"); 4 | require("codemirror/addon/edit/matchbrackets"); 5 | require("codemirror/addon/edit/closebrackets"); 6 | require('./glsl'); 7 | var util = require('./util'); 8 | var State = require("./state"); 9 | var PREFIX = util.PREFIX; 10 | var pads = require("./pads"); 11 | 12 | Number.prototype.toHHMMSS = function () { 13 | var sec_num = this; 14 | var hours = Math.floor(sec_num / 3600); 15 | var minutes = Math.floor((sec_num - (hours * 3600)) / 60); 16 | var seconds = sec_num - (hours * 3600) - (minutes * 60); 17 | 18 | seconds = seconds.toFixed(1); 19 | if (hours < 10) {hours = "0"+hours;} 20 | if (minutes < 10) {minutes = "0"+minutes;} 21 | if (seconds < 10) {seconds = "0"+seconds;} 22 | return hours+':'+minutes+':'+seconds+"s"; 23 | } 24 | 25 | var buttons = document.querySelectorAll("#header button, #foot button"); 26 | 27 | function toggle(el) { 28 | if(el.classList.contains("disabled")) { 29 | el.classList.remove("disabled"); 30 | } else { 31 | el.classList.add("disabled"); 32 | } 33 | var cl = el.id; 34 | if(cl) { 35 | if(document.body.classList.contains(cl)) { 36 | document.body.classList.remove(cl); 37 | } else { 38 | document.body.classList.add(cl); 39 | } 40 | } 41 | 42 | return el.classList.contains("disabled"); 43 | } 44 | 45 | var Buttons = {}; 46 | Array.prototype.forEach.call(buttons, function (b) { 47 | Buttons[b.id] = b; 48 | b.addEventListener("click", { 49 | code:function () { 50 | document.querySelector("#tap").click(); 51 | toggle(this); 52 | cm.refresh(); 53 | }, 54 | 55 | reset: function () { 56 | State.fpsCount = State.frameCount = 0; 57 | State.offsetTime = State.currentTime; 58 | }, 59 | 60 | play: function () { 61 | State.paused = !toggle(this); 62 | }, 63 | 64 | joy: function () { 65 | pads(false); 66 | }, 67 | 68 | full: function () { 69 | document.querySelector("#tap").click(); 70 | var element = State.canvas; 71 | if (element.requestFullscreen) { 72 | element.requestFullscreen(); 73 | } else if (element.mozRequestFullScreen) { 74 | element.mozRequestFullScreen(); 75 | } else if (element.webkitRequestFullScreen) { 76 | element.webkitRequestFullScreen(); 77 | } 78 | }, 79 | 80 | apply: function (e) { 81 | if(this.classList.contains("disabled")) e.preventDefault(); 82 | State.text = cm.getValue(); 83 | this.classList.add("disabled"); 84 | }, 85 | 86 | split: function(){ 87 | toggle(this); 88 | cm.refresh(); 89 | //cs.refresh(); 90 | }, 91 | 92 | hd: function(){ 93 | document.querySelector("#tap").click(); 94 | State.canvasScale = toggle(this) ? 0.5 : 1; 95 | }, 96 | 97 | create: function(){ 98 | window.open("run.html?s=XQAAAAKrAAAAAAAAAAAFYdTeawTqKymAWjyFtIA3PCFdfKL6my74WDEb0KQDsz72jEGYTdqWLpRRnBYSxJcYux7lHGFyi_EhBW2J5uw0y1LC4slw3EK69zgNwislq4iRrB5DqeUkJeeLcHvqd_BS9y5Cp5-HCw8Lu1K_eLKUtMKjBC1wqRNhc4urpOBp4N3--OLA"); 99 | } 100 | }[b.id]) 101 | }); 102 | 103 | var running = false; 104 | 105 | var state = document.querySelector("#state"); 106 | 107 | var hints = { 108 | 109 | }; 110 | 111 | function hint(cm, option) { 112 | var c = cm.getCursor(); 113 | var w = cm.findWordAt(c); 114 | var sw = cm.getRange(w.anchor, w.head); 115 | w.anchor.ch -= 2; 116 | if(w.anchor.ch <= 0) return; 117 | var pw = cm.findWordAt(w.anchor); 118 | if( pw.ch < 1 ) return; 119 | w.anchor.ch += 1; 120 | pw.anchor.ch -= 1; 121 | var ps = cm.getRange(pw.anchor, w.anchor); 122 | var m = ps.match(/\@(\w+)/); 123 | if(!m) return; 124 | 125 | var h = hints[m[1]]; 126 | if(!h) return; 127 | 128 | 129 | for (var o in obj) { 130 | 131 | }; 132 | 133 | return { list: comp, 134 | from: CodeMirror.Pos(cursor.line, start), 135 | to: CodeMirror.Pos(cursor.line, end)} 136 | } 137 | 138 | function cmOpt(readOnly) { 139 | return { 140 | lineNumbers: true, 141 | lineWrapping: true, 142 | autoCloseBrackets:true, 143 | matchBrackets: true, 144 | extraKeys: { 145 | "Alt-Enter": function (cm) { 146 | document.querySelector("#apply").click(); 147 | } 148 | }, 149 | //hintOptions: {hint: hint}, 150 | mode: "text/x-glsl", 151 | readOnly: readOnly ? true :false, 152 | //viewportMargin: Infinity 153 | } 154 | } 155 | 156 | var cm = CM(document.querySelector("#main"), cmOpt()); 157 | var cs = CM(document.querySelector("#side"), cmOpt(true)); 158 | 159 | var applyButton = document.querySelector("#apply"); 160 | cm.on("change", function(cm, change) { applyButton.classList.remove("disabled"); }); 161 | 162 | function checkAlt(e){ 163 | if(e.altKey) { 164 | document.body.classList.add("hide-editor"); 165 | } else { 166 | document.body.classList.remove("hide-editor"); 167 | } 168 | } 169 | document.addEventListener("keydown", checkAlt ); 170 | document.addEventListener("keyup", checkAlt ); 171 | 172 | window.addEventListener("focus", function () { document.body.classList.remove("hide-editor"); }, false); 173 | 174 | 175 | document.querySelector('#main').addEventListener("click", function(e) { 176 | if(e.target.classList.contains('cm-link')) { 177 | var s = e.target.textContent; 178 | if(s[0] === "@") { 179 | var o = State.config.imports[""]; 180 | s = s.replace("@", ""); 181 | o = o.ns[s]; 182 | s = o.origText; 183 | if(!o || !o.origText) { 184 | return; 185 | } 186 | } else if (s in State.config.imports) { 187 | s = State.config.imports[s].origText; 188 | if(!s) return; 189 | } else { 190 | open(s, "Rayglider"); 191 | return; 192 | } 193 | if(document.body.classList.contains("split")) { 194 | Buttons["split"].click(); 195 | } 196 | cs.setValue(s); 197 | } 198 | }) 199 | 200 | state.addEventListener("mouseenter", function() { 201 | if(this.classList.contains("wait")) return; 202 | var s = cm.getValue(); 203 | if(s === state._lastValue) return; 204 | this.classList.add("wait"); 205 | this.classList.remove("bad"); 206 | this.classList.remove("good"); 207 | this.title = "Generating editor state url." 208 | state._lastValue = s; 209 | var self = this; 210 | util.lzma.compress(s, 9, function(r) { 211 | self.classList.remove("wait"); 212 | var enc = util.btoa(new Uint8Array(r)); 213 | state.href = PREFIX + enc; 214 | var len = state.href.length; 215 | if(len > 2048) { 216 | self.classList.add("bad"); 217 | self.title = "URL TOO BIG "+len+" bytes > 2KB) Won't be able to shorten it :("; 218 | } else { 219 | self.classList.add("good"); 220 | self.title = "State url is "+len+" bytes. You can shorten and share it :)"; 221 | } 222 | // console.log(s.length, len); 223 | }, function(pc) { 224 | // console.log(pc+"%"); 225 | }); 226 | }); 227 | 228 | state.addEventListener("click", function(e) { 229 | if(this.classList.contains("wait")) e.preventDefault(); 230 | }); 231 | 232 | var stats = document.querySelectorAll("#stats span"); 233 | 234 | setInterval(function () { 235 | stats[0].textContent = "Frame " + State.frameCount; 236 | stats[1].textContent = (1000 * (State.frameCount - State.fpsCount) / (State.currentTime - State.fpsTime + 0.0001)).toFixed(1) + " FPS"; 237 | stats[2].textContent = ((State.currentTime - State.offsetTime) / 1000).toHHMMSS(); 238 | State.fpsCount = State.frameCount; 239 | State.fpsTime = State.currentTime; 240 | }, 150); 241 | 242 | var src = util.getParameterByName("s"); 243 | if(!src) { 244 | var res = [ 245 | "","", 246 | "void $main(inout vec4 fragColor, vec2 fragCoord) {", 247 | " vec2 uv = fragCoord/iRes.xy;", "", 248 | " vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));","", 249 | " fragColor = vec4(col,1.0);","", 250 | "}" 251 | ].join("\n"); 252 | var s = window.localStorage.getItem(""); 253 | if(s) res = s; 254 | State.text = res; 255 | cm.setValue(res); 256 | } else{ 257 | util.lzma.decompress(util.atob(src), function(res){ State.text = res; cm.setValue(res); }, function(){}); 258 | } 259 | cm.refresh(); 260 | var titleEl = document.querySelector("#title"), titleTimeout; 261 | 262 | module.exports = function (err, title) { 263 | if(err) { 264 | if(document.body.classList.contains("split")) { 265 | Buttons["split"].click(); 266 | } 267 | document.body.classList.add("error"); 268 | cs.setValue(err); 269 | cs.execCommand("goDocEnd"); 270 | 271 | 272 | } else { 273 | document.body.classList.remove("error"); 274 | cs.setValue(""); 275 | } 276 | 277 | if(title) { 278 | titleEl.textContent = title; 279 | titleEl.classList.add("show"); 280 | if(titleTimeout) window.clearTimeout(titleTimeout); 281 | titleTimeout = window.setTimeout(function() { 282 | titleEl.remove("show"); 283 | }, title.length * 200); 284 | } 285 | } -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; 2 | 3 | var lookup = new Uint8Array(256); 4 | for (var i = 0; i < chars.length; i++) { 5 | lookup[chars.charCodeAt(i)] = i; 6 | } 7 | 8 | var encode = function(bytes) { 9 | var i, len = bytes.length, base64 = ""; 10 | 11 | for (i = 0; i < len; i+=3) { 12 | base64 += chars[bytes[i] >> 2]; 13 | base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; 14 | base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; 15 | base64 += chars[bytes[i + 2] & 63]; 16 | } 17 | 18 | if ((len % 3) === 2) { 19 | base64 = base64.substring(0, base64.length - 1) + "!"; 20 | } else if (len % 3 === 1) { 21 | base64 = base64.substring(0, base64.length - 2) + "!!"; 22 | } 23 | 24 | return base64; 25 | }; 26 | 27 | var decode = function(base64) { 28 | var bufferLength = base64.length * 0.75, 29 | len = base64.length, i, p = 0, 30 | encoded1, encoded2, encoded3, encoded4; 31 | 32 | if (base64[base64.length - 1] === "!") { 33 | bufferLength--; 34 | if (base64[base64.length - 2] === "!") { 35 | bufferLength--; 36 | } 37 | } 38 | 39 | var arraybuffer = new ArrayBuffer(bufferLength), 40 | bytes = new Uint8Array(arraybuffer); 41 | 42 | for (i = 0; i < len; i+=4) { 43 | encoded1 = lookup[base64.charCodeAt(i)]; 44 | encoded2 = lookup[base64.charCodeAt(i+1)]; 45 | encoded3 = lookup[base64.charCodeAt(i+2)]; 46 | encoded4 = lookup[base64.charCodeAt(i+3)]; 47 | 48 | bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); 49 | bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); 50 | bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); 51 | } 52 | 53 | return bytes; 54 | }; 55 | 56 | var rext = /^\#([\w]+)\s+([\w\@\$\-]+)\s*\<\s*(.*)\s*\>/; 57 | var ext1 = /[\@](\w+)\.(\w+)/g; 58 | var ext2 = /[\$](\w+)/g; 59 | var rhex = /\#([0-9a-fA-F]+)/g; 60 | 61 | function resolve(s, obj, arr) { 62 | if(!arr) arr = []; 63 | return s.replace(ext1, function(s1, s2, s3) { 64 | var nso = obj.ns[s2], idx; 65 | if(!nso) return; 66 | var idx = nso.exported && nso.exported[s3] ? nso.exported[s3].idx:nso.idx; 67 | var s = ["raygl", s3, idx].join("_"); arr.push(s); return s; 68 | }).replace(ext2, function(s1, s2) { var s = ["raygl", s2, obj.idx].join("_"); arr.push(s); return s;}) 69 | .replace(rhex, function(s) { 70 | if(s !== s.toUpperCase()) return s; 71 | var arr = []; 72 | for(var i=1; i < s.length; i+=2) { 73 | arr.push(parseInt("0x" + s.substr(i,2)) / 255); 74 | } 75 | return ["vec", arr.length, "(", arr.map(function(v){ return v.toFixed(3); }).join(", "), ")"].join(""); 76 | }); 77 | } 78 | 79 | 80 | //LZMA © 2015 Nathan Rugg | MIT 81 | function LZMA(lzma_path) { 82 | var action_compress = 1, 83 | action_decompress = 2, 84 | action_progress = 3, 85 | 86 | callback_obj = {}, 87 | 88 | ///NOTE: Node.js needs something like "./" or "../" at the beginning. 89 | lzma_worker = new Worker(lzma_path || "./lzma_worker-min.js"); 90 | 91 | lzma_worker.onmessage = function onmessage(e) { 92 | if (e.data.action === action_progress) { 93 | if (callback_obj[e.data.cbn] && typeof callback_obj[e.data.cbn].on_progress === "function") { 94 | callback_obj[e.data.cbn].on_progress(e.data.result); 95 | } 96 | } else { 97 | if (callback_obj[e.data.cbn] && typeof callback_obj[e.data.cbn].on_finish === "function") { 98 | callback_obj[e.data.cbn].on_finish(e.data.result, e.data.error); 99 | 100 | /// Since the (de)compression is complete, the callbacks are no longer needed. 101 | delete callback_obj[e.data.cbn]; 102 | } 103 | } 104 | }; 105 | 106 | /// Very simple error handling. 107 | lzma_worker.onerror = function(event) { 108 | var err = new Error(event.message + " (" + event.filename + ":" + event.lineno + ")"); 109 | 110 | for (var cbn in callback_obj) { 111 | callback_obj[cbn].on_finish(null, err); 112 | } 113 | 114 | console.error('Uncaught error in lzma_worker', err); 115 | }; 116 | 117 | return (function () { 118 | 119 | function send_to_worker(action, data, mode, on_finish, on_progress) { 120 | var cbn; 121 | 122 | do { 123 | cbn = Math.floor(Math.random() * (10000000)); 124 | } while(typeof callback_obj[cbn] !== "undefined"); 125 | 126 | callback_obj[cbn] = { 127 | on_finish: on_finish, 128 | on_progress: on_progress 129 | }; 130 | 131 | lzma_worker.postMessage({ 132 | action: action, /// action_compress = 1, action_decompress = 2, action_progress = 3 133 | cbn: cbn, /// callback number 134 | data: data, 135 | mode: mode 136 | }); 137 | } 138 | 139 | return { 140 | compress: function compress(mixed, mode, on_finish, on_progress) { 141 | send_to_worker(action_compress, mixed, mode, on_finish, on_progress); 142 | }, 143 | decompress: function decompress(byte_arr, on_finish, on_progress) { 144 | send_to_worker(action_decompress, byte_arr, false, on_finish, on_progress); 145 | }, 146 | worker: function worker() { 147 | return lzma_worker; 148 | } 149 | }; 150 | }()); 151 | }; 152 | 153 | var parseStyle = function (s) { 154 | var a = s.split(";"), ret = {}; 155 | a.forEach(function(v){ 156 | var a = v.split(":").map(function(v) { return v.trim()}); 157 | if(a.length > 1 ) { 158 | ret[a[0]] = a[1].split(" ").map(function(v){ 159 | var n = parseFloat(v); 160 | return isNaN(n) ? v : n; 161 | }); 162 | } else { 163 | ret[a[0]] = true; 164 | } 165 | }); 166 | return ret; 167 | }; 168 | 169 | var wsrc = ["var LZMA=function(){function m(a){var b=[];b[a-1]=void 0;return b}function J(a,b){return Z(a[0]+b[0],a[1]+b[1])}function ta(a,b){var c=E(a)&E(b);var d=4294967296*(~~Math.max(Math.min(a[1]/4294967296,2147483647),-2147483648)&~~Math.max(Math.min(b[1]/4294967296,2147483647),-2147483648));var e=c;0>c&&(e+=4294967296);return[e,d]}function M(a,b){if(a[0]==b[0]&&a[1]==b[1])return 0;var c=0>a[1];var d=0>b[1];return c&&!d?-1:!c&&d?1:0>Z(a[0]-b[0],a[1]-b[1])[1]?-1:1}function Z(a,b){b%=1.8446744073709552E19;", 170 | "a%=1.8446744073709552E19;var c=b%4294967296;var d=4294967296*Math.floor(a/4294967296);b=b-c+d;for(a=a-d+c;0>a;)a+=4294967296,b-=4294967296;for(;4294967295b;)b+=1.8446744073709552E19;return[a,b]}function K(a){return 0<=a?[a,0]:[a+4294967296,-4294967296]}function E(a){return 2147483648<=a[0]?~~Math.max(Math.min(a[0]-4294967296,2147483647),-2147483648):~~Math.max(Math.min(a[0],", 171 | "2147483647),-2147483648)}function aa(a){return 30>=a?1<a[1])throw Error('Neg');var c=aa(b);var d=a[1]*c%1.8446744073709552E19;var e=a[0]*c;c=e-e%4294967296;d+=c;0x7fffffffffffffff<=d&&(d-=1.8446744073709552E19);return[e-c,d]}function wa(a,b){var c=aa(b&63);return Z(Math.floor(a[0]/c),a[1]/c)}function xa(a,b){a.buf=b;a.pos=0;a.count=b.length;return a}function N(a){return a.pos>=a.count?-1:a.buf[a.pos++]&255}function ya(a){a.buf=", 172 | "m(32);a.count=0;return a}function ba(a){var b=a.buf;b.length=a.count;return b}function na(a,b,c,d,e){for(var f=0;fM(f,ca))throw Error('invalid length '+f);a.length_0=f;b={};var h;b._repDistances=m(4);b._optimum=[];b._rangeEncoder={};b._isMatch=m(192);b._isRep=m(12);b._isRepG0=m(12);b._isRepG1=m(12);b._isRepG2=m(12);b._isRep0Long=m(192);b._posSlotEncoder=[];b._posEncoders=m(114);b._posAlignEncoder=", 173 | "T({},4);b._lenEncoder=Aa({});b._repMatchLenEncoder=Aa({});b._literalEncoder={};b._matchDistances=[];b._posSlotPrices=[];b._distancesPrices=[];b._alignPrices=m(16);b.reps=m(4);b.repLens=m(4);b.processedInSize=[H];b.processedOutSize=[H];b.finished=[0];b.properties=m(5);b.tempPrices=m(128);b._longestMatchLength=0;b._matchFinderType=1;b._numDistancePairs=0;b._numFastBytesPrev=-1;for(h=b.backRes=0;4096>h;++h)b._optimum[h]={};for(h=0;4>h;++h)b._posSlotEncoder[h]=T({},6);h=1<1<>24;for(c=0;4>c;++c)b.properties[1+c]=b._dictionarySize>>8*c<<24>>24;na(b.properties,", 175 | "0,e.buf,e.count,5);e.count+=5;for(c=0;64>c;c+=8)h=E(wa(f,c))&255,e.buf[e.count++]=h<<24>>24;b._needReleaseMFStream=0;b._inStream=d;b._finished=0;b._matchFinder||(d={},f=4,b._matchFinderType||(f=2),d.HASH_ARRAY=2f){d._cutValue=16+(c>>1);g=f+4096;h=c+274;d._keepSizeBefore=g;d._keepSizeAfter=h;g=g+h+(~~((f+4096+c+274)/2)+256);if(null==d._bufferBase||d._blockSize!=g)d._bufferBase=null,", 177 | "d._blockSize=g,d._bufferBase=m(d._blockSize);d._pointerToLastSafePosition=d._blockSize-h;d._matchMaxLen=c;c=f+1;d._cyclicBufferSize!=c&&(d._son=m(2*(d._cyclicBufferSize=c)));c=65536;d.HASH_ARRAY&&(c=f-1,c|=c>>1,c|=c>>2,c|=c>>4,c=(c|c>>8)>>1|65535,16777216>=1),d._hashMask=c,++c,c+=d.kFixHashSize);c!=d._hashSizeSum&&(d._hash=m(d._hashSizeSum=c))}b._dictionarySizePrev=b._dictionarySize;b._numFastBytesPrev=b._numFastBytes}b._rangeEncoder.Stream=e;b._state=0;for(e=b._previousByte=0;4>e;++e)b._repDistances[e]=", 178 | "0;e=b._rangeEncoder;e._position=H;e.Low=H;e.Range=-1;e._cacheSize=1;e._cache=0;x(b._isMatch);x(b._isRep0Long);x(b._isRep);x(b._isRepG0);x(b._isRepG1);x(b._isRepG2);x(b._posEncoders);e=b._literalEncoder;f=1<e;++e)x(b._posSlotEncoder[e].Models);Ba(b._lenEncoder,1<f;++f){var g=N(c);if(-1==g)throw Error('truncated input');h[f]=g<<24>>24}var k={m_OutWindow:{},m_RangeDecoder:{}};k.m_IsMatchDecoders=", 180 | "m(192);k.m_IsRepDecoders=m(12);k.m_IsRepG0Decoders=m(12);k.m_IsRepG1Decoders=m(12);k.m_IsRepG2Decoders=m(12);k.m_IsRep0LongDecoders=m(192);k.m_PosSlotDecoder=m(4);k.m_PosDecoders=m(114);k.m_PosAlignDecoder=U({},4);k.m_LenDecoder=Ga({});k.m_RepLenDecoder=Ga({});k.m_LiteralDecoder={};for(f=0;4>f;++f)k.m_PosSlotDecoder[f]=U({},6);var l;if(5>h.length)f=0;else{var q=h[0]&255;g=~~(q/9);for(l=f=0;4>l;++l)f+=(h[1+l]&255)<<8*l;if(!(h=99999999f)f=0;else{if(k.m_DictionarySize!=f){k.m_DictionarySize=f;k.m_DictionarySizeCheck=Math.max(k.m_DictionarySize,1);f=k.m_OutWindow;h=Math.max(k.m_DictionarySizeCheck,", 182 | "4096);if(null==f._buffer||f._windowSize!=h)f._buffer=m(h);f._windowSize=h;f._pos=0;f._streamPos=0}f=1}}if(!f)throw Error('corrupted input');for(f=0;64>f;f+=8){g=N(c);if(-1==g)throw Error('truncated input');g=g.toString(16);1==g.length&&(g='0'+g);e=g+''+e}/^0+$|^f+$/i.test(e)?a.length_0=ca:(e=parseInt(e,16),a.length_0=4294967295d;++d)x(k.m_PosSlotDecoder[d].Models);Ia(k.m_LenDecoder);Ia(k.m_RepLenDecoder);x(k.m_PosAlignDecoder.Models);d=k.m_RangeDecoder;d.Code=0;d.Range=-1;for(c=0;5>c;++c)d.Code=d.Code<<8|N(d.Stream);k.state=0;k.rep0=0;k.rep1=0;k.rep2=0;k.rep3=0;k.outSize=e;k.nowPos64=", 184 | "H;k.prevByte=0;d={};d.decoder=k;d.encoder=null;d.alive=1;a.chunker=d;return a}function F(a,b){return a._bufferBase[a._bufferOffset+a._pos+b]}function O(a,b,c,d){a._streamEndWasReached&&a._pos+b+d>a._streamPos&&(d=a._streamPos-(a._pos+b));++c;var e=a._bufferOffset+a._pos+b;for(b=0;b=c.count?c=-1:(b=Math.min(b,c.count-c.pos),na(c.buf,c.pos,a._bufferBase,a._bufferOffset+a._streamPos,b),c.pos+=b,c=b);if(-1==c){a._posLimit=a._streamPos;c=a._bufferOffset+a._posLimit;c>a._pointerToLastSafePosition&&(a._posLimit=a._pointerToLastSafePosition-a._bufferOffset);a._streamEndWasReached=1;break}a._streamPos+=c;a._streamPos>=a._pos+a._keepSizeAfter&&(a._posLimit=a._streamPos-a._keepSizeAfter)}}function Ka(a,b){a._bufferOffset+=b;a._posLimit-=b;a._pos-=b;a._streamPos-=b}function ea(a){++a._cyclicBufferPos>=", 186 | "a._cyclicBufferSize&&(a._cyclicBufferPos=0);++a._pos;if(a._pos>a._posLimit){var b=a._bufferOffset+a._pos;if(b>a._pointerToLastSafePosition){var c=a._bufferOffset+a._pos-a._keepSizeBefore;0a._cyclicBufferSize?a._pos-a._cyclicBufferSize:0;var f=a._bufferOffset+a._pos;if(a.HASH_ARRAY){var h=fa[a._bufferBase[f]&255]^a._bufferBase[f+1]&255;var g=h&1023;a._hash[g]=a._pos;h^=(a._bufferBase[f+2]&255)<<8;g=h&65535;a._hash[1024+g]=a._pos;g=(h^fa[a._bufferBase[f+3]&255]<<5)&a._hashMask}else g=a._bufferBase[f]&255^(a._bufferBase[f+", 188 | "1]&255)<<8;h=a._hash[a.kFixHashSize+g];a._hash[a.kFixHashSize+g]=a._pos;var k=(a._cyclicBufferPos<<1)+1;var l=a._cyclicBufferPos<<1;var q=c=a.kNumHashDirectBytes;for(g=a._cutValue;;){if(h<=e||0==g--){a._son[k]=a._son[l]=0;break}var n=a._pos-h;n=(n<=a._cyclicBufferPos?a._cyclicBufferPos-n:a._cyclicBufferPos-n+a._cyclicBufferSize)<<1;var I=a._bufferOffset+h;var m=q=a._windowSize&&(a._pos=0);a._streamPos=a._pos}}function Na(a,b){var c=a._pos-b-1;0>c&&(c+=a._windowSize);return a._buffer[c]}function ha(a){a-=2;return 4>a?a:3}function L(a){return 4>a?0:10>a?a-3:a-6}function ia(a){if(!a.alive)throw Error('bad state');", 190 | "if(a.encoder){a:{var b=a.encoder,c=a.encoder.processedInSize,d=a.encoder.processedOutSize,e=a.encoder.finished,f;c[0]=H;d[0]=H;e[0]=1;if(b._inStream){b._matchFinder._stream=b._inStream;var h=b._matchFinder;h._bufferOffset=0;h._pos=0;h._streamPos=0;h._streamEndWasReached=0;Ja(h);h._cyclicBufferPos=0;Ka(h,-1);b._needReleaseMFStream=1;b._inStream=null}if(!b._finished){b._finished=1;var g=h=b.nowPos64;if(g[0]==H[0]&&g[1]==H[1]){if(!W(b._matchFinder)){oa(b,E(b.nowPos64));break a}pa(b);var k=E(b.nowPos64)&", 191 | "b._posStateMask;t(b._rangeEncoder,b._isMatch,(b._state<<4)+k,0);b._state=L(b._state);var l=F(b._matchFinder,-b._additionalOffset);Oa(P(b._literalEncoder,E(b.nowPos64),b._previousByte),b._rangeEncoder,l);b._previousByte=l;--b._additionalOffset;b.nowPos64=J(b.nowPos64,Pa)}if(W(b._matchFinder))for(;;){g=$a(b,E(b.nowPos64));l=b.backRes;k=E(b.nowPos64)&b._posStateMask;var q=(b._state<<4)+k;if(1==g&&-1==l){t(b._rangeEncoder,b._isMatch,q,0);l=F(b._matchFinder,-b._additionalOffset);q=P(b._literalEncoder,", 192 | "E(b.nowPos64),b._previousByte);if(7>b._state)Oa(q,b._rangeEncoder,l);else{var n=F(b._matchFinder,-b._repDistances[0]-1-b._additionalOffset);var I;k=b._rangeEncoder;var m=n,r=l,w=I=1;for(f=7;0<=f;--f){var v=r>>f&1;n=w;I&&(I=m>>f&1,n+=1+I<<8,I=I==v);t(k,q.m_Encoders,n,v);w=w<<1|v}}b._previousByte=l;b._state=L(b._state)}else{t(b._rangeEncoder,b._isMatch,q,1);if(4>l){if(t(b._rangeEncoder,b._isRep,b._state,1),l?(t(b._rangeEncoder,b._isRepG0,b._state,1),1==l?t(b._rangeEncoder,b._isRepG1,b._state,0):(t(b._rangeEncoder,", 193 | "b._isRepG1,b._state,1),t(b._rangeEncoder,b._isRepG2,b._state,l-2))):(t(b._rangeEncoder,b._isRepG0,b._state,0),1==g?t(b._rangeEncoder,b._isRep0Long,q,0):t(b._rangeEncoder,b._isRep0Long,q,1)),1==g?b._state=7>b._state?9:11:(qa(b._repMatchLenEncoder,b._rangeEncoder,g-2,k),b._state=7>b._state?8:11),q=b._repDistances[l],0!=l){for(;1<=l;--l)b._repDistances[l]=b._repDistances[l-1];b._repDistances[0]=q}}else{t(b._rangeEncoder,b._isRep,b._state,0);b._state=7>b._state?7:10;qa(b._lenEncoder,b._rangeEncoder,g-", 194 | "2,k);l-=4;k=ra(l);q=ha(g);X(b._posSlotEncoder[q],b._rangeEncoder,k);if(4<=k)if(f=(k>>1)-1,n=(2|k&1)<k)for(q=b._posEncoders,k=n-k-1,n=b._rangeEncoder,r=v,w=1,v=0;v>=1;else Qa(b._rangeEncoder,v>>4,f-4),Ra(b._posAlignEncoder,b._rangeEncoder,v&15),++b._alignPriceCount;q=l;for(l=3;1<=l;--l)b._repDistances[l]=b._repDistances[l-1];b._repDistances[0]=q;++b._matchPriceCount}b._previousByte=F(b._matchFinder,g-1-b._additionalOffset)}b._additionalOffset-=g;b.nowPos64=", 195 | "J(b.nowPos64,K(g));if(!b._additionalOffset){128<=b._matchPriceCount&&Ca(b);16<=b._alignPriceCount&&Da(b);c[0]=b.nowPos64;g=b._rangeEncoder;g=J(J(K(g._cacheSize),g._position),[4,0]);d[0]=g;if(!W(b._matchFinder)){oa(b,E(b.nowPos64));break}g=b.nowPos64;g=Z(g[0]-h[0],g[1]-h[1]);if(0<=M(g,[4096,0])){b._finished=0;e[0]=0;break}}}else oa(b,E(b.nowPos64))}}a.inBytesProcessed=a.encoder.processedInSize[0];a.encoder.finished[0]&&(b=a.encoder,Sa(b),b._rangeEncoder.Stream=null,a.alive=0)}else{a:{b=a.decoder;e=", 196 | "E(b.nowPos64)&b.m_PosStateMask;if(G(b.m_RangeDecoder,b.m_IsMatchDecoders,(b.state<<4)+e)){if(G(b.m_RangeDecoder,b.m_IsRepDecoders,b.state))c=0,G(b.m_RangeDecoder,b.m_IsRepG0Decoders,b.state)?(G(b.m_RangeDecoder,b.m_IsRepG1Decoders,b.state)?(G(b.m_RangeDecoder,b.m_IsRepG2Decoders,b.state)?(d=b.rep3,b.rep3=b.rep2):d=b.rep2,b.rep2=b.rep1):d=b.rep1,b.rep1=b.rep0,b.rep0=d):G(b.m_RangeDecoder,b.m_IsRep0LongDecoders,(b.state<<4)+e)||(b.state=7>b.state?9:11,c=1),c||(c=Ta(b.m_RepLenDecoder,b.m_RangeDecoder,", 197 | "e)+2,b.state=7>b.state?8:11);else if(b.rep3=b.rep2,b.rep2=b.rep1,b.rep1=b.rep0,c=2+Ta(b.m_LenDecoder,b.m_RangeDecoder,e),b.state=7>b.state?7:10,g=ja(b.m_PosSlotDecoder[ha(c)],b.m_RangeDecoder),4<=g)if(d=(g>>1)-1,b.rep0=(2|g&1)<g){e=b.rep0;h=b.m_PosDecoders;g=b.rep0-g-1;l=b.m_RangeDecoder;n=1;for(k=f=0;k>>=1,l=h.Code-h.Range>>>31,h.Code-=h.Range&l-1,g=g<<1|1-l,h.Range&-16777216||(h.Code=", 198 | "h.Code<<8|N(h.Stream),h.Range<<=8);b.rep0=e+(g<<4);d=b.rep0;e=b.m_PosAlignDecoder;h=b.m_RangeDecoder;q=1;for(l=k=0;lb.rep0){b=-1==b.rep0?1:-1;break a}}else b.rep0=g;if(0<=M(K(b.rep0),b.nowPos64)||b.rep0>=b.m_DictionarySizeCheck){b=-1;break a}d=b.m_OutWindow;e=c;h=d._pos-b.rep0-1;for(0>h&&(h+=d._windowSize);0!=e;--e)h>=d._windowSize&&(h=0),d._buffer[d._pos++]=d._buffer[h++],d._pos>=d._windowSize&&V(d);b.nowPos64=J(b.nowPos64,", 199 | "K(c));b.prevByte=Na(b.m_OutWindow,0)}else{c=b.m_LiteralDecoder;d=E(b.nowPos64);c=c.m_Coders[((d&c.m_PosMask)<>>8-c.m_NumPrevBits)];if(7>b.state){d=b.m_RangeDecoder;e=1;do e=e<<1|G(d,c.m_Decoders,e);while(256>e);b.prevByte=e<<24>>24}else{d=b.m_RangeDecoder;e=Na(b.m_OutWindow,b.rep0);l=1;do if(g=e>>7&1,e<<=1,h=G(d,c.m_Decoders,(1+g<<8)+l),l=l<<1|h,g!=h){for(;256>l;)l=l<<1|G(d,c.m_Decoders,l);break}while(256>l);b.prevByte=l<<24>>24}c=b.m_OutWindow;d=b.prevByte;c._buffer[c._pos++]=", 200 | "d;c._pos>=c._windowSize&&V(c);b.state=L(b.state);b.nowPos64=J(b.nowPos64,Pa)}b=0}if(-1==b)throw Error('corrupted input');a.inBytesProcessed=ca;a.outBytesProcessed=a.decoder.nowPos64;if(b||0<=M(a.decoder.outSize,H)&&0<=M(a.decoder.nowPos64,a.decoder.outSize))V(a.decoder.m_OutWindow),b=a.decoder.m_OutWindow,V(b),b._stream=null,a.decoder.m_RangeDecoder.Stream=null,a.alive=0}return a.alive}function Ha(a,b){for(;a.m_NumPosStatesb;++b){var c=a._alignPrices,d=b,e,f=a._posAlignEncoder,h=b,g=1,k=0;for(e=f.NumBitLevels;0!=e;--e){var l=h&1;h>>>=1;k+=Y(f.Models[g],l);g=g<<1|l}c[d]=k}a._alignPriceCount=0}function Ca(a){var b;for(b=4;128>b;++b){var c=ra(b);var d=(c>>1)-1;var e=(2|c&1)<>>=1,q+=z[((a._posEncoders[e-c-1+l]-g^-g)&2047)>>>2],l=l<<1|g;f[h]=q}for(e=0;4>e;++e){b=a._posSlotEncoder[e];f=e<<6;for(c=0;c>1)-1-4<<6;c=128*e;for(b=0;4>b;++b)a._distancesPrices[c+b]=a._posSlotPrices[f+b];for(;128>b;++b)a._distancesPrices[c+b]=a._posSlotPrices[f+ra(b)]+a.tempPrices[b]}a._matchPriceCount=0}function oa(a,b){Sa(a);var c=b&a._posStateMask;a._writeEndMark&&(t(a._rangeEncoder,a._isMatch,(a._state<<4)+c,1),t(a._rangeEncoder,a._isRep,a._state,0),a._state=7>a._state?7:10,qa(a._lenEncoder,a._rangeEncoder,0,c),c=ha(2),X(a._posSlotEncoder[c],a._rangeEncoder,", 205 | "63),Qa(a._rangeEncoder,67108863,26),Ra(a._posAlignEncoder,a._rangeEncoder,15));for(c=0;5>c;++c)sa(a._rangeEncoder)}function $a(a,b){var c,d,e,f,h,g,k,l;if(a._optimumEndIndex!=a._optimumCurrentIndex){var q=a._optimum[a._optimumCurrentIndex].PosPrev-a._optimumCurrentIndex;a.backRes=a._optimum[a._optimumCurrentIndex].BackPrev;a._optimumCurrentIndex=a._optimum[a._optimumCurrentIndex].PosPrev;return q}a._optimumCurrentIndex=a._optimumEndIndex=0;if(a._longestMatchWasFound){var n=a._longestMatchLength;a._longestMatchWasFound=", 206 | "0}else n=pa(a);q=a._numDistancePairs;var m=W(a._matchFinder)+1;if(2>m)return a.backRes=-1,1;for(c=d=0;4>c;++c)a.reps[c]=a._repDistances[c],a.repLens[c]=O(a._matchFinder,-1,a.reps[c],273),a.repLens[c]>a.repLens[d]&&(d=c);if(a.repLens[d]>=a._numFastBytes){a.backRes=d;q=a.repLens[d];var x=q-1;0=a._numFastBytes)return a.backRes=a._matchDistances[q-1]+4,q=n-1,0n&&r!=w&&2>a.repLens[d])return a.backRes=-1,1;a._optimum[0].State=a._state;var v=b&a._posStateMask;a._optimum[1].Price=z[a._isMatch[(a._state<<4)+v]>>>2]+la(P(a._literalEncoder,b,a._previousByte),7<=a._state,w,r);var u=a._optimum[1];u.BackPrev=-1;u.Prev1IsChar=0;u=z[2048-a._isMatch[(a._state<<4)+v]>>>2];var A=u+z[2048-a._isRep[a._state]>>>2];if(w==r){c=a._state;var t=A+(z[a._isRepG0[c]>>>2]+z[a._isRep0Long[(c<<4)+v]>>>2]);t=a.repLens[d]?n:a.repLens[d];if(2>d)return a.backRes=a._optimum[1].BackPrev,1;a._optimum[1].PosPrev=0;a._optimum[0].Backs0=a.reps[0];a._optimum[0].Backs1=a.reps[1];a._optimum[0].Backs2=a.reps[2];a._optimum[0].Backs3=a.reps[3];c=d;do a._optimum[c--].Price=268435455;while(2<=c);for(c=0;4>c;++c)if(t=a.repLens[c],!(2>t)){var D=A+Q(a,c,a._state,v);do{var y=D+a._repMatchLenEncoder._prices[272*v+(t-2)];var p=a._optimum[t];y>>2];c=2<=a.repLens[0]?a.repLens[0]+1:2;if(c<=n){for(A=0;c>a._matchDistances[A];)A+=2;for(;n=a._matchDistances[A+1],y=u+Va(a,n,c,v),p=a._optimum[c],y=a._numFastBytes)return a._longestMatchLength=D,a._longestMatchWasFound=1,Ua(a,n);", 210 | "++b;u=a._optimum[n].PosPrev;a._optimum[n].Prev1IsChar?(--u,a._optimum[n].Prev2?(c=a._optimum[a._optimum[n].PosPrev2].State,c=4>a._optimum[n].BackPrev2?7>c?8:11:7>c?7:10):c=a._optimum[u].State,c=L(c)):c=a._optimum[u].State;u==n-1?c=a._optimum[n].BackPrev?L(c):7>c?9:11:(a._optimum[n].Prev1IsChar&&a._optimum[n].Prev2?(u=a._optimum[n].PosPrev2,v=a._optimum[n].BackPrev2,c=7>c?8:11):(v=a._optimum[n].BackPrev,c=4>v?7>c?8:11:7>c?7:10),u=a._optimum[u],4>v?v?1==v?(a.reps[0]=u.Backs1,a.reps[1]=u.Backs0,a.reps[2]=", 211 | "u.Backs2,a.reps[3]=u.Backs3):2==v?(a.reps[0]=u.Backs2,a.reps[1]=u.Backs0,a.reps[2]=u.Backs1,a.reps[3]=u.Backs3):(a.reps[0]=u.Backs3,a.reps[1]=u.Backs0,a.reps[2]=u.Backs1,a.reps[3]=u.Backs2):(a.reps[0]=u.Backs0,a.reps[1]=u.Backs1,a.reps[2]=u.Backs2,a.reps[3]=u.Backs3):(a.reps[0]=v-4,a.reps[1]=u.Backs0,a.reps[2]=u.Backs1,a.reps[3]=u.Backs2));a._optimum[n].State=c;a._optimum[n].Backs0=a.reps[0];a._optimum[n].Backs1=a.reps[1];a._optimum[n].Backs2=a.reps[2];a._optimum[n].Backs3=a.reps[3];u=a._optimum[n].Price;", 212 | "r=F(a._matchFinder,-1);w=F(a._matchFinder,-a.reps[0]-1-1);v=b&a._posStateMask;y=u+z[a._isMatch[(c<<4)+v]>>>2]+la(P(a._literalEncoder,b,F(a._matchFinder,-2)),7<=c,w,r);m=a._optimum[n+1];p=0;y>>2];A=u+z[2048-a._isRep[c]>>>2];w!=r||m.PosPrev>>2]+z[a._isRep0Long[(c<<4)+v]>>>2]),t<=m.Price&&(m.Price=t,m.PosPrev=n,m.BackPrev=0,m.Prev1IsChar=0,p=1));t=W(a._matchFinder)+", 213 | "1;m=t=4095-nm)){m>a._numFastBytes&&(m=a._numFastBytes);if(!p&&w!=r&&(p=Math.min(t-1,a._numFastBytes),p=O(a._matchFinder,0,a.reps[0],p),2<=p)){var B=L(c);var C=b+1&a._posStateMask;y=y+z[2048-a._isMatch[(B<<4)+C]>>>2]+z[2048-a._isRep[B]>>>2];for(e=n+1+p;df;++f)if(r=O(a._matchFinder,", 214 | "-1,a.reps[f],m),!(2>r)){C=r;do{for(;dc?8:11;C=b+r&a._posStateMask;y=A+(g=a._repMatchLenEncoder._prices[272*v+(r-2)],g+Q(a,f,c,v))+z[a._isMatch[(B<<4)+C]>>>2]+la(P(a._literalEncoder,b+r,F(a._matchFinder,", 215 | "r-1-1)),1,F(a._matchFinder,r-1-(a.reps[f]+1)),F(a._matchFinder,r-1));B=L(B);C=b+r+1&a._posStateMask;y+=z[2048-a._isMatch[(B<<4)+C]>>>2];y+=z[2048-a._isRep[B]>>>2];for(e=r+1+p;dm){D=m;for(q=0;D>a._matchDistances[q];q+=2);a._matchDistances[q]=D;q+=2}if(D>=w){for(u+=z[a._isRep[c]>>>", 216 | "2];da._matchDistances[A];)A+=2;for(r=w;;++r)if(D=a._matchDistances[A+1],y=u+Va(a,D,r,v),p=a._optimum[n+r],yc?7:10;C=b+r&a._posStateMask;y=y+z[a._isMatch[(B<<4)+C]>>>2]+la(P(a._literalEncoder,b+r,F(a._matchFinder,r-1-1)),1,F(a._matchFinder,r-(D+1)-1),F(a._matchFinder,r-1));B=L(B);C=b+", 217 | "r+1&a._posStateMask;y+=z[2048-a._isMatch[(B<<4)+C]>>>2];y+=z[2048-a._isRep[B]>>>2];for(e=r+1+p;db)b=a._distancesPrices[128*e+b];else{var f=a._posSlotPrices;var h=131072>b?R[b>>6]+12:134217728>b?R[b>>16]+32:R[b>>26]+", 218 | "52;b=f[(e<<6)+h]+a._alignPrices[b&15]}return b+a._lenEncoder._prices[272*d+(c-2)]}function Q(a,b,c,d){if(b){var e=z[2048-a._isRepG0[c]>>>2];1==b?e+=z[a._isRepG1[c]>>>2]:(e+=z[2048-a._isRepG1[c]>>>2],e+=Y(a._isRepG2[c],b-2))}else e=z[a._isRepG0[c]>>>2],e+=z[2048-a._isRep0Long[(c<<4)+d]>>>2];return e}function pa(a){var b=0;a:{var c=a._matchFinder;var d=a._matchDistances,e,f;if(c._pos+c._matchMaxLen<=c._streamPos)var h=c._matchMaxLen;else if(h=c._streamPos-c._pos,hc._cyclicBufferSize?c._pos-c._cyclicBufferSize:0;var l=c._bufferOffset+c._pos;var q=1;var n=e=0;if(c.HASH_ARRAY){var m=fa[c._bufferBase[l]&255]^c._bufferBase[l+1]&255;e=m&1023;m^=(c._bufferBase[l+2]&255)<<8;n=m&65535;var t=(m^fa[c._bufferBase[l+3]&255]<<5)&c._hashMask}else t=c._bufferBase[l]&255^(c._bufferBase[l+1]&255)<<8;m=c._hash[c.kFixHashSize+t]||0;if(c.HASH_ARRAY){var r=c._hash[e]||0;var w=c._hash[1024+n]||0;c._hash[e]=c._pos;c._hash[1024+n]=c._pos;r>k&&c._bufferBase[c._bufferOffset+", 220 | "r]==c._bufferBase[l]&&(d[g++]=q=2,d[g++]=c._pos-r-1);w>k&&c._bufferBase[c._bufferOffset+w]==c._bufferBase[l]&&(w==r&&(g-=2),d[g++]=q=3,d[g++]=c._pos-w-1,r=w);0!=g&&r==m&&(g-=2,q=1)}c._hash[c.kFixHashSize+t]=c._pos;var v=(c._cyclicBufferPos<<1)+1;var u=c._cyclicBufferPos<<1;t=f=c.kNumHashDirectBytes;0!=c.kNumHashDirectBytes&&m>k&&c._bufferBase[c._bufferOffset+m+c.kNumHashDirectBytes]!=c._bufferBase[l+c.kNumHashDirectBytes]&&(d[g++]=q=c.kNumHashDirectBytes,d[g++]=c._pos-m-1);for(e=c._cutValue;;){if(m<=", 221 | "k||0==e--){c._son[v]=c._son[u]=0;break}r=c._pos-m;n=(r<=c._cyclicBufferPos?c._cyclicBufferPos-r:c._cyclicBufferPos-r+c._cyclicBufferSize)<<1;var x=c._bufferOffset+m;w=ta?R[a]:2097152>a?R[a>>10]+20:R[a>>20]+40}function Ba(a,b){x(a._choice);for(var c=0;c>>2];var g=z[2048-a._choice[0]>>>2];var k=g+z[a._choice[1]>>>2];g+=z[2048-a._choice[1]>>>2];for(f=0;8>f;++f){if(f>=c)return;d[e+f]=h+ka(a._lowCoder[b],f)}for(;16>f;++f){if(f>=c)return;d[e+f]=k+ka(a._midCoder[b],f-8)}for(;fc?(t(b,a._choice,0,0),X(a._lowCoder[d],b,c)):(c-=8,t(b,a._choice,0,1),8>c?(t(b,a._choice,1,0),X(a._midCoder[d],b,c)):(t(b,a._choice,1,1),X(a._highCoder,b,c-8)));0==--a._counters[d]&&", 224 | "(Wa(a,d,a._tableSize,a._prices,272*d),a._counters[d]=a._tableSize)}function Aa(a){a._choice=m(2);a._lowCoder=m(16);a._midCoder=m(16);a._highCoder=T({},8);for(var b=0;16>b;++b)a._lowCoder[b]=T({},3),a._midCoder[b]=T({},3);a._prices=[];a._counters=[];return a}function Ea(a,b){for(var c=0;c>>8-a.m_NumPrevBits)]}function Oa(a,b,c){var d,e=1;for(d=7;0<=", 225 | "d;--d){var f=c>>d&1;t(b,a.m_Encoders,e,f);e=e<<1|f}}function la(a,b,c,d){var e=1,f=7,h=0;if(b)for(;0<=f;--f){var g=c>>f&1;b=d>>f&1;h+=Y(a.m_Encoders[(1+g<<8)+e],b);e=e<<1|b;if(g!=b){--f;break}}for(;0<=f;--f)b=d>>f&1,h+=Y(a.m_Encoders[e],b),e=e<<1|b;return h}function U(a,b){a.NumBitLevels=b;a.Models=m(1<>>d&1;t(b,a.Models,e,f);e=e<<1|f}}function ka(a,b){var c,d=1,e=0;for(c=a.NumBitLevels;0!=c;){--c;var f=b>>>c&1;e+=Y(a.Models[d],f);d=(d<<1)+f}return e}function Ra(a,b,c){var d,e=1;for(d=0;d>=1}}function G(a,b,c){var d=b[c];var e=(a.Range>>>11)*d;if((a.Code^-2147483648)<(e^-2147483648))return a.Range=e,b[c]=d+(2048-d>>>5)<<16>>16,a.Range&-16777216||(a.Code=a.Code<<8|N(a.Stream),a.Range<<=", 227 | "8),0;a.Range-=e;a.Code-=e;b[c]=d-(d>>>5)<<16>>16;a.Range&-16777216||(a.Code=a.Code<<8|N(a.Stream),a.Range<<=8);return 1}function x(a){for(var b=a.length-1;0<=b;--b)a[b]=1024}function t(a,b,c,d){var e=b[c];var f=(a.Range>>>11)*e;d?(a.Low=J(a.Low,ta(K(f),[4294967295,0])),a.Range-=f,b[c]=e-(e>>>5)<<16>>16):(a.Range=f,b[c]=e+(2048-e>>>5)<<16>>16);a.Range&-16777216||(a.Range<<=8,sa(a))}function Qa(a,b,c){for(--c;0<=c;--c)a.Range>>>=1,1==(b>>>c&1)&&(a.Low=J(a.Low,K(a.Range))),a.Range&-16777216||(a.Range<<=", 228 | "8,sa(a))}function sa(a){var b=a.Low;var c=32;var d=wa(b,c);0>b[1]&&(d=J(d,ua([2,0],63-c)));b=E(d);if(0!=b||0>M(a.Low,[4278190080,0])){a._position=J(a._position,K(a._cacheSize));d=a._cache;do c=a.Stream,d+=b,c.buf[c.count++]=d<<24>>24,d=255;while(0!=--a._cacheSize);a._cache=E(a.Low)>>>24}++a._cacheSize;a.Low=ua(ta(a.Low,[16777215,0]),8)}function Y(a,b){return z[((a-b^-b)&2047)>>>2]}function Xa(a){for(var b=0,c=0,d,e,f,h=a.length,g=[],k=[];b=h)return a;", 229 | "e=a[++b]&255;if(128!=(e&192))return a;k[c]=(d&31)<<6|e&63}else if(224==(d&240)){if(b+2>=h)return a;e=a[++b]&255;if(128!=(e&192))return a;f=a[++b]&255;if(128!=(f&192))return a;k[c]=(d&15)<<12|(e&63)<<6|f&63}else return a;else{if(!d)return a;k[c]=d}16383==c&&(g.push(String.fromCharCode.apply(String,k)),c=-1)}0=a?++d:d=!a||128<=a&&2047>=a?d+2:d+3;c=[];for(e=d=0;e=a?c[d++]=a<<24>>24:(!a||128<=a&&2047>=a?c[d++]=(192|a>>6&31)<<24>>24:(c[d++]=(224|a>>12&15)<<24>>24,c[d++]=(128|a>>6&63)<<24>>24),c[d++]=(128|a&63)<<24>>24);return c}function ma(a){return a[1]+a[0]}var S='function'==typeof setImmediate?setImmediate:setTimeout,ca=[4294967295,-4294967296],va=[0,-9223372036854775808],H=[0,0],Pa=[1,0],fa=function(){var a,b,c=[];for(a=0;256>a;++a){var d=a;for(b=", 231 | "0;8>b;++b)d=0!=(d&1)?d>>>1^-306674912:d>>>1;c[a]=d}return c}(),R=function(){var a,b,c=2,d=[0,1];for(b=2;22>b;++b){var e=1<<(b>>1)-1;for(a=0;a>24}return d}(),z=function(){var a,b,c=[];for(b=8;0<=b;--b){var d=1<<9-b-1;for(a=1<<9-b;d>>9-b-1)}return c}(),Za=function(){var a=[{s:16,f:64,m:0},{s:20,f:64,m:0},{s:19,f:64,m:1},{s:20,f:64,m:1},{s:21,f:128,m:1},{s:22,f:128,m:1},{s:23,f:128,m:1},{s:24,f:255,m:1},{s:25,f:255,m:1}];return function(b){return a[b-", 232 | "1]||a[6]}}();'undefined'==typeof onmessage||'undefined'!=typeof window&&'undefined'!=typeof window.document||function(){onmessage=function(a){a&&a.data&&(2==a.data.action?LZMA.decompress(a.data.data,a.data.cbn):1==a.data.action&&LZMA.compress(a.data.data,a.data.mode,a.data.cbn))}}();return{compress:function(a,b,c,d){function e(){try{for(var a,b=(new Date).getTime();ia(f.c.chunker);)if(h=ma(f.c.chunker.inBytesProcessed)/ma(f.c.length_0),200<(new Date).getTime()-b)return d(h),S(e,0),0;d(1);a=ba(f.c.output);", 233 | "S(c.bind(null,a),0)}catch(n){c(null,n)}}var f={},h,g='undefined'==typeof c&&'undefined'==typeof d;if('function'!=typeof c){var k=c;c=d=0}d=d||function(a){'undefined'!=typeof k&&postMessage({action:3,cbn:k,result:a})};c=c||function(a,b){if('undefined'!=typeof k)return postMessage({action:1,cbn:k,result:a,error:b})};if(g){for(f.c=za({},Ya(a),Za(b));ia(f.c.chunker););return ba(f.c.output)}try{f.c=za({},Ya(a),Za(b)),d(0)}catch(l){return c(null,l)}S(e,0)},decompress:function(a,b,c){function d(){try{for(var a,", 234 | "g=0,h=(new Date).getTime();ia(e.d.chunker);)if(0==++g%1E3&&200<(new Date).getTime()-h)return l&&(f=ma(e.d.chunker.decoder.nowPos64)/k,c(f)),S(d,0),0;c(1);a=Xa(ba(e.d.output));S(b.bind(null,a),0)}catch(da){b(null,da)}}var e={},f,h='undefined'==typeof b&&'undefined'==typeof c;if('function'!=typeof b){var g=b;b=c=0}c=c||function(a){'undefined'!=typeof g&&postMessage({action:3,cbn:g,result:l?a:-1})};b=b||function(a,b){if('undefined'!=typeof g)return postMessage({action:2,cbn:g,result:a,error:b})};if(h){for(e.d=", 235 | "Fa({},a);ia(e.d.chunker););return Xa(ba(e.d.output))}try{e.d=Fa({},a);var k=ma(e.d.length_0);var l=-1