diff --git a/201410_-_Sine_City/README b/201410_-_Sine_City/README new file mode 100644 index 0000000..6c111eb --- /dev/null +++ b/201410_-_Sine_City/README @@ -0,0 +1,11 @@ +Source code for "Sine City" +A JavaScript 8K intro + +On Pouet: http://www.pouet.net/prod.php?which=64233 + +Notes: + * It is likely not to work on your end. + * Tested on Linux using Chromium and Firefox with a NVidia GPU + * Known to work on some Windows boxes w/ native GL enabled + * The "original" version is not the code that was shown at DemoJS, + I adjusted quite a few things in the final, packed version. diff --git a/201410_-_Sine_City/custom-jsexe-bootstrap b/201410_-_Sine_City/custom-jsexe-bootstrap new file mode 100644 index 0000000..ea3c9f2 --- /dev/null +++ b/201410_-_Sine_City/custom-jsexe-bootstrap @@ -0,0 +1,2 @@ +// in case I need it again +C=V.getContext('2d');for(C.drawImage(this,0,_='');$--;(A=C.getImageData($%X,$/X,1,1).data[0])&&(_+=String.fromCharCode(A)));(1,eval)(_) diff --git a/201410_-_Sine_City/music.js b/201410_-_Sine_City/music.js new file mode 100644 index 0000000..f51b6e7 --- /dev/null +++ b/201410_-_Sine_City/music.js @@ -0,0 +1,159 @@ +var song = { + songData: [ + { + i: [ 0, 198, 128, 0, 3, 192, 128, 1, 0, 0, 96, 128, 28, 0, 0, 0, 0, 2, 63, 61, 13, 16, 44, 8, 0, 0 ], + + p: [1,2,3,4,5,2,3,4,6,7,1,2,3,4,5,2,3,4,5,2,3,4,1,2,3,8,9], + + c: [ + {n: [115,,,,,,,,,,,,117,,,,,,,,,,,,113], + f: [12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,96]}, + {n: [115,,,,,,,,,,,,,,,110], + f: [12,,,,,,,,,,,,,,,12,,,,,,,,,,,,,,,,,,,,,115,,,,,,,,,,,,,,,148]}, + {n: [115,,,,,,,,,,,,117,,,,,,,,,,,,118], + f: [12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,96]}, + {n: [122,,,,,,,,,,,,,,,123,,,,,115], + f: [12,,,,,,,,,,,,,,,12,,,,,12,,,,,,,,,,,,,,,,115,,,,,,,,,,,,,,,8,,,,,128]}, + {n: [118,,,,,,,,,,,,117,,,,,,,,,,,,113], + f: [12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,96]}, + {n: [115], + f: [12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,255]}, + {n: [,,,,,,,,,,,,,,,,118,118,118], + f: [,,,,,,,,,,,,,,,,12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,128]}, + {n: [122,,,,,,,,,,,,,,,123,,,,,115], + f: [12,,,,,,,,,,,,,,,12,,,,,13,12,,,,,,,,,,,,,,,115,,,,,,,,,,,,,,,8,,,,,95,61]}, + {n: [], + f: []} + ] + }, + { + i: [ 0, 255, 116, 1, 0, 255, 101, 0, 0, 15, 1, 4, 45, 0, 13, 6, 1, 2, 62, 27, 60, 48, 0, 0, 44, 2 ], + + p: [,1,,2,,1,,2,,3,4,4,5,4,5,4,5,4,5,4,5,4,5,4,3,6], + + c: [ + {n: [,,,,,139,,,139,139,,,,,,139,,,,,,,,139,,,139,139,,,,,139,139,139], + f: []}, + {n: [,,139,139,,,,,139,139,,,,,139,139,,,,,,,,,,139,139,139,,,,,139,139,139], + f: []}, + {n: [,,,139,,,,,139,139,,,,,,139,,,,,139,139,,,,,,139,,,,139,139,139], + f: []}, + {n: [139,,,,139,,139,,,139,,,139,,,,139,,139,,,139,,,139,,,,139,,139,,,139], + f: []}, + {n: [139,,,139,139,,139,,,139,,,139,,,139,139,,139,,,139,,,139,,,139,139,,139,,,139], + f: []}, + {n: [139,,,139,139,,139,,,139,,,139,,,139,139,,139,,,139,139], + f: []} + ] + }, + { + i: [ 0, 0, 140, 0, 0, 0, 140, 0, 0, 128, 4, 10, 34, 0, 187, 5, 0, 1, 239, 135, 34, 19, 108, 8, 8, 4 ], + + p: [,,1,,1,,3,,1,3,2,2,2,2,2,3,2,3,2,3,2,3,2,3,1], + + c: [ + {n: [,,,,,,,,,,127,,,,,,,,,,,,127,,,,,,,,,,,,127], + f: []}, + {n: [,,,139,,,,,,139,139,,,,,139,,,,,,139,139,,,,,139,,,,,139,,139], + f: []}, + {n: [,,127,,,,,,,,127,,,,127,,,,,,,,127,,,,127,,,,,,,,127], + f: []} + ] + }, + { + i: [ 1, 192, 128, 0, 3, 201, 128, 0, 0, 93, 5, 6, 58, 3, 195, 6, 1, 3, 35, 63, 14, 17, 11, 10, 61, 6 ], + + p: [,,,,1,2,3,4,5,6,7,5,8,9,10,11,8,9,10,11,12,13,14,15,16], + + c: [ + {n: [,,,,,,,,147,146,145,,,,,,,,,,145,147,146,,,,,,,,,,149,148,147], + f: []}, + {n: [,,,,,,151,151,151,,,,,,,146,142,138,,,,,,,,,,,,,,,147,150,147], + f: []}, + {n: [,,146,,,146,,,,,,,,,147,146,145,,,,,,,,,,146,145,142,,,142,,,142], + f: []}, + {n: [,,141,,,,,,,,,,,,141,140,139,,,146,,,146,,,127], + f: []}, + {n: [,139,,151,,,,,,,,,,151,,139,,,,,,,,,,139,,151], + f: []}, + {n: [,139,,151,150,149,,,,,,,,139,,149,148,147,,,,,,,,139,,,146,,,145,,,144], + f: []}, + {n: [,139,,,139,142,,,142,144,146,,144,,,,146,147,,,147,146,141,,137,,,,142,141,,141,,137], + f: []}, + {n: [139,,127,,142,,,130,,,144,142,141,,129,,141,,,144,,,141,144,142,,,,149,,,146,,,151], + f: []}, + {n: [,149,,149,,146,144,142,,,141,,142,,,139,,,,151,147,,,,146,147,,144,146,,142,144,141,146], + f: []}, + {n: [,,,,,,,,147,146,145,,,,,141,144,147,146,,144,,137,,,144,146,142,144,,,142,144,141,146], + f: []}, + {n: [,146,,151,,146,,,,144,142,144,146,,,,142,,,,,139,,,,,151,146,139,144,139,142,141,142,151], + f: []}, + {n: [,,147,,,149,146,,,142,,142,141,,,144,,144,,,144,146,142,144,142,,,,151,150,149,,137,,149], + f: []}, + {n: [,151,,,147,151,,,146,151,,,,,144,151,,149,147,149,151,,146,,,144,143,142,,,139,,151,139], + f: []}, + {n: [,,134,,,134,,,134,,,146,144,,,141,144,,,146,147,146,,,144,,139,140,141,,,153,,,141], + f: []}, + {n: [,142,,151,,,142,143,144,,,,151,,,139,,,137,,149,,137,,,,125,,,141,142,141,,,146], + f: []}, + {n: [139,,127,139,,127,139,,127,139,127], + f: []} + ] + }, + { + i: [ 2, 160, 128, 1, 0, 160, 128, 0, 1, 60, 4, 7, 41, 0, 60, 4, 1, 3, 14, 0, 35, 32, 31, 12, 89, 1 ], + + p: [,,,,,,,,,,,,1,2,1,2,1,2,1,2,1,2,1,2,1,3], + + c: [ + {n: [,,139,,,,,,,,,,,,139,,,,,,,,,,,,139,,,,,139,139,,,139], + f: []}, + {n: [,,139,,,151,,,139,,,151,,,139,,,151,,,139,,,151,,,139,,,151,,,146,146,146,146], + f: []}, + {n: [,,139,,,151,,,139,,,151,,,139,,139,139,139,139,127], + f: []} + ] + }, + { + i: [ 2, 100, 128, 0, 3, 201, 128, 0, 0, 0, 0, 6, 29, 0, 195, 6, 1, 3, 28, 229, 119, 77, 147, 6, 61, 2 ], + + p: [,,,,,,,,,6,1,2,3,4,5,2,3,4,1,2,3,4,1,2,3,7], + + c: [ + {n: [115,115,115,115,115,115,115,115,115,115,115,115,117,117,117,117,117,117,117,117,117,117,117,117,113,113,113,113,113,113,113,113,113,113,113,113], + f: []}, + {n: [115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110], + f: []}, + {n: [115,115,115,115,115,115,115,115,115,115,115,115,117,117,117,117,117,117,117,117,117,117,117,117,118,118,118,118,118,118,118,118,118,118,118,118], + f: []}, + {n: [110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,111,111,111,111,111,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115], + f: []}, + {n: [118,118,118,118,118,118,118,118,118,118,118,118,117,117,117,117,117,117,117,117,117,117,117,117,113,113,113,113,113,113,113,113,113,113,113,113], + f: []}, + {n: [,,,,,,,,,,,,,,,,,,130,,130,,130,,130,,130,,130,118,130,118,130,118,130,118], + f: [,,,,,,,,,,,,,,,,,,26,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2]}, + {n: [110,122,,122,134,,110,122,,122,134,,110,122,,111,123,,111,123,115,127,139,127,115], + f: [,,,,,,,,,,,,,,,,,,,,26,25,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,8,147]} + ] + }, + { + i: [ 2, 100, 128, 0, 3, 201, 128, 0, 0, 0, 5, 6, 58, 0, 195, 6, 1, 2, 135, 0, 0, 32, 147, 6, 121, 6 ], + + p: [], + + c: [ + ] + }, + { + i: [ 2, 100, 128, 0, 3, 201, 128, 0, 0, 0, 5, 6, 58, 0, 195, 6, 1, 2, 135, 0, 0, 32, 147, 6, 121, 6 ], + + p: [], + + c: [ + ] + } + ], + rowLen: 5513, + patternLen: 36, + endPattern: 28 +}; diff --git a/201410_-_Sine_City/music.ogg b/201410_-_Sine_City/music.ogg new file mode 100644 index 0000000..0f4291e Binary files /dev/null and b/201410_-_Sine_City/music.ogg differ diff --git a/201410_-_Sine_City/player-small.js b/201410_-_Sine_City/player-small.js new file mode 100644 index 0000000..22c48d8 --- /dev/null +++ b/201410_-_Sine_City/player-small.js @@ -0,0 +1,336 @@ +/* -*- mode: javascript; tab-width: 4; indent-tabs-mode: nil; -*- +* +* Copyright (c) 2011-2013 Marcus Geelnard +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* +* 3. This notice may not be removed or altered from any source +* distribution. +* +*/ + +"use strict"; + +var CPlayer = function() { + + //-------------------------------------------------------------------------- + // Private methods + //-------------------------------------------------------------------------- + + // Oscillators + var osc_sin = function (value) { + return Math.sin(value * 6.283184); + }; + + var osc_saw = function (value) { + return 2 * (value % 1) - 1; + }; + + var osc_square = function (value) { + return (value % 1) < 0.5 ? 1 : -1; + }; + + var osc_tri = function (value) { + var v2 = (value % 1) * 4; + if(v2 < 2) return v2 - 1; + return 3 - v2; + }; + + var getnotefreq = function (n) { + // 174.61.. / 44100 = 0.003959503758 (F3) + return 0.003959503758 * Math.pow(2, (n - 128) / 12); + }; + + var createNote = function (instr, n) { + var osc1 = mOscillators[instr.i[0]], + o1vol = instr.i[1], + o1xenv = instr.i[3], + osc2 = mOscillators[instr.i[4]], + o2vol = instr.i[5], + o2xenv = instr.i[8], + noiseVol = instr.i[9], + attack = instr.i[10] * instr.i[10] * 4, + sustain = instr.i[11] * instr.i[11] * 4, + release = instr.i[12] * instr.i[12] * 4, + releaseInv = 1 / release; + + var noteBuf = new Int32Array(attack + sustain + release); + + // Calculate note frequencies for the oscillators + var o1t = getnotefreq(n + instr.i[2] - 128); + var o2t = getnotefreq(n + instr.i[6] - 128) * (1 + 0.0008 * instr.i[7]); + + // Re-trig oscillators + var c1 = 0, c2 = 0; + + // Local variables. + var j, e, t, rsample; + + // Generate one note (attack + sustain + release) + for (j = 0; j < attack + sustain + release; j++) { + // Envelope + e = 1; + if (j < attack) { + e = j / attack; + } else if (j >= attack + sustain) { + e -= (j - attack - sustain) * releaseInv; + } + + // Oscillator 1 + t = o1t; + if (o1xenv) { + t *= e * e; + } + c1 += t; + rsample = osc1(c1) * o1vol; + + // Oscillator 2 + t = o2t; + if (o2xenv) { + t *= e * e; + } + c2 += t; + rsample += osc2(c2) * o2vol; + + // Noise oscillator + if (noiseVol) { + rsample += (2 * Math.random() - 1) * noiseVol; + } + + // Add to (mono) channel buffer + noteBuf[j] = (80 * rsample * e) | 0; + } + + return noteBuf; + }; + + + //-------------------------------------------------------------------------- + // Private members + //-------------------------------------------------------------------------- + + // Array of oscillator functions + var mOscillators = [ + osc_sin, + osc_square, + osc_saw, + osc_tri + ]; + + // Private variables set up by init() + var mSong, mLastRow, mCurrentCol, mNumWords, mMixBuf; + + + //-------------------------------------------------------------------------- + // Initialization + //-------------------------------------------------------------------------- + + this.init = function (song) { + // Define the song + mSong = song; + + // Init iteration state variables + mLastRow = song.endPattern - 2; + mCurrentCol = 0; + + // Prepare song info + mNumWords = song.rowLen * song.patternLen * (mLastRow + 1) * 2; + + // Create work buffer (initially cleared) + mMixBuf = new Int32Array(mNumWords); + }; + + + //-------------------------------------------------------------------------- + // Public methods + //-------------------------------------------------------------------------- + + // Generate audio data for a single track + this.generate = function () { + // Local variables + var i, j, b, p, row, col, n, cp, + k, t, lfor, e, x, rsample, rowStartSample, f, da; + + // Put performance critical items in local variables + var chnBuf = new Int32Array(mNumWords), + instr = mSong.songData[mCurrentCol], + rowLen = mSong.rowLen, + patternLen = mSong.patternLen; + + // Clear effect state + var low = 0, band = 0, high; + var lsample, filterActive = false; + + // Clear note cache. + var noteCache = []; + + // Patterns + for (p = 0; p <= mLastRow; ++p) { + cp = instr.p[p]; + + // Pattern rows + for (row = 0; row < patternLen; ++row) { + // Execute effect command. + var cmdNo = cp ? instr.c[cp - 1].f[row] : 0; + if (cmdNo) { + instr.i[cmdNo - 1] = instr.c[cp - 1].f[row + patternLen] || 0; + + // Clear the note cache since the instrument has changed. + if (cmdNo < 14) { + noteCache = []; + } + } + + // Put performance critical instrument properties in local variables + var oscLFO = mOscillators[instr.i[13]], + lfoAmt = instr.i[14] / 512, + lfoFreq = Math.pow(2, instr.i[15] - 9) / rowLen, + fxLFO = instr.i[16], + fxFilter = instr.i[17], + fxFreq = instr.i[18] * 43.23529 * 3.141592 / 44100, + q = 1 - instr.i[19] / 255, + dist = instr.i[20] * 1e-5, + drive = instr.i[21] / 32, + panAmt = instr.i[22] / 512, + panFreq = 6.283184 * Math.pow(2, instr.i[23] - 9) / rowLen, + dlyAmt = instr.i[24] / 255, + dly = instr.i[25] * rowLen; + + // Calculate start sample number for this row in the pattern + rowStartSample = (p * patternLen + row) * rowLen; + + // Generate notes for this pattern row + for (col = 0; col < 4; ++col) { + n = cp ? instr.c[cp - 1].n[row + col * patternLen] : 0; + if (n) { + if (!noteCache[n]) { + noteCache[n] = createNote(instr, n); + } + + // Copy note from the note cache + var noteBuf = noteCache[n]; + for (j = 0, i = rowStartSample * 2; j < noteBuf.length; j++, i += 2) { + chnBuf[i] += noteBuf[j]; + } + } + } + + // Perform effects for this pattern row + for (j = 0; j < rowLen; j++) { + // Dry mono-sample + k = (rowStartSample + j) * 2; + rsample = chnBuf[k]; + + // We only do effects if we have some sound input + if (rsample || filterActive) { + // State variable filter + f = fxFreq; + if (fxLFO) { + f *= oscLFO(lfoFreq * k) * lfoAmt + 0.5; + } + f = 1.5 * Math.sin(f); + low += f * band; + high = q * (rsample - band) - low; + band += f * high; + rsample = fxFilter == 3 ? band : fxFilter == 1 ? high : low; + + // Distortion + if (dist) { + rsample *= dist; + rsample = rsample < 1 ? rsample > -1 ? osc_sin(rsample*.25) : -1 : 1; + rsample /= dist; + } + + // Drive + rsample *= drive; + + // Is the filter active (i.e. still audiable)? + filterActive = rsample * rsample > 1e-5; + + // Panning + t = Math.sin(panFreq * k) * panAmt + 0.5; + lsample = rsample * (1 - t); + rsample *= t; + } else { + lsample = 0; + } + + // Delay is always done, since it does not need sound input + if (k >= dly) { + // Left channel = left + right[-p] * t + lsample += chnBuf[k-dly+1] * dlyAmt; + + // Right channel = right + left[-p] * t + rsample += chnBuf[k-dly] * dlyAmt; + } + + // Store in stereo channel buffer (needed for the delay effect) + chnBuf[k] = lsample | 0; + chnBuf[k+1] = rsample | 0; + + // ...and add to stereo mix buffer + mMixBuf[k] += lsample | 0; + mMixBuf[k+1] += rsample | 0; + } + } + } + + // Next iteration. Return progress (1.0 == done!). + mCurrentCol++; + return mCurrentCol / 8; + }; + + // Create a WAVE formatted Uint8Array from the generated audio data + this.createWave = function() { + // Create WAVE header + var l1 = mNumWords * 2 - 8; + var l2 = l1 - 36; + var headerLen = 44; + var wave = new Uint8Array(headerLen + mNumWords * 2); + wave.set( + [82,73,70,70, + l1 & 255,(l1 >> 8) & 255,(l1 >> 16) & 255,(l1 >> 24) & 255, + 87,65,86,69,102,109,116,32,16,0,0,0,1,0,2,0, + 68,172,0,0,16,177,2,0,4,0,16,0,100,97,116,97, + l2 & 255,(l2 >> 8) & 255,(l2 >> 16) & 255,(l2 >> 24) & 255] + ); + + // Append actual wave data + for (var i = 0, idx = headerLen; i < mNumWords; ++i) { + // Note: We clamp here + var y = mMixBuf[i]; + y = y < -32767 ? -32767 : (y > 32767 ? 32767 : y); + wave[idx++] = y & 255; + wave[idx++] = (y >> 8) & 255; + } + + // Return the WAVE formatted typed array + return wave; + }; + + // Get n samples of wave data at time t [s]. Wave data in range [-2,2]. + this.getData = function(t, n) { + var i = 2 * Math.floor(t * 44100); + var d = new Array(n); + for (var j = 0; j < 2*n; j += 1) { + var k = i + j; + d[j] = t > 0 && k < mMixBuf.length ? mMixBuf[k] / 32768 : 0; + } + return d; + }; +}; + diff --git a/201410_-_Sine_City/sine-city-ori.html b/201410_-_Sine_City/sine-city-ori.html new file mode 100644 index 0000000..7b94824 --- /dev/null +++ b/201410_-_Sine_City/sine-city-ori.html @@ -0,0 +1,5 @@ + + + + + diff --git a/201410_-_Sine_City/sine-city-ori.js b/201410_-_Sine_City/sine-city-ori.js new file mode 100644 index 0000000..75c0928 --- /dev/null +++ b/201410_-_Sine_City/sine-city-ori.js @@ -0,0 +1,1665 @@ +var USE_SYNTH = 0; + +// Vector stuff +function vecOp(code) { + return eval('(function(o,a,b){for(var i=o.length;--i>=0;)' + code + ';return o})') +} +function vomNew(s) { + return new Float32Array(s) +} +var vecDot = function(a,b) { + var s = 0; + for ( var i in a ) { + s += a[i]*b[i]; + } + return s; +}; +var vecLen = function(a) { + return Math.sqrt( vecDot(a,a) ) +}; +var vecNorm = function(o,a) { + var l = vecLen(a); + return l ? vecScale(o,a,1/l) : vecCopy(o,a); +}; +var vecCross = function(o,a,b) { + return vecSet(o, + a[1]*b[2]-a[2]*b[1] , + a[2]*b[0]-a[0]*b[2] , + a[0]*b[1]-a[1]*b[0] + ); +}; +var vecs2Mat3 = function(o,a,b,c){ + for ( var i = 0 ; i < 3 ; i ++ ) { + o[i*3+0] = a[i]; + o[i*3+1] = b[i]; + o[i*3+2] = c[i]; + } +}; +var vecSet = vecOp('o[i]=arguments[i+1]') , + vecCopy = vecOp('o[i]=a[i]') , + vecAdd = vecOp('o[i]=a[i]+b[i]') , + vecSub = vecOp('o[i]=a[i]-b[i]') , + vecScale = vecOp('o[i]=a[i]*b'); + +var luma = [.299 , .587 , .114]; + + +// Shaders +var shaderBits = { + precision: 'precision highp float;' , + inPosition: 'attribute vec2 inPosition;' , + voTexCoords: 'varying vec2 voTexCoords;' , + raymarchVSMain: [ + "void main( )" , + "{" , + "gl_Position = vec4( inPosition , 0. , 1. );" , + "voTexCoords = inPosition;" , + "}" + ].join('') , + rmUtils: [ + "const vec3 LUMA = vec3( " , shaderVec(luma) , " );" , + + "struct Material" , + "{" , + "vec3 albedo;" , + "float metallic;" , + "float smoothness;" , + "float r0;" , + "};" , + + "float hash( float n )" , + "{" , + "return fract(sin(n)*43758.5453);" , + "}" , + + "float noise( vec3 x )" , + "{" , + "vec3 p = floor(x);" , + "vec3 f = fract(x);" , + + "f = f*f*(3.0-2.0*f);" , + "float n = p.x + p.y*57.0 + 113.0*p.z;" , + "return mix(mix(mix( hash(n+ 0.0), hash(n+ 1.0),f.x)," , + "mix( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y)," , + " mix(mix( hash(n+113.0), hash(n+114.0),f.x)," , + "mix( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);" , + "}" , + + "float atan2( in float y , in float x )" , + "{" , + "return ( x == 0.0" , + "? ( y > 0.0 ? ( 3.14159 * .5 ) : ( -3.14159 * .5 ) )" , + ": atan( y , x ) );", + "}" , + + "vec2 solve(float a, float b, float c)" , + "{" , + "float d = b * b - 4. * a * c;" , + "if ( d < 0. ) {" , + "return vec2(-1.);" , + "}" , + "d=sqrt(d);" , + "vec2 s = -.5*vec2(b+d,b-d)/a;" , + "return vec2(min(s.x,s.y),max(s.x,s.y));" , + "}" , + + "float lx( vec2 v , float x )" , + "{" , + "vec2 s = pow(abs(v),vec2(x));" , + "return pow( s.x + s.y , 1. / x );" , + "}" , + + "mat2 rot( float angle )" , + "{" , + "vec2 cs = vec2( cos(angle),sin(angle));" , + "return mat2( -cs.x,cs.y,cs.y,cs.x );" , + "}" , + + "float schlick( vec3 hv , vec3 cd , float r0, float smooth )" , + "{" , + "float d = clamp((1. - dot( hv, -cd )), 0., 1.) , d2 = d * d;" , + "return r0 + (1. - r0) * d * d2 * d2 * smooth;" , + "}" , + + "float getSpecular(vec3 i, vec3 l, vec3 n, float smooth)" , + "{ " , + "float rDotL = max(dot(reflect(i, n), l), 0.0);" , + "float sp = exp2(4.0 + 6.0 * smooth);" , + "float si = (sp + 2.0) * 0.125;" , + "return pow(rDotL, sp) * si;" , + "}" , + + ].join('\n'), + beadCyl: [ + "float beadCyl( vec3 p )" , + "{" , + "vec2 q = mod( p.xy , vec2(15.) ) - vec2(7.5);" , + "return length(q) - .5 + .05 * sin(p.z*9.42477);" , + "}" + ].join('\n'), +}; + +var shaders = { + raymarchVS: [ 'v' , + 'inPosition' , 'voTexCoords' , 'raymarchVSMain' ] , +}; +var programs = { +}; + +function shaderFloat( v ) { + return '' + v + ( v == Math.floor(v) ? '.' : '' ); +} +function shaderVec( v ) { + return v.map(shaderFloat).join(','); +} + +function compileShader( def ) { + var type = def[0] == 'f' ? glCtx.FRAGMENT_SHADER : glCtx.VERTEX_SHADER; + var src = ''; + for ( var i = 1 ; i < def.length ; i ++ ) { + src += shaderBits[ def[ i ] ]; + } + + var shader = glCtx.createShader( type ); + glCtx.shaderSource( shader , src ); + glCtx.compileShader( shader ); + if (!glCtx.getShaderParameter(shader, glCtx.COMPILE_STATUS)) { + src = src.split( /\n/ ); + for ( var i in src ) { + var j = parseInt(i)+1; + src[i] = j + ": " + src[i]; + } + throw "SHADER ERROR\n" + glCtx.getShaderInfoLog(shader) + "\n" + src.join('\n'); + } + + + return shader; +} + +function compileShaders( ) { + for ( var name in shaders ) { + shaders[name] = compileShader( shaders[name] ); + } +} + +function buildProgram( name ) { + var p = glCtx.createProgram( ); + var d = programs[name]; + var ul = d.shift(); + while( d.length ) { + glCtx.attachShader( p , shaders[ d.shift() ] ); + } + glCtx.bindAttribLocation( p , 0 , "inPosition" ); + glCtx.linkProgram( p ); + var nul = {}; + while ( ul.length ) { + d = ul.shift(); + nul[d] = glCtx.getUniformLocation( p , 'u' + d ); + } + programs[name] = [p,nul]; +} + +function buildPrograms( ) { + compileShaders( ); + for ( var name in programs ) { + buildProgram(name); + } +} + + +// Raymarchers + +var materials = [ + { + albedo: [.05,.05,.05] , + metallic: .7 , + smoothness: .02 , + r0: .01 + } , { + albedo: [ .2,.2,.2 ] , + noise: [ .1,.1,.1] , + noiseScale: 5 , + metallic: .7 , + smoothness: .9 , + r0: .01 + } , { + albedo: [ 1,1,1 ] , + metallic: .5 , + smoothness: .4 , + r0: .02 + } , { + albedo: [ 3,0,0 ] , + noise: [.6,0,0], + noiseScale: 6 , + metallic: .3 , + smoothness: .1 , + r0: .6 + } , { + albedo: [ 0,2,0 ] , + noise: [0,.2,0], + noiseScale: 15 , + metallic: .9 , + smoothness: .5 , + r0: .4 + } , { + albedo: [ 3,1.5,0 ] , + noise: [.3,.6,0], + noiseScale: .3 , + metallic: .95 , + smoothness: .6 , + r0: .9 + } , { + albedo: [ .1,.1,.1 ] , + noise: [3,3,3] , + noiseScale: 3, + metallic: 0 , + smoothness: .04 , + r0: .002 + } , { + albedo: [ 0,1.5,2 ] , + metallic: .9 , + smoothness: .3 , + r0: .4 + } +]; + + +var raymarchers = { + tunnel: { + extraUniforms: {BallOffset:'float'} , + extraBits: [] , + + maxDistance: 100 , + epsilon: .025 , + epsilonMultiplier: 1 , + step: .7 , + depth: 64 , + + normalDelta: .0005 , + noiseScale: .5 , + reflectionDistance: .3 , + + fog: .4 , + fogDensity: .015 , + + aoSteps: 4 , + aoDelta: 2 , + aoWeight: .75 , + + materials: [0,1,2,3] , + + setCamera: function( ctx ) { + var z = ctx.time*30; + vecSet(ctx.camPos,0,0,z); + z += 5; + vecSet(ctx.lookAt,-Math.sin(z*.05),-.7*Math.cos(z*.033),z); + vecSet(ctx.up,0,1,0); + ctx.toNearPlane = 2; + } , + map: [ + "vec3 q = p + .1 * ( sin(p.zxy*.17+uTime*.5)+sin(p.yzx*.7+uTime*1.5) )*8.5*(1. + cos(sin(p.z * .1 ) + p.z * .3 ));" , + "float b = 14. - length( q.xy )," , + "ap = atan2(p.y,p.x),c,m,n=8.35-uBallOffset*1.35;" , + "q = vec3( 9.*(mod(ap+p.z*.02,.628)-.314) , length(p.xy)-9. , mod(p.z,12.56)-6.28);" , + "c = length(q.xy)-.25+.1*cos(p.z*8.+uTime*.1);" , + "c = min(c,length(q.yz)-.5);", + "q = vec3(n*(mod(ap+p.z*.02,1.256636)-.628318),q.y+9.-n,mod(p.z,62.8318)-31.4159);" , + "m = step(c,b)+1.;" , + "b=min(c,b);" , + "c=length(q) - 1.3;" , + "if(c 0 ) { + ctx.desync += desync; + } + ctx.desync *= Math.random( ) * .25 + .75; + }; +}; +var balls1CamFunc = function(lax,laz,desync) { + var dsf = desyncFunc(desync); + return function(ctx) { + var z = ctx.time*3; + vecSet(ctx.camPos,0,4,z); + vecSet(ctx.lookAt,lax,0,z+laz); + vecSet(ctx.up,0,0,1); + ctx.fade = 0; + dsf(ctx,this.time); + }; +}; +var balls2CamCommon = function(ctx) { + var z = ctx.time*5; + vecSet(ctx.camPos,Math.cos(ctx.time)*12,8,Math.sin(ctx.time)*12+z); + vecSet(ctx.lookAt,1,0,z); + vecSet(ctx.up,0,1,0); + ctx.toNearPlane = 2.5; + z = ctx.time - direction[43].startTime; + ctx.extras[0] = '1f(p.EffectTime,'+z+')'; +}; +var balls2LightFunc = function(scale) { + return function(ctx,lctx) { + var z = ctx.time*30; + vecSet(lctx.pos,scale*Math.cos(ctx.time*2)*5,3,scale*Math.sin(ctx.time*2)*5+z); + vecSet(lctx.colour,1,1,1); + lctx.distance = 4; + return true; + }; +}; +var fract1CamCommon = function(ctx) { + var z = ctx.time; + vecSet(ctx.camPos,4,2.5+.025*z,6.7); + vecSet(ctx.lookAt,0,2.5-.05*z,6.7); + vecSet(ctx.up,0,0,1); + ctx.extras = [ '1f(p.BallSize,0)' , '1f(p.TimeEffect,0)' ]; + ctx.toNearPlane = 3; +}; +var fract1CamFunc = function(desync) { + var dsf = desyncFunc(desync); + return function(ctx){ + fract1CamCommon(ctx); + dsf(ctx,this.time); + }; +}; +var fract2CamCommon = function(ctx,bs) { + vecSet(ctx.camPos,5*Math.sin(ctx.time*.2),9*Math.cos(ctx.time*.41),7.8); + vecCopy(ctx.lookAt,ctx.camPos); + ctx.lookAt[0] = Math.cos(ctx.time*.2); + ctx.lookAt[1] = Math.sin(ctx.time*.33); + ctx.lookAt[2] -= 2; + vecSet(ctx.up,0,0,1); + ctx.toNearPlane = 3; + ctx.extras = [ '1f(p.BallSize,'+bs+')' , '1f(p.TimeEffect,1)' ]; +}; + +var drawText = function(str,x) { + twoDCtx.shadowBlur = c2height/5; + twoDCtx.fillText( str , x , c2height / 2 ); + twoDCtx.shadowBlur = 0; + twoDCtx.strokeText( str , x , c2height / 2 ); +}; +var titleText = function(str,shake,alpha,r,g,b) { + drawText( str , canvasWidth/15 ); + vecSet( context.text , 1 - c2rHeight + Math.random() * shake - shake * .5 , alpha , c2rHeight ); + vecSet( context.textColour , r , g , b ); +}; +var greetings = "Greetings to ... Mog, Sycop, Tim & Wullon ... Adinpsz ... Alcatraz ... ASD ... Bits'n'Bites ... Brain Control ... Cocoon ... Conspiracy ... Ctrl+Alt+Test ... Fairlight ... Farbrausch ... Kewlers ... LNX ... Loonies ... Mercury ... Popsy Team ... Razor 1911 ... RGBA ... 7th Cube ... Still ... TPOLM ... TRBL ... Umlaut Design ... X-Men ... Youth Uprising ... Everyone here at DemoJS 2014!"; +var greetingsText = function() { + var t = ( context.time - direction[34].startTime ) * canvasWidth * .5; + drawText( greetings , canvasWidth - t ); + vecSet( context.text , 1 - c2rHeight + Math.random() * .02 - .01 , 1 , c2rHeight ); + vecSet( context.textColour , 1 , 1 , 1 ); +}; + +var squaresCam = function(ctx) { + var t = ctx.time - direction[25].startTime; + var z = t*10 - 80; + vecSet(ctx.camPos,z,0,0); + vecSet(ctx.lookAt,z+Math.cos(t*.5) * 80,Math.sin(t*.25)*40,100); + vecSet(ctx.up,0,1,0); +}; + +var squaresCam2 = function(ctx,s) { + var t = ctx.time - direction[s].startTime; + var z = t*20 - 80; + vecSet(ctx.camPos,0,0,z); + vecSet(ctx.lookAt,Math.cos(t*.5) * 80,Math.sin(t*.25)*40,z+100); + vecSet(ctx.up,Math.sin(t),Math.cos(t),0); + ctx.desync = Math.random() * .025 - .0125; +}; + +var tunnelLight = function(ctx,lctx) { + vecAdd(lctx.pos,vecNorm(lctx.pos,vecScale(lctx.pos,ctx.lookAt,-1)),ctx.camPos); + lctx.pos[1] -= .5; + vecSet(lctx.colour,3,3,3); + lctx.distance = 30; + return true; +}; +var tunnelCam = function(ctx,mul) { + var z = ctx.time*30*ctx.tunMul; + vecSet(ctx.camPos,1.1*Math.cos(z*.1),Math.sin(z*.02),z); + z += 5; + vecSet(ctx.lookAt,-Math.sin(z*.05),-.7*Math.cos(z*.033),z); + vecSet(ctx.up,0,1,0); + ctx.toNearPlane = 2; +}; +var tunnelLightBall = function(ctx,lctx) { + vecSet(lctx.pos , 4*Math.sin(ctx.time*.5)*Math.cos(ctx.time*.7) , + 3*Math.cos(ctx.time*1.5), + ctx.time*30*ctx.tunMul+14+16*Math.sin(ctx.time*3.3)*Math.cos(ctx.time*.77) + ); + vecSet(lctx.colour,1,1,1); + lctx.distance = 15; + return true; +}; + +var direction = [ + { + // Music rows + rows: 36 , + // Raymarcher id + rm: 'tunnel' , + setGlobals: function( ctx ) { + var z = tun1CamCommon(ctx,this); + z *= z*z; + ctx.fade = 1 - z; + ctx.toNearPlane = 5; + } , + lights: [ + lightFunc(3,20) + ] , + updateText: null + } , { + rows: 18 , + rm: 'tunnel' , + setGlobals: function( ctx ) { + var z = tun1CamCommon(ctx,this); + ctx.toNearPlane = 5 - 2.25 * z; + ctx.fade = 0; + } , + lights: [ + lightFunc(3,20) + ] , + updateText: null + } , { + rows: 36 , + rm: 'tunnel' , + setGlobals: function( ctx ) { + var z = tun1CamCommon(ctx,this); + ctx.toNearPlane = 2.75 - 2.25 * z; + ctx.fade = z; + } , + lights: [ + lightFunc(3,20) + ] , + updateText: null + } , { + rows: 18 , + rm: 'balls' , + setGlobals: function( ctx ) { + var z = ctx.time*3; + vecSet(ctx.camPos,0,4,z); + vecSet(ctx.lookAt,4,0,z); + vecSet(ctx.up,0,0,1); + z = ctx.stepTime / this.time; + z *= z*z; + ctx.fade = 1 - z; + ctx.toNearPlane = 2.5; + } , + lights: [ + lightFunc(.75,8) + ] , + updateText: null + } , { + rows: 22 , + rm: 'balls' , + setGlobals: balls1CamFunc(4,0,0) , + lights: [ + lightFunc(1,8) + ] , + updateText: null + } , { + rows: 22 , + rm: 'balls' , + setGlobals: balls1CamFunc(2,-2,0) , + lights: [ + lightFunc(1,8) + ] , + updateText: null + } , { + rows: 3 , + rm: 'balls' , + setGlobals: balls1CamFunc( 6 , 0 , -.5 ) , + lights: [ + lightFunc(.5,4) + ] , + updateText: null + } , { + rows: 9 , + rm: 'balls' , + setGlobals: balls1CamFunc( 6 , 0 , .5 ) , + lights: [ + lightFunc(.5,4) + ] , + updateText: null + } , { + rows: 3 , + rm: 'balls' , + setGlobals: balls1CamFunc( 2 , 2 , -.2 ) , + lights: [ + lightFunc(1,8) + ] , + updateText: null + } , { + rows: 9 , + rm: 'balls' , + setGlobals: balls1CamFunc( 2 , 2 , .2 ) , + lights: [ + lightFunc(1,8) + ] , + updateText: null + } , { + rows: 3 , + rm: 'balls' , + setGlobals: balls1CamFunc( 2 , 5 , -.7 ) , + lights: [ + lightFunc(1,2) + ] , + updateText: null + } , { + rows: 7 , + rm: 'balls' , + setGlobals: balls1CamFunc( 2 , 2 , .7 ) , + lights: [ + lightFunc(1,2) + ] , + updateText: null + } , { + rows: 9 , + rm: 'balls' , + setGlobals: function(ctx) { + var z = ctx.time*3; + ctx.fade = ctx.stepTime / this.time; + vecSet(ctx.camPos,0,4+ctx.fade*2,z); + vecSet(ctx.lookAt,2,0,z+2); + vecSet(ctx.up,0,0,1); + ctx.fadeColour = 1; + } , + lights: [ + lightFunc(1,2) + ] , + updateText: null + } , { + rows: 21 , + rm: 'fractals' , + setGlobals: function( ctx ) { + fract1CamCommon(ctx); + ctx.fadeColour = 1; + var z = ctx.stepTime / this.time; + ctx.fade = 1 - z*z*z; + } , lights : [ + fractLight = function(ctx,lctx) { + vecCopy(lctx.pos,ctx.camPos); + lctx.pos[2]+=2; + vecSet(lctx.colour,1,1,1); + lctx.distance = 3; + return true; + } + ], + updateText: null + } , { + rows: 14 , + rm: 'fractals' , + setGlobals: fract1CamFunc(0) , + lights : [ + fractLight + ], + updateText: null + } , { + rows: 3 , + rm: 'fractals' , + setGlobals: fract1CamFunc(-.4) , + lights : [ fractLight ], + updateText: null + } , { + rows: 9 , + rm: 'fractals' , + setGlobals: fract1CamFunc(.4) , + lights : [ fractLight ], + updateText: null + } , { + rows: 3 , + rm: 'fractals' , + setGlobals: fract1CamFunc(-.2) , + lights : [ fractLight ], + updateText: null + } , { + rows: 9 , + rm: 'fractals' , + setGlobals: fract1CamFunc(.2) , + lights : [ fractLight ], + updateText: null + } , { + rows: 12 , + rm: 'fractals' , + setGlobals: function(ctx) { + fract1CamCommon(ctx); + ctx.desync = -( 6 * ctx.stepTime / this.time ) % 1; + ctx.desync *= Math.random( ) * .25 + .75; + } , + lights : [ fractLight ], + updateText: null + } , { + rows: 22 , + rm: 'fractals' , + setGlobals: function(ctx) { + fract1CamCommon(ctx); + ctx.fadeColour = .4; + ctx.fade = ctx.stepTime / this.time; + ctx.toNearPlane = 3 - 2.5*Math.max(1,2*ctx.fade); + ctx.desync = ( 30 * ctx.fade ) % 1; + ctx.desync *= Math.random( ) * .15 + .85; + } , + lights : [ fractLight ], + updateText: null + } , { + rows: 3 , + rm: 'fractals' , + setGlobals: neutral = function(ctx) { + fract1CamCommon(ctx); + ctx.fadeColour = .4; + ctx.fade = 1; + ctx.desync = 0; + } , + lights : [ fractLight ], + updateText: null + } , { + rows: 12 , + rm: 'fractals' , + setGlobals: neutral , + lights : [ fractLight ], + updateText: function() { + titleText( "TheT(ourist)" , .08 , 1 , 1 , 1 , 1 ); + } + } , { + rows: 12 , + rm: 'fractals' , + setGlobals: neutral , + lights : [ fractLight ], + updateText: function() { + titleText( "TheT(ourist)" , .08 , 1 - context.stepTime / this.time , 1 , 1 , 1 ); + } + } , { + rows: 12 , + rm: 'fractals' , + setGlobals: neutral , + lights : [ fractLight ], + updateText: function() { + titleText( "presents" , .08 , 1 - context.stepTime / this.time , 1 , 1 , 1 ); + } + } , { + rows: 20 , + rm: 'squares' , + setGlobals: function(ctx) { + squaresCam(ctx); + ctx.toNearPlane = 2.5; + ctx.fade = 1 - ctx.stepTime / this.time; + } , + lights : [ lightFunc(1,30) ], + updateText: function() { + titleText( "Sine City" , .04 , context.stepTime / this.time , 1 , 1 , 1 ); + } + } , { + rows: 52 , + rm: 'squares' , + setGlobals: function(ctx) { + squaresCam(ctx); + ctx.fade = 0; + } , + lights : [ lightFunc(1,30) ], + updateText: function() { + titleText( "Sine City" , .04 , 1 , 1 , 1 , 1 ); + } + } , { + rows: 12 , + rm: 'squares' , + setGlobals: function(ctx){ + squaresCam(ctx); + var z = context.stepTime / this.time; + ctx.fadeColour = 1; + ctx.fade = Math.max(0,1-z*2); + } , + lights : [ lightFunc(1,30) ], + updateText: function() { + var z = context.stepTime / this.time; + titleText( "Sine City" , .04 , 1 , 1 - z , 1 - z * .5 , 1 - z ); + } + } , { + rows: 12 , + rm: 'squares' , + setGlobals: function(ctx) { + squaresCam(ctx); + var z = context.stepTime / this.time; + ctx.desync = ( 1 - z ) * .7 + Math.random() * .025 - .0125; + ctx.greyMix = 1 - .5 * z; + } , + lights : [ lightFunc(1,30) ], + updateText: function() { + titleText( "Sine City" , .04 , 1 , 0 , .5 , 0 ); + } + } , { + rows: 9 , + rm: 'squares' , + setGlobals: function(ctx) { + squaresCam2(ctx,29); + var z = context.stepTime / this.time; + ctx.fade = Math.max(0,1-z*1.5); + ctx.desync += ( z - 1 ) * .3; + ctx.greyMix = .5 - .5 * z; + } , + lights : [ lightFunc(1,30) ], + updateText: function() { + titleText( "Sine City" , .04 , 1 - context.stepTime / this.time , 0 , .5 , 0 ); + } + } , { + rows: 69 , + rm: 'squares' , + setGlobals: function(ctx) { + squaresCam2(ctx,29); + ctx.greyMix = 0; + } , + lights : [ lightFunc(1,30) ], + updateText: null + } , { + rows: 11 , + rm: 'squares' , + setGlobals: function(ctx) { + squaresCam2(ctx,29); + ctx.fadeColour = .2; + ctx.fade = context.stepTime / this.time; + } , + lights : [ lightFunc(1,30) ], + updateText: null + } , { + rows: 11 , + rm: 'tunnel' , + setGlobals: function(ctx) { + ctx.tunMul = 1; + tunnelCam(ctx); + ctx.fade = 1 - context.stepTime / this.time; + } , + lights : [ tunnelLight ], + updateText: null + } , { + rows: 17 , + rm: 'tunnel' , + setGlobals: function(ctx) { + tunnelCam(ctx); + ctx.fade = 0; + ctx.desync = (1.2 - 1.2 * context.stepTime / this.time) * (.98 + .04*Math.random()); + } , + lights : [ tunnelLight ], + updateText: null + } , { + rows: 18 , + rm: 'tunnel' , + setGlobals: function(ctx) { + tunnelCam(ctx); + ctx.desync = 0; + ctx.extras[0] = '1f(p.BallOffset,'+(context.stepTime / this.time) + ')'; + } , + lights : [ tunnelLight ], + updateText: greetingsText + } , { + rows: 36 , + rm: 'tunnel' , + setGlobals: function(ctx) { + tunnelCam(ctx); + ctx.fade = 1 - Math.min(4*context.stepTime / this.time,1); + ctx.extras[0] = '1f(p.BallOffset,1)'; + } , + lights : tunLights = [ tunnelLight , tunnelLightBall ], + updateText: greetingsText + } , { + rows: 33 , + rm: 'tunnel' , + setGlobals: function(ctx) { + tunnelCam(ctx); + ctx.fadeColour = .7; + ctx.fade = context.stepTime / this.time; + ctx.extras[0] = '1f(p.BallOffset,1)'; + ctx.desync = -((4*ctx.fade)%1)*(.95+Math.random()*.1); + } , + lights : tunLights , + updateText: greetingsText + } , { + rows: 9 , + rm: 'fractals' , + setGlobals: function(ctx) { + fract2CamCommon(ctx,0); + ctx.fadeColour = 1; + ctx.fade = context.stepTime / this.time; + } , + lights : [ fractLight ], + updateText: greetingsText + } , { + rows: 20 , + rm: 'fractals' , + setGlobals: function(ctx) { + fract2CamCommon(ctx,context.stepTime / this.time); + ctx.fade = 0; + } , + lights : [ fractLight ], + updateText: greetingsText + } , { + rows: 31 , + rm: 'fractals' , + setGlobals: function(ctx) { + fract2CamCommon(ctx,1); + ctx.fade = Math.min(0,1-5*context.stepTime / this.time); + } , + lights : [ fractLight ], + updateText: greetingsText + } , { + rows: 5 , + rm: 'fractals' , + setGlobals: function(ctx) { + fract2CamCommon(ctx,1); + ctx.fade = 1-context.stepTime / this.time; + } , + lights : [ fractLight ], + updateText: greetingsText + } , { + rows: 18 , + rm: 'fractals' , + setGlobals: function(ctx) { + fract2CamCommon(ctx,1); + ctx.fade = Math.min(0,1-5*context.stepTime / this.time); + ctx.desync = Math.max(0,1.6*context.stepTime / this.time - .8 )*(.95+Math.random()*.1); + } , + lights : [ fractLight ], + updateText: greetingsText + } , { + rows: 12 , + rm: 'fractals' , + setGlobals: function(ctx) { + fract2CamCommon(ctx,1); + ctx.fadeColour = .1; + ctx.fade = context.stepTime / this.time; + ctx.desync = .8*(1-ctx.fade)*(.9+Math.random()*.2); + } , + lights : [ fractLight ], + updateText: greetingsText + } , { + rows: 22 , + rm: 'balls' , + setGlobals: function(ctx) { + balls2CamCommon(ctx); + ctx.fade = 1-context.stepTime / this.time; + ctx.desync = 0; + } , + lights : ballsLights = [ lightFunc(.6,30) , balls2LightFunc(1),balls2LightFunc(-1) ], + updateText: greetingsText + } , { + rows: 59 , + rm: 'balls' , + setGlobals: function(ctx) { + balls2CamCommon(ctx); + ctx.fadeColour = .8; + ctx.fade = Math.max(0,1-(12*context.stepTime / this.time)%4); + } , + lights : ballsLights, + updateText: greetingsText + } , { + rows: 8 , + rm: 'balls' , + setGlobals: function(ctx) { + balls2CamCommon(ctx); + ctx.fadeColour = .1; + ctx.fade = context.stepTime / this.time; + ctx.desync = .1-Math.random()*.2; + } , + lights : ballsLights, + updateText: null + } , { + rows: 7 , + rm: 'tunnel' , + setGlobals: function(ctx) { + ctx.tunMul = 2; + tunnelCam(ctx); + ctx.fade = 1-context.stepTime / this.time; + ctx.desync = .1-Math.random()*.2; + } , + lights : tunLights , + updateText: null + } , { + rows: 22 , + rm: 'tunnel' , + setGlobals: function(ctx) { + tunnelCam(ctx); + ctx.desync = .1-Math.random()*.2; + } , + lights : tunLights , + updateText: null + } , { + rows: 10 , + rm: 'tunnel' , + setGlobals: function(ctx) { + tunnelCam(ctx); + ctx.desync = context.stepTime*4+.1-Math.random()*.2; + ctx.fadeColour = 1; + ctx.fade = context.stepTime / this.time; + } , + lights : tunLights , + updateText: null + } , { + rows: 10 , + rm: 'squares' , + setGlobals: function(ctx) { + squaresCam2(ctx,49); + ctx.desync = (context.time-direction[48].startTime)*4+.1-Math.random()*.2; + ctx.fade = 1-context.stepTime / this.time; + } , + lights : [ lightFunc(1,30) ] , + updateText: null + } , { + rows: 40 , + rm: 'squares' , + setGlobals: function(ctx) { + squaresCam2(ctx,49); + ctx.desync = -(context.time-direction[48].startTime)*6; + } , + lights : [ lightFunc(1,30) ] , + updateText: null + } , { + rows: 40 , + rm: 'squares' , + setGlobals: function(ctx) { + squaresCam2(ctx,49); + ctx.desync = (context.time-direction[48].startTime)*8; + ctx.fadeColour = 0; + ctx.fade = context.stepTime / this.time; + } , + lights : [ lightFunc(1,30) ] , + updateText: null + } , { + rows: 68 , + rm: 'squares' , + setGlobals: function(ctx) { + squaresCam2(ctx,49); + ctx.fade = 1 + } , + lights : [ lightFunc(1,30) ] , + updateText: null + } +]; +var t = 0; +var BPM = 120; +for ( var i in direction ) { + direction[ i ].startTime = t; + t += ( direction[i].time = direction[ i ].rows * 60 / ( BPM * 4 ) ); + direction[ i ].endTime = t; +} +rows = t * BPM * 4 / 60; +console.log( "endTime: " + t + " (rows: " + rows + "; patterns: " + Math.floor(rows/36) + " + " + (rows - 36*Math.floor(rows/36)) + " extra row(s))" ); +console.log( "len : " + direction.length ); +t = 0; + +function getMaterialsCode(indices,noiseScale) +{ + var code = 'Material getMaterial( vec3 pos , float index ) {\n' + + 'Material m;'; + + for ( var i in indices ) { + if ( i > 0 ) { + code += 'else '; + } + var m = materials[ indices[i] ]; + code += 'if ( index == ' + shaderFloat( i ) + ' ) {\n' + + 'm.albedo = '; + if ( m.noiseScale ) { + code += 'mix( vec3(' + shaderVec( m.albedo ) + ') , vec3(' + + shaderVec( m.noise ) + ') , noise( pos * ' + + shaderFloat( noiseScale * m.noiseScale ) + ') )'; + } else { + code += 'vec3(' + shaderVec( m.albedo ) + ')'; + } + code += ';\nm.metallic=' + shaderFloat( m.metallic ) + ';\nm.smoothness=' + + shaderFloat( m.smoothness ) + ';\nm.r0=' + shaderFloat( m.r0 ) + ';\n}'; + } + + return code + 'return m;\n}\n'; +} + +function createRaymarcherCode( def , nLights ) { + var uniforms = [ 'CamMat' , 'CamPos' , 'Time' , 'Desync' , 'GreyMix' , 'Fade' , 'FadeColour' , 'TextParams' , 'TextColour' , 'Texture' ]; + var code = shaderBits['precision'] + + shaderBits['voTexCoords'] + + [ 'uniform mat3 uCamMat;' , + 'uniform vec3 uCamPos;' , + 'uniform float uTime;' , + 'uniform float uGreyMix;' , + 'uniform float uFade;' , + 'uniform float uFadeColour;' , + 'uniform vec3 uTextParams;' , + 'uniform vec3 uTextColour;' , + 'uniform sampler2D uTexture;' , + 'uniform float uDesync;' + ].join('\n'); + for ( var i in def.lights ) { + code += 'uniform vec3 uLightPos' + i + ', uLightColor' + i + ";\nuniform float uLightDistance" + i + ";\n"; + uniforms.splice( uniforms.length , 0 , 'LightPos' + i , 'LightColor' + i , 'LightDistance' + i ); + } + for ( var i in def.extraUniforms ) { + code += 'uniform ' + def.extraUniforms[i] + " u" + i + ";\n"; + uniforms.push(i); + } + for ( var i in def.extraBits ) { + code += shaderBits[def.extraBits[i]]; + } + code += shaderBits['rmUtils']; + code += 'vec2 cMap( vec3 p , float d ) {' + def.map + + '}' + getMaterialsCode(def.materials,def.noiseScale) + + [ + "vec3 getNormal( vec3 hp , float d )" , + "{" , + "vec2 e = vec2(1.,-1.)*" , shaderFloat(def.normalDelta) , ";" , + "vec4 f = vec4(" , + "cMap(hp+e.xyy,d).x," , + "cMap(hp+e.yyx,d).x," , + "cMap(hp+e.yxy,d).x," , + "cMap(hp+e.xxx,d).x" , + ");" , + "return normalize(f.x * e.xyy + f.y * e.yyx + f.z * e.yxy + f.w * e.xxx);" , + "}" , + + "float ambientOcclusion(vec3 p, vec3 n, vec3 cp){" , + "const int steps = ", def.aoSteps , ";" , + "const float delta = ", shaderFloat( def.aoDelta ) , ";" , + + "float a = 0.0;" , + "float weight = " , shaderFloat( def.aoWeight ) , ";" , + "float m;" , + "for(int i=1; i<=steps; i++) {" , + "float d = (float(i) / float(steps)) * delta;" , + "vec3 op = p + n * d;" , + "a += weight*(d - cMap(op,distance(op,cp)).x);" , + "weight *= 0.5;" , + "}" , + "return max(0.1 , 1.0 - clamp( pow(a , 1. ) , 0.0, 1.0) );" , + "}" , + + + "vec2 march( vec3 pos , vec3 dir , int nSteps)" , + "{" , + "float cd = 0. , e = " , shaderFloat( def.epsilon ) , ';' , + "vec2 m;" , + + "for ( int i = 0 ; i < " , def.depth , " ; i++ ) {" , + "m = cMap( pos + dir * cd , cd );" , + "if ( m.x < e || i > nSteps || cd >" , shaderFloat( def.maxDistance ) , " ) {" , + "break;" , + "}" , + "cd += m.x * " , shaderFloat( def.step ) , ";" , + "e *= " , shaderFloat( def.epsilonMultiplier ) , ";" , + "}" , + + "return vec2(cd,m.y);" , + "}" , + + "void main( ) " , + "{" , + "vec3 tc = vec3(voTexCoords,1.);" , + "tc.y += mod(uDesync,1.)*2.2;" , + "if ( tc.y > 1. ) {" , + "if ( tc.y < 1.2 ) discard;" , + "tc.y -= 2.2;" , + "}" , + "tc.x *= 1.6;" , + "vec3 dir = normalize( tc * uCamMat ) ," , + "pos = uCamPos , outColor = vec3( 0. ) , " , + "cMul = vec3( 1. ) , ld , l;" , + + "float ll;" , + "for ( int j = 0 ; j < 2 ; j ++ ) {" , + "vec3 col, nDir = dir , nPos = pos, ncMul = vec3(0.);" , + "vec2 vl,r = march( pos , dir , " , def.depth , " / ( j + 1 ) );" , + "if ( r.x < " , shaderFloat( def.maxDistance ) , " ) {" , + "vec3 hp = pos + dir * r.x , n=getNormal( hp , r.x ),hDir;" , + "nDir = reflect(dir,n);" , + "hDir = normalize( nDir + dir );" , + "float fog = " , shaderFloat( def.fogDensity ) , " * r.x;" , + "fog *= fog*fog*1.442695;" , + "fog = clamp( exp(-fog) , 0., 1. );" , + "Material m = getMaterial( hp , r.y );" , + "float occlusion = ambientOcclusion( hp , n , pos ) , " , + "fresnel = schlick( hDir , dir , m.r0 , m.smoothness * .9 + .1 );" , + "vec3 sCol = mix(vec3(1.),m.albedo/dot(LUMA,m.albedo),m.metallic) ," , + "dc , sc;" , + "col = vec3(0.);" , + ].join('\n'); + + for ( var l in def.lights ) { + var ld = def.lights[l]; + code += [ + "ld = uLightPos" + l + " - hp;" , + "ll = length(ld);" , + "ld /= ll;" , + "ll = max( 1. , ll - uLightDistance" + l + ") * " , shaderFloat( ld.attenuation ) , ";" , + "ll = 1. / max( 1. , ll * ll );" , + "l = ll * max(dot(ld,n),0.) * uLightColor" + l + ";" , + "dc = (1. - m.metallic) * m.albedo * occlusion;" , + "sc = sCol * getSpecular( dir , ld , n , m.smoothness );" , + "col += mix( dc , sc , fresnel ) * l;" + ].join('\n'); + code += ";"; + } + code += [ + "ncMul = cMul * fog * normalize(sCol) * m.r0 * (m.smoothness * .9 + .1) * fresnel;" , + "col = mix( vec3(" , shaderFloat(def.fog) , ") , col , fog );" , + "nPos += normalize(nDir) * " , shaderFloat(def.reflectionDistance) , ";" , + "} else {" , + "col=vec3(" , shaderFloat(def.fog) , ');' , + "}" , + ].join('\n'); + + for ( var l in def.lights ) { + var ld = def.lights[l]; + var r = ld.vRadius; + if ( r ) { + code += [ + "ld = pos - uLightPos" + l + ";" , + "vl = solve(dot(dir,dir),2.*dot(ld,dir),dot(ld,ld)-" + shaderFloat(r*r) + ");" , + "if ( vl.x > 0. && vl.y < r.x ) {" , + "float fog = " , shaderFloat( def.fogDensity ) , " * vl.x;" , + "fog *= fog*fog*1.442695;" , + "fog = clamp( exp(-fog) , 0., 1. );" , + "col += fog * uLightDistance" + l + " * uLightColor" + l + " * pow( (vl.y-vl.x) / " + shaderFloat(r*2) + " , 64. );" , + "}" + ].join( "\n" ); + } + } + + code += [ + "outColor += col * cMul;" , + "if ( all(lessThan(ncMul, vec3(.01))) ) {" , + "break;" , + "}" , + "cMul = ncMul;" , + "pos = nPos;" , + "dir = normalize(nDir);" , + "}" , + + "vec3 cMix = ( outColor.r == outColor.g && outColor.b == outColor.g ) ? outColor : vec3( dot( outColor , LUMA ) );" , + "outColor = mix( mix( outColor , cMix , uGreyMix ) , vec3(uFadeColour) , smoothstep( .05 , .95 , uFade ) );" , + + "cMix.xy = vec2(voTexCoords.x,tc.y) * .5 + .5;" , + "cMix.y = ( 1. - cMix.y - uTextParams.x ) / uTextParams.z;" , + "vec4 tcol = texture2D( uTexture , cMix.xy );" , + "outColor = mix(outColor , tcol.rgb * uTextColour , tcol.a * uTextParams.y );" , + + "outColor = max( vec3( 0. ) , outColor - vec3( .004 ) );" , + "outColor = ( outColor * ( 6.2 * outColor + .5 ) ) / ( outColor * ( 6.2 * outColor + vec3( 1.7 ) ) + vec3( .06 ) );" , + + "float g = smoothstep(.98,1.,noise(tc*8.-uTime*40.));" , + "outColor *= .95 + .05 * cos(uTime*41.);" , + "outColor += vec3(g);" , + "g = 1.-(smoothstep(.98,1.,noise(tc*8.+uTime*20.))+smoothstep(.95,1.,noise(tc*1.5+uTime*10.)));" , + + "float v = g*smoothstep(1.042+.008*cos(uTime*40.),.8,lx(voTexCoords,8.));" , + "g = (tc.x + 1. ) * (tc.y + 1. ) * (uTime * 10.0);" , + "v *= 1.-(mod((mod(g, 13.0) + 1.0) * (mod(g, 47.0) + 1.0), 0.01)) * 8.;" , + + "gl_FragColor = vec4(v*outColor,1.);" , + "}" , + ].join('\n'); + return [ code , uniforms ]; +} + +function buildRaymarchers( ) +{ + var ml = 1; + for ( var n in raymarchers ) { + var rm = raymarchers[n]; + var nl = rm.lights.length; + ml=nl>ml?nl:ml; + for ( var i=1;i<=nl;i++) { + var sn = 'rmfs_' + n + '_' + i; + var sd = createRaymarcherCode(rm,i); + shaderBits[sn] = sd[0]; + shaders[sn] = ['f',sn]; + programs['rm_' + n + '_' + i] = [ sd[1] , 'raymarchVS' , sn ]; + } + } + var context = { + camPos: vomNew(3), + lookAt: vomNew(3), + up: vomNew(3), + toNearPlane: 0 , + time: 0 , + desync: 0 , + greyMix: 0 , + fade: 0 , + fadeColour: 0 , + text: vomNew(3) , + textColour: vomNew(3) , + lights: [] + }; + for ( var i = 0;ivw){ + C1.width=canvasWidth=vw; + C1.height=canvasHeight=ch; + }else{ + C1.width=canvasWidth=cw; + C1.height=canvasHeight=vh; + } + C2.style.width=C2.width=canvasWidth; + c2height=C2.style.height=C2.height=Math.max(canvasHeight/8,100); + c2rHeight=c2height/canvasHeight; + twoDCtx.shadowColor = "#ccc"; + twoDCtx.font = "normal small-caps bold " + Math.floor(c2height/2) + "px monospace"; + twoDCtx.fillStyle = "#111"; + twoDCtx.strokeStyle = "#ddd"; + C1.style.left=Math.floor((vw-canvasWidth)*.5); + C1.style.top=Math.floor((vh-canvasHeight)*.5); + glCtx.viewport(0,0,canvasWidth,canvasHeight); +} + +// FIXME: re-enable! document.body.style.cursor = 'none'; +document.body.style.background = 'black'; +document.body.innerHTML = '