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); + }); }); }