diff --git a/resources/shaders/incinerate.frag b/resources/shaders/incinerate.frag
index 08a6225..e1b551f 100644
--- a/resources/shaders/incinerate.frag
+++ b/resources/shaders/incinerate.frag
@@ -13,79 +13,124 @@
// The content from common.glsl is automatically prepended to each shader effect.
+uniform sampler2D uAshTexture;
uniform vec2 uSeed;
-uniform vec3 uColor;
+// uniform vec3 uColor;
uniform float uScale;
-uniform float uWidth;
+// uniform float uWidth;
uniform float uTurbulence;
+const float BURN_TIME = 0.5;
+const float FIRE_WIDTH = 0.03;
+const float ASH_WIDTH = 0.5;
+const float ASH_LAYERS = 4;
+
vec4 getFireColor(float v) {
- const float steps[3] = float[](0.0, 0.5, 1.0);
- vec4 colors[3] = vec4[](vec4(1, 0, 0, 0), vec4(1, 0, 0, 0.5), vec4(1, 1, 0, 1));
+ const float steps[4] = float[](0.0, 0.33, 0.66, 1.0);
+ vec4 colors[4] =
+ vec4[](vec4(1, 0, 0, 0), vec4(1, 0, 0, 0.5), vec4(1, 1, 0, 1), vec4(1, 0, 0, 0));
if (v < steps[0]) {
return colors[0];
}
- for (int i = 0; i < 2; ++i) {
+ for (int i = 0; i < 3; ++i) {
if (v <= steps[i + 1]) {
- return mix(colors[i], colors[i + 1],
- vec4(v - steps[i]) / (steps[i + 1] - steps[i]));
+ float blend = smoothstep(0, 1, (v - steps[i]) / (steps[i + 1] - steps[i]));
+ return mix(colors[i], colors[i + 1], vec4(blend));
}
}
- return colors[2];
+ return colors[3];
}
void main() {
- float progress = uProgress / 0.8;
-
- progress = uForOpening ? 1.0 - progress : progress;
-
- vec2 uv = 0.01 * cogl_tex_coord_in[0].st * uSize / uScale;
-
- float lfNoise = simplex2D(cogl_tex_coord_in[0].st + uSeed);
- lfNoise = mix(lfNoise - 1.0, lfNoise + 1.0, progress);
-
- float hfNoise = simplex2DFractal(uv + uSeed);
- hfNoise = mix(hfNoise - 1.0, hfNoise + 1.0, progress);
-
- float noise = mix(lfNoise, hfNoise, uTurbulence);
- float fire = noise / (0.03 * uWidth) - progress / (0.03 * uWidth) + progress;
-
- vec4 windowColor = texture2D(uTexture, cogl_tex_coord_in[0].st);
-
// Shell.GLSLEffect uses straight alpha. So we have to convert from premultiplied.
+ vec4 windowColor = texture2D(uTexture, cogl_tex_coord_in[0].st);
if (windowColor.a > 0) {
windowColor.rgb /= windowColor.a;
}
cogl_color_out = windowColor;
- if (fire >= 1) {
- cogl_color_out.a = 0;
- } else if (fire > 0) {
- vec4 fire = getFireColor(fire);
- fire.a *= cogl_color_out.a;
- cogl_color_out = alphaOver(cogl_color_out, fire);
+ vec2 uv = cogl_tex_coord_in[0].st * uSize / uScale;
+
+ float lfNoise = simplex2D(uv / max(uSize.x, uSize.y) + uSeed);
+ float hfNoise = simplex2DFractal(uv * 0.01 + uSeed);
+
+ float noise = (lfNoise + hfNoise * uTurbulence * 0.5) / (1.0 + uTurbulence * 0.5);
+
+ float burnProgress =
+ (uForOpening ? 1.0 - uProgress / BURN_TIME : uProgress / BURN_TIME);
+
+ vec2 fireRange = vec2(burnProgress - FIRE_WIDTH, burnProgress + FIRE_WIDTH);
+
+ if (uForOpening) {
+
+ // Hide window.
+ if (noise < fireRange.x) {
+ cogl_color_out.a = 0.0;
+ }
+
+ } else {
+
+ // Draw ash particles if the window is closed.
+ vec2 ashRange = vec2(burnProgress - ASH_WIDTH, burnProgress);
+
+ float ash = clamp(1.0 - (noise - ashRange.x) / (ashRange.y - ashRange.x), 0, 1);
+ // vec4 ashColor = vec4(0, 0, 0, 0);
+ // cogl_color_out = mix(cogl_color_out, ashColor, ash);
+
+ // for (float i = 0; i < ASH_LAYERS; ++i) {
+
+ // float factor = ASH_LAYERS == 1 ? 0 : i / (ASH_LAYERS - 1);
+
+ // Now check wether there is actually something in the current ash layer at
+ // the coords position.
+ vec2 ashMap = texture2D(uAshTexture, uv * 0.005).rg;
+ // float ashGroup = floor(ashMap.g * ASH_LAYERS * 0.999);
+
+ // if (ashGroup == i) {
+
+ // Get the window color.
+ // vec4 windowColor = texture2D(uTexture, coords + 0.5);
+
+ // // Shell.GLSLEffect uses straight alpha. So we have to convert from
+ // premultiplied. if (windowColor.a > 0) {
+ // windowColor.rgb /= windowColor.a;
+ // }
+
+ // Dissolve and blend the layers.
+ if (ashMap.x - ash < 0) {
+ cogl_color_out.a = 0;
+ }
+ // }
+ // }
}
- if ((uForOpening && fire < 0) || (!uForOpening && fire > 1)) {
- float smoke = (uForOpening ? -fire : fire - 1) * uWidth;
+ // Add fire.
+ if (fireRange.x < noise && noise < fireRange.y) {
+ float fire = 1.0 - (noise - fireRange.x) / (fireRange.y - fireRange.x);
+ fire *= cogl_color_out.a;
+ cogl_color_out = alphaOver(cogl_color_out, getFireColor(fire));
+ }
- vec2 uv = 0.01 * cogl_tex_coord_in[0].st * uSize / uScale;
- vec2 smokeShift = vec2(0, smoke * 0.1);
- smoke = clamp(smoke, 0, 1) * simplex2DFractal(uv + uSeed + smokeShift);
+ // Add smoke.
+ vec2 smokeRange = uForOpening ? vec2(fireRange.y, 1.0) : vec2(0.0, fireRange.x);
+ if (smokeRange.x < noise && noise < smokeRange.y) {
+ float smoke = (noise - smokeRange.x) / (smokeRange.y - smokeRange.x);
+ if (!uForOpening) {
+ smoke = 1 - smoke;
+ }
+
+ vec2 smokeShift = vec2(0, smoke * noise * uTime);
+ smoke *= simplex2DFractal(uv * 0.01 + uSeed + smokeShift);
smoke *= getRelativeEdgeMask(0.3);
- float fadeOutProgress = clamp((uProgress - 0.7) / 0.3, 0, 1);
+ float fadeOutProgress = clamp((uProgress - BURN_TIME) / (1.0 - BURN_TIME), 0, 1);
smoke *= 1 - fadeOutProgress;
cogl_color_out = alphaOver(cogl_color_out, vec4(vec3(0.2), smoke));
}
-
- // if (cogl_tex_coord_in[0].s > 0.5) {
- // cogl_color_out = vec4(vec3(noise), 1.0);
- // }
}
\ No newline at end of file
diff --git a/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml b/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml
index 46ffea2..4aeb5cc 100644
--- a/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml
+++ b/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml
@@ -348,7 +348,7 @@
- 2000
+ 3500
Incinerate Animation Time
The time the Incinerate effect takes.
@@ -366,7 +366,7 @@
- 0.15
+ 0.2
Incinerate Turbulence
The turbulence of the effect.
diff --git a/src/Incinerate.js b/src/Incinerate.js
index fea0ed0..12e2899 100644
--- a/src/Incinerate.js
+++ b/src/Incinerate.js
@@ -37,12 +37,20 @@ var Incinerate = class {
// newly created shader instance.
constructor() {
this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => {
- // We import Clutter in this function as it is not available in the preferences
- // process. This creator function of the ShaderFactory is only called within GNOME
- // Shell's process.
- const Clutter = imports.gi.Clutter;
+ // We import these modules in this function as they are not available in the
+ // preferences process. This callback is only called within GNOME Shell's process.
+ const {Clutter, GdkPixbuf, Cogl} = imports.gi;
+
+ // Create the texture in the first call.
+ if (!this._ashTexture) {
+ const ashData = GdkPixbuf.Pixbuf.new_from_resource('/img/dust.png');
+ this._ashTexture = new Clutter.Image();
+ this._ashTexture.set_data(ashData.get_pixels(), Cogl.PixelFormat.RGB_888,
+ ashData.width, ashData.height, ashData.rowstride);
+ }
// Store uniform locations of newly created shaders.
+ shader._uAshTexture = shader.get_uniform_location('uAshTexture');
shader._uSeed = shader.get_uniform_location('uSeed');
shader._uColor = shader.get_uniform_location('uColor');
shader._uWidth = shader.get_uniform_location('uWidth');
@@ -65,6 +73,23 @@ var Incinerate = class {
shader.set_uniform_float(shader._uTurbulence, 1, [settings.get_double('incinerate-turbulence')]);
// clang-format on
});
+
+ // This is required to bind the ash texture for drawing. Sadly, this seems to be
+ // impossible under GNOME 3.3x as this.get_pipeline() is not available. It was
+ // called get_target() back then but this is not wrapped in GJS.
+ // https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598
+ shader.connect('update-animation', (shader) => {
+ const pipeline = shader.get_pipeline();
+
+ // Use linear filtering for the window texture.
+ pipeline.set_layer_filters(0, Cogl.PipelineFilter.LINEAR,
+ Cogl.PipelineFilter.LINEAR);
+
+ // Bind the ash texture.
+ pipeline.set_layer_texture(1, this._ashTexture.get_texture());
+ pipeline.set_layer_wrap_mode(1, Cogl.PipelineWrapMode.REPEAT);
+ pipeline.set_uniform_1i(shader._uAshTexture, 1);
+ });
});
}