1local destroy_shid; 2 3local shaders = { 4-- a more ambitious version would use a LUT to give a perlin-noise 5-- like distribution, weight that with the contents and the distance 6-- to the last known mouse cursor position, with an edge gradient 7-- using yellow-red-blacks for the burn. 8dissolve = {nil, nil, [[ 9uniform sampler2D map_tu0; 10uniform sampler2D map_tu1; 11varying vec2 texco; 12uniform float trans_blend; 13 14void main() 15{ 16 vec4 col = texture2D(map_tu0, texco); 17 vec4 ptn = texture2D(map_tu1, texco); 18 19 float intens = (col.r + col.g + col.b) / 3.0; 20 intens = intens / (intens + 0.5); 21 intens = max(0.3 + ptn.r, intens); 22 23 if (intens < trans_blend) 24 discard; 25 if (intens < trans_blend + 0.02){ 26 col.r = 1.0; 27 col.g = 0.5; 28 col.b = 0.0; 29 } 30 31 col.a = 1.0; 32 gl_FragColor = col; 33} 34 35]], "destroy_burn" 36}, 37}; 38 39local flame = build_shader(nil, [[ 40uniform sampler2D map_tu0; 41varying vec2 texco; 42uniform float trans_blend; 43 44float hash2D(vec2 x) { 45 return fract(sin(dot(x, vec2(13.454, 7.405)))*12.3043); 46} 47 48float voronoi2D(vec2 uv) { 49 vec2 fl = floor(uv); 50 vec2 fr = fract(uv); 51 float res = 1.0; 52 for( int j=-1; j<=1; j++ ) { 53 for( int i=-1; i<=1; i++ ) { 54 vec2 p = vec2(i, j); 55 float h = hash2D(fl+p); 56 vec2 vp = p-fr+h; 57 float d = dot(vp, vp); 58 59 res +=1.0/pow(d, 8.0); 60 } 61 } 62 return pow( 1.0/res, 1.0/16.0 ); 63} 64 65void main() 66{ 67 vec4 col = texture2D(map_tu0, texco); 68 vec2 uv = texco / vec2(100, 100); 69 float tv = 2.0 * trans_blend - 1.0; 70 71 float up0 = voronoi2D(uv * vec2(6.0, 4.0) + vec2(0, tv )); 72 float up1 = 0.5 + voronoi2D(uv * vec2(6.0, 4.0) + vec2(42, tv) + 30.0 ); 73 float finalMask = up0 * up1 + (1.0-uv.y); 74 75 finalMask += (1.0-uv.y)* 0.5; 76 finalMask *= 0.7-abs(uv.x - 0.5); 77 78 vec3 dark = mix( vec3(0.0), vec3( 1.0, 0.4, 0.0), step(0.8,finalMask) ) ; 79 vec3 light = mix( dark, vec3( 1.0, 0.8, 0.0), step(0.95, finalMask) ) ; 80 81 gl_FragColor = vec4(light, 1.0); 82} 83 84]], "flame"); 85 86-- on-demand compile shaders 87local function synch_shader(key) 88 local sk = shaders[key]; 89 assert(sk); 90 91 if (sk[1]) then 92 return sk[1]; 93 else 94 sk[1] = build_shader(sk[2], sk[3], sk[4]); 95 return sk[1]; 96 end 97end 98 99local function run_destr_eval(evalf, wm, wnd, space, space_active, popup) 100 if (not space_active or popup) then 101 return; 102 end 103 104 flair_supp_segment(wnd, 105 wnd.effective_w < 100 and wnd.effective_w * 0.2 or wnd.effective_w * 0.1, 106 wnd.effective_h < 100 and wnd.effective_h * 0.2 or wnd.effective_h * 0.1, 107 evalf); 108end 109 110local function falling(vid, speed, cx, cy, cw, ch, ox, oy, sx, sy, sw, sh) 111 local rx = ox - cx; 112 local ry = oy - cy; 113 local dist = 0.00001 + math.sqrt(rx*rx+ry*ry); 114 115 local mx = sw - ox; 116 local my = sw - oy; 117 local maxdist = 0.00001 + math.sqrt(sw * sw + sh * sh); 118 119-- initial delay is proportional to the distance from the epicentrum 120-- manipulating these transformations and delays really defines the 121-- effect, be creative :) 122 local fact = dist / maxdist; 123 local delay = fact * (0.5 * speed); 124 move_image(vid, cx, cy, delay); 125 resize_image(vid, cw, ch, delay); 126 rotate_image(vid, 0, delay + delay * 0.1); 127 blend_image(vid, 1.0, delay); 128 129 local ifun = INTERP_EXPOUT; 130-- have each piece travel the same distance so the speed will match 131 move_image(vid, cx+8, cy+8, speed, ifun); 132 resize_image(vid, 16, 16, speed, ifun); 133 rotate_image(vid, math.random(359), speed, ifun); 134 blend_image(vid, 0.0, speed, ifun); 135-- image_shader(vid, flame); 136end 137 138-- generic runner for creating a canvas copy and dispatching a 139-- shader, can be re-used for all effects that don't require special 140-- details like window specific uniforms 141local noise_source; 142local function run_shader(key, wm, wnd, space, space_active, popup) 143 if (not space_active) then 144 return; 145 end 146 147 if (not noise_source) then 148 noise_source = random_surface(64, 64, "fbm", 2, 0.5, 6, 1, 1, 1); 149 image_texfilter(noise_source, FILTER_BILINEAR); 150 end 151 152 local vid = flair_supp_clone(wnd); 153 image_framesetsize(vid, 2, FRAMESET_MULTITEXTURE); 154 set_image_as_frame(vid, noise_source, 1); 155 156 if (valid_vid(vid)) then 157 blend_image(vid, 0.0, gconfig_get("flair_speed")); 158 image_shader(vid, synch_shader("dissolve")); 159 end 160end 161 162-- re-use the menu entry but apply on a 'fake' window where we unpin 163-- and expire randomly by attaching to its update timer 164local cloth = system_load("tools/flair/cloth.lua")(); 165 166local function run_clothfall(wm, wnd, space, space_active, popup) 167 if (not space_active) then 168 return; 169 end 170 171 local vid = flair_supp_clone(wnd); 172 if (not valid_vid(vid)) then 173 return; 174 end 175 176 local fwin = { 177 canvas = vid, 178 border = null_surface(1,1) 179 }; 180 link_image(fwin.border, fwin.canvas); 181 182 local count = gconfig_get("flair_speed"); 183 local steps = 0; 184 185 cloth.start(fwin, true); 186 local oupd = fwin.verlet.update; 187 fwin.verlet.update = function(...) 188 189 for i=0,fwin.verlet.w-1,1 do 190 fwin.verlet_control.pin(fwin.verlet, i, 0, false); 191 end 192 193-- will cause the destructor to run 194 if (not valid_vid(vid)) then 195 fwin.canvas = nil; 196 return false; 197 end 198 199 return oupd(...); 200 end 201end 202 203return { 204 dissolve = function(...) 205 run_shader("dissolve", ...); 206 end, 207 splitfade = function(...) 208 run_destr_eval(falling, ...); 209 end, 210 clothfall = function(...) 211 run_clothfall(...); 212 end 213}; 214