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