Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local ffi = require ("ffi")
- --
- ffi.cdef [[
- typedef struct Complex {double X, Y;} Complex;
- ]]
- --
- App = {Ring = {}, Complex = {}, Offset = {}, Shaders = {}, Meshes = {}, Canvases = {}}
- App.Sound = nil
- App.Audio = nil
- App.Legend = false
- App.Bloom = true
- App.Ring.Count = 48
- App.Ring.Order = 2
- App.Ring.Color = {1, 0.4, 0.05}
- App.Ring.Data = love.data.newByteData (ffi.sizeof ("float")*App.Ring.Count)
- App.Ring.Waves = {[App.Ring.Order] = ffi.cast ("float *", App.Ring.Data:getFFIPointer ())}
- App.Complex.Count = 1024
- App.Complex.Wave = ffi.new ("Complex [?]", App.Complex.Count)
- App.Offset.Order = 3
- App.Offset.Frequency = 4
- App.Offset.X = ffi.new ("double [?]", App.Offset.Order + 1)
- App.Offset.Y = ffi.new ("double [?]", App.Offset.Order + 1)
- App.Shaders.Ring = love.graphics.newShader [[
- #define COUNT 48
- #define SIZE 0.8
- #define SMOOTH 0.04
- //
- const float PI = acos (-1.0);
- //
- uniform vec3 Color;
- uniform float Data [COUNT];
- uniform vec2 Offset;
- //
- void effect () {
- vec2 FP = (VaryingTexCoord.xy + Offset - 0.5)*2.0;
- float FD = length (FP);
- float FA = atan (FP.y, FP.x);
- //
- float RI = (FA/PI*0.5 - 0.25 - 0.5/COUNT)*COUNT;
- float RF = floor (RI);
- float RC = ceil (RI);
- float RA = Data [int (mod (RF, COUNT))];
- float RB = Data [int (mod (RC, COUNT))];
- float RV = RF != RC ? mix (RA, RB, smoothstep (RF, RC, RI)) : RA;
- float RE = RV - abs (FD - SIZE)/(1.0 - SIZE);
- //
- if (RE <= 0.0) {discard;}
- //
- love_PixelColor = vec4 (Color*(RV*3.5 + 0.8), min (RE/SMOOTH, 1.0));
- }
- ]]
- App.Shaders.Blur = love.graphics.newShader [[
- #pragma language glsl3
- //
- const vec3 Kernel [] = vec3 [13] (
- vec3 (-2.0, -2.0, 1.0/30.0),
- vec3 (+0.0, -2.0, 2.0/30.0),
- vec3 (+2.0, -2.0, 1.0/30.0),
- vec3 (-1.0, -1.0, 4.0/30.0),
- vec3 (+1.0, -1.0, 4.0/30.0),
- vec3 (-2.0, +0.0, 2.0/30.0),
- vec3 (+0.0, +0.0, 2.0/30.0),
- vec3 (+2.0, +0.0, 2.0/30.0),
- vec3 (-1.0, +1.0, 4.0/30.0),
- vec3 (+1.0, +1.0, 4.0/30.0),
- vec3 (-2.0, +2.0, 1.0/30.0),
- vec3 (+0.0, +2.0, 2.0/30.0),
- vec3 (+2.0, +2.0, 1.0/30.0)
- );
- //
- uniform Image MainTex;
- //
- void effect () {
- vec2 Dimensions = vec2 (textureSize (MainTex, 0));
- //
- vec3 Color = vec3 (0.0);
- //
- for (int Index = 0; Index < 13; ++ Index) {
- vec3 Info = Kernel [Index];
- //
- vec3 TextureColor = Texel (MainTex, VaryingTexCoord.xy + Info.xy/Dimensions).rgb;
- //
- Color += TextureColor*Info.z;
- }
- //
- love_PixelColor = vec4 (Color, 1.0);
- }
- ]]
- App.Meshes.Ring = love.graphics.newMesh (
- {
- {"VertexPosition", "float", 2},
- {"VertexTexCoord", "float", 2}
- }, {
- {0, 0, 0, 0},
- {1, 0, 1, 0},
- {1, 1, 1, 1},
- {0, 1, 0, 1}
- }
- )
- App.Canvases.Index = 0
- --
- for Index = 0, App.Ring.Order - 1 do App.Ring.Waves [Index] = ffi.new ("float [?]", App.Ring.Count) end
- --
- io.stdout:setvbuf ("no")
- --
- function App.map (V, A, B, X, Y)
- return X + (Y - X)*(V - A)/(B - A)
- end
- function App.emap (V, A, B, X, Y)
- return math.pow (Y/X, (V - A)/(B - A))*X
- end
- function App.slope (V, A, B, X, Y)
- return math.pow (V/A, math.log (Y, X))*B
- end
- function App.volume (D)
- return math.pow (10, D/20)
- end
- function App.abs (C)
- return math.sqrt (C.X*C.X + C.Y*C.Y)
- end
- function App.smooth (A, B, F, DT)
- local R = F*DT*math.pi*2
- --
- return A + (B - A)/(R + 1)*R
- end
- function App.mratio (R, X, Y)
- if not Y then Y = X end
- --
- local W = math.min (Y*R, X)
- local H = math.min (X/R, Y)
- --
- return W, H
- end
- function App.complexTransform (W, C, I)
- if C == 1 then return W end
- --
- local D, S = C/2, I and 1 or -1
- --
- local A = ffi.new ("Complex [?]", D)
- local B = ffi.new ("Complex [?]", D)
- --
- for J = 0, D - 1 do A [J], B [J] = W [J*2], W [J*2 + 1] end
- --
- local X = App.complexTransform (A, D)
- local Y = App.complexTransform (B, D)
- --
- local T = ffi.new ("Complex [?]", C)
- --
- for J = 0, D - 1 do
- local EA = S*J/D*math.pi
- --
- local EX = math.cos (EA)
- local EY = math.sin (EA)
- --
- local FX = Y [J].X*EX - Y [J].Y*EY
- local FY = Y [J].X*EY + Y [J].Y*EX
- --
- T [J].X = X [J].X + FX
- T [J].Y = X [J].Y + FY
- --
- T [D + J].X = X [J].X - FX
- T [D + J].Y = X [J].Y - FY
- end
- --
- return T
- end
- function App.updateCanvases (MW, MH)
- for Index = 0, #App.Canvases do App.Canvases [Index] = nil end
- --
- for Index = 0, math.huge do
- local CW = math.floor (MW/2^Index)
- local CH = math.floor (MH/2^Index)
- --
- if CW < 8 or CH < 8 then break end
- --
- local Samples = Index == 0 and 4 or 0
- --
- App.Canvases [Index] = love.graphics.newCanvas (CW, CH, {format = "rg11b10f", msaa = Samples})
- end
- --
- App.Canvases.Index = math.min (App.Canvases.Index, #App.Canvases)
- --
- return true
- end
- --
- function love.update (DT)
- if App.Audio and App.Sound and App.Audio:isPlaying () then
- local AI = App.Audio:tell ("samples")
- local SC = App.Sound:getSampleCount ()
- local CC = App.Sound:getChannelCount ()
- --
- for Index = 0, App.Complex.Count - 1 do
- local SI = AI - App.Complex.Count + Index + 1
- --
- if CC == 1 then
- local MV = SI >= 0 and SI < SC and App.Sound:getSample (SI) or 0
- --
- App.Complex.Wave [Index] = {MV, MV}
- else
- local LV = SI >= 0 and SI < SC and App.Sound:getSample (SI, 1) or 0
- local RV = SI >= 0 and SI < SC and App.Sound:getSample (SI, 2) or 0
- --
- App.Complex.Wave [Index] = {LV, RV}
- end
- end
- else
- for Index = 0, App.Complex.Count - 1 do App.Complex.Wave [Index] = {0, 0} end
- end
- --
- local FrequencyWave = App.complexTransform (App.Complex.Wave, App.Complex.Count)
- --
- local SR = App.Sound and App.Sound:getSampleRate () or 1
- --
- for Index = 0, App.Ring.Count - 1 do
- local FR = App.emap (Index, 0, App.Ring.Count - 1, 20, 20000)
- local FS = App.slope (FR, 20, 1, 2, App.volume (3.8))
- local FI = FR/SR*App.Complex.Count
- --
- local FF, FC = math.floor (FI), math.ceil (FI)
- local FA, FB = FrequencyWave [FF % App.Complex.Count], FrequencyWave [FC % App.Complex.Count]
- --
- local FV = FF ~= FC and App.map (FI, FF, FC, App.abs (FA), App.abs (FB)) or App.abs (FA)
- --
- App.Ring.Waves [0] [Index] = FV*FS/App.Complex.Count*0.9 + 0.06
- --
- for OI = 1, App.Ring.Order do
- local SA = App.Ring.Waves [OI] [Index]
- local SB = App.Ring.Waves [OI - 1] [Index]
- --
- App.Ring.Waves [OI] [Index] = App.smooth (SA, SB, 6, DT)
- end
- end
- --
- local OV, OC = 0, 32
- --
- for OI = 1, OC do
- local FR = App.emap (OI, 1, OC, 20, 20000)
- local FI = FR/SR*App.Complex.Count
- --
- local FF, FC = math.floor (FI), math.ceil (FI)
- local FA, FB = FrequencyWave [FF % App.Complex.Count], FrequencyWave [FC % App.Complex.Count]
- --
- local FV = FF ~= FC and App.map (FI, FF, FC, App.abs (FA), App.abs (FB)) or App.abs (FA)
- --
- OV = OV + FV/FR/FR/App.Complex.Count
- end
- --
- App.Offset.X [0] = (math.random ()*2 - 1)*OV*32
- App.Offset.Y [0] = (math.random ()*2 - 1)*OV*32
- --
- for OI = 1, App.Offset.Order do
- App.Offset.X [OI] = App.smooth (App.Offset.X [OI], App.Offset.X [OI - 1], App.Offset.Frequency, DT)
- App.Offset.Y [OI] = App.smooth (App.Offset.Y [OI], App.Offset.Y [OI - 1], App.Offset.Frequency, DT)
- end
- end
- function love.draw ()
- if not App.Canvases [0] then return end
- --
- local WW, WH = love.graphics.getDimensions ()
- --
- local RW, RH = App.mratio (1, WW*0.75, WH*0.75)
- local RX, RY = WW/2 - RW/2, WH/2 - RH/2
- --
- App.Shaders.Ring:send ("Color", App.Ring.Color)
- App.Shaders.Ring:send ("Data", App.Ring.Data)
- App.Shaders.Ring:send ("Offset", {App.Offset.X [App.Offset.Order], App.Offset.Y [App.Offset.Order]})
- --
- love.graphics.push ("all")
- love.graphics.setCanvas (App.Canvases [0])
- love.graphics.setShader (App.Shaders.Ring)
- love.graphics.clear ()
- love.graphics.setBlendMode ("replace", "alphamultiply")
- love.graphics.draw (App.Meshes.Ring, RX, RY, 0, RW, RH)
- love.graphics.pop ()
- --
- if App.Bloom then
- love.graphics.push ("all")
- love.graphics.setBlendMode ("replace", "premultiplied")
- love.graphics.setShader (App.Shaders.Blur)
- --
- for Index = 1, #App.Canvases do
- local Current, Previous = App.Canvases [Index], App.Canvases [Index - 1]
- --
- local CW, CH = Current:getDimensions ()
- local PW, PH = Previous:getDimensions ()
- --
- Previous:setFilter ("linear")
- --
- love.graphics.setCanvas (Current)
- love.graphics.draw (Previous, 0, 0, 0, CW/PW, CH/PH)
- end
- --
- love.graphics.setBlendMode ("add", "premultiplied")
- --
- for Index = #App.Canvases - 1, 0, -1 do
- local Current, Next = App.Canvases [Index], App.Canvases [Index + 1]
- --
- local CW, CH = Current:getDimensions ()
- local NW, NH = Next:getDimensions ()
- --
- Next:setFilter ("linear")
- --
- love.graphics.setCanvas (Current)
- love.graphics.draw (Next, 0, 0, 0, CW/NW, CH/NH)
- end
- --
- love.graphics.pop ()
- end
- --
- local Canvas = App.Canvases [App.Canvases.Index]
- --
- local CW, CH = Canvas:getDimensions ()
- --
- Canvas:setFilter ("nearest")
- --
- love.graphics.push ("all")
- love.graphics.setBlendMode ("replace", "premultiplied")
- love.graphics.draw (Canvas, 0, 0, 0, WW/CW, WH/CH)
- love.graphics.pop ()
- --
- if App.Legend then
- local Info = string.format ("Mip level: %i/%i (%i, %i)", App.Canvases.Index, #App.Canvases, CW, CH)
- --
- love.graphics.print (string.format ("FPS: %i", love.timer.getFPS ()), 10, 10)
- love.graphics.print (Info, 10, 25)
- --
- if App.Audio then
- local AP, AD = App.Audio:tell (), App.Audio:getDuration ()
- --
- local Position = string.format ("%i:%02i", AP/60, AP % 60)
- local Duration = string.format ("%i:%02i", AD/60, AD % 60)
- --
- love.graphics.print (string.format ("Audio position: %s/%s", Position, Duration), 10, 40)
- else
- love.graphics.print ("No audio. Drag and drop an audio file on this window.", 10, 40)
- end
- --
- love.graphics.print ("Keyboard:", 10, 70)
- love.graphics.print ("[ESCAPE] Quit.", 10, 85)
- love.graphics.print ("[F] Toggle fullscreen.", 10, 100)
- love.graphics.print ("[L] Toggle this legend.", 10, 115)
- love.graphics.print ("[B] Toggle bloom effect.", 10, 130)
- love.graphics.print ("[Q/E] Change mip level (requires bloom enabled).", 10, 145)
- love.graphics.print ("[S] Stop audio.", 10, 160)
- love.graphics.print ("[A/D] Rewind or advance audio 5 seconds.", 10, 175)
- love.graphics.print ("[SPACE BAR] Play or pause audio.", 10, 190)
- love.graphics.print ("[0-9] Seek audio.", 10, 205)
- end
- end
- function love.resize (WW, WH)
- App.updateCanvases (WW, WH)
- end
- function love.keypressed (Button)
- if Button == "escape" then
- love.event.quit ()
- elseif Button == "f" then
- love.window.setFullscreen (not love.window.getFullscreen ())
- elseif Button == "l" then
- App.Legend = not App.Legend
- elseif Button == "b" then
- App.Bloom = not App.Bloom
- --
- if not App.Bloom then App.Canvases.Index = 0 end
- elseif Button == "q" then
- App.Canvases.Index = math.max (App.Canvases.Index - 1, 0)
- elseif Button == "e" then
- if App.Bloom then App.Canvases.Index = math.min (App.Canvases.Index + 1, #App.Canvases) end
- elseif App.Audio then
- if Button == "s" then
- App.Audio:stop ()
- elseif Button == "a" then
- App.Audio:seek (math.max (App.Audio:tell () - 5, 0))
- elseif Button == "d" then
- App.Audio:seek (math.min (App.Audio:tell () + 5, App.Audio:getDuration ()))
- elseif Button == "space" then
- if App.Audio:isPlaying () then App.Audio:pause () else App.Audio:play () end
- else
- for Index = 0, 9 do
- if Button == tostring (Index) or Button == "kp" .. Index then
- App.Audio:seek (App.Audio:getDuration ()*Index/10)
- --
- break
- end
- end
- end
- end
- end
- function love.filedropped (File)
- pcall (function ()
- if App.Audio then App.Audio:stop () end
- --
- App.Sound = love.sound.newSoundData (File)
- App.Audio = love.audio.newSource (App.Sound)
- --
- App.Audio:play ()
- end)
- end
- --
- App.updateCanvases (love.graphics.getDimensions ())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement