From 1262089e82058058a63c2b9ea6d33d68d56a8ba1 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Sat, 14 May 2022 21:09:19 +0200 Subject: [PATCH] :memo: Update tutorial --- docs/how-to-create-new-effects.md | 180 ++++++++++++------------------ 1 file changed, 74 insertions(+), 106 deletions(-) diff --git a/docs/how-to-create-new-effects.md b/docs/how-to-create-new-effects.md index cbd3c2a..35029ce 100644 --- a/docs/how-to-create-new-effects.md +++ b/docs/how-to-create-new-effects.md @@ -77,11 +77,55 @@ Just remember to replace `simple-fade` with your custom name! ### 2. Creating the Effect Class -You will have to create a new file called `src/SimpleFade.js` and paste the following code to it. +You will have to create a new GLSL file called `resources/shaders/simple-fade.glsl` and a new JavaScript source file called `src/SimpleFade.js`. +Simply paste the following source code to the respective file. Please study this code carefully, all of it is explained with inline comments.
- Expand this to show the code. + Expand this to show the GLSL code. + +```glsl +// The code below injects some standard uniforms which will be updated during the +// animation. This includes: +// bool uForOpening: True if a window-open animation is ongoing, false otherwise. +// sampler2D uTexture: Contains the texture of the window. +// float uProgress: A value which transitions from 0 to 1 during the entire animation. +// float uTime: A steadily increasing value in seconds. +// vec2 uSize: The size of uTexture in pixels. +#include "common/uniforms.glsl" + +// The width of the fading effect is loaded from the settings. +uniform float uFadeWidth; + +void main() { + // Get the color from the window texture. + cogl_color_out = texture2D(uTexture, cogl_tex_coord_in[0].st); + + // Shell.GLSLEffect uses straight alpha. So we have to convert from premultiplied. + if (cogl_color_out.a > 0) { + cogl_color_out.rgb /= cogl_color_out.a; + } + + // Radial distance from window edge to the window's center. + float dist = length(cogl_tex_coord_in[0].st - 0.5) * 2.0 / sqrt(2.0); + + // This gradually dissolves from [1..0] from the outside to the center. We + // switch the direction for opening and closing. + float progress = uForOpening ? 1.0 - uProgress : uProgress; + float mask = (1.0 - progress * (1.0 + uFadeWidth) - dist + uFadeWidth) / uFadeWidth; + + // Make the mask smoother. + mask = smoothstep(0, 1, mask); + + // Apply the mask to the output. + cogl_color_out.a *= mask; +} +``` + +
+ +
+ Expand this to show the JavaScript code. ```javascript ////////////////////////////////////////////////////////////////////////////////////////// @@ -106,25 +150,41 @@ const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // This effect ... // // <- Please add a description of your effect here -> // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// The effect class is completely static. It can be used to get some metadata (like the -// effect's name or supported GNOME Shell versions), to initialize the respective page of -// the settings dialog, as well as to create the actual shader for the effect. +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var SimpleFade = class SimpleFade { + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // 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; + + // Store uniform locations of newly created shaders. + shader._uForOpening = shader.get_uniform_location('uForOpening'); + shader._uFadeWidth = shader.get_uniform_location('uFadeWidth'); + + // Write all uniform values at the start of each animation. + shader.connect('begin-animation', (shader, settings) => { + shader.set_uniform_float(shader._uForOpening, 1, [forOpening]); + shader.set_uniform_float(shader._uFadeWidth, 1, [settings.get_double('simple-fade-width')]); + }); + }); + } + // ---------------------------------------------------------------------------- metadata // The effect is available on all GNOME Shell versions supported by this extension. @@ -158,23 +218,6 @@ var SimpleFade = class SimpleFade { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.updateAnimation(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. @@ -182,81 +225,6 @@ var SimpleFade = class SimpleFade { return {x: 1.0, y: 1.0}; } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const Shell = imports.gi.Shell; - const shaderSnippets = Me.imports.src.shaderSnippets; - - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - _init(settings, forOpening) { - super._init({shader_type: Clutter.ShaderType.FRAGMENT_SHADER}); - - this._uForOpening = this.get_uniform_location('uForOpening'); - this._uFadeWidth = this.get_uniform_location('uFadeWidth'); - } - - // This is called each time the shader is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - updateAnimation(actor, settings, forOpening) { - this.set_uniform_float(this._uForOpening, 1, [forOpening]); - this.set_uniform_float(this._uFadeWidth, 1, [settings.get_double('simple-fade-width')]); - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. - vfunc_build_pipeline() { - - const declarations = ` - // The code below injects some standard uniforms which will be updated during the - // animation. This includes: - // bool uForOpening: True if a window-open animation is ongoing, false otherwise. - // sampler2D uTexture: Contains the texture of the window. - // float uProgress: A value which transitions from 0 to 1 during the entire animation. - // float uTime: A steadily increasing value in seconds. - // vec2 uSize: The size of uTexture in pixels. - #include "common/uniforms.glsl" - - // The width of the fading effect is loaded from the settings. - uniform float uFadeWidth; - `; - - const code = ` - // Get the color from the window texture. - vec4 windowColor = texture2D(uTexture, cogl_tex_coord_in[0].st); - - // Radial distance from window edge to the window's center. - float dist = length(cogl_tex_coord_in[0].st - 0.5) * 2.0 / sqrt(2.0); - - // This gradually dissolves from [1..0] from the outside to the center. We - // switch the direction for opening and closing. - float progress = uForOpening ? 1.0 - uProgress : uProgress; - float mask = (1.0 - progress * (1.0 + uFadeWidth) - dist + uFadeWidth) / uFadeWidth; - - // Make the mask smoother. - mask = smoothstep(0, 1, mask); - - // Set the final output color. This uses premultiplied alpha. - cogl_color_out = windowColor * mask; - `; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, code, true); - }; - }); -} ```
@@ -270,7 +238,7 @@ Like this: ```javascript const ALL_EFFECTS = [ ... - Me.imports.src.SimpleFade.SimpleFade, + new Me.imports.src.SimpleFade.SimpleFade(), ... ]; ```