diff --git a/Concepts.md b/Concepts.md index ea20a22..8c982ae 100644 --- a/Concepts.md +++ b/Concepts.md @@ -1,12 +1,12 @@ | Term | Meaning | |---|---| | **Account** | Who you are signed in as. The editor asks for a user name and a password before it loads anything, and every project belongs to one account — see [Accounts](Accounts). The **Account** menu is at the far right of the toolbar and holds the name, *Change password* and *Logout*; signing in and out changes what the canvas draws, it never reloads the page. | -| **Asset** | Named thing with a type (`scene`, `camera`, `clip`, `sprite`, `text`, `behavior`, `collider`, `container`, `texture`, `animation`, `statemachine`, `shader`, `font`, `sound`, `particles`, `variables`) and typed properties. Assets have **no position**. | +| **Asset** | Named thing with a type (`scene`, `camera`, `clip`, `sprite`, `text`, `behavior`, `collider`, `container`, `texture`, `animation`, `statemachine`, `shader`, `shaderfunction`, `font`, `sound`, `particles`, `variables`) and typed properties. Assets have **no position**. | | **Texture** | An uploaded image. Scenes, textures, animations, state machines, shaders, fonts, sounds and Variables assets are the eight types that cannot be placed: a texture has no position, it is *worn* by a sprite through the ordinary property `texture`. See [Textures](Textures). | | **Font** | An uploaded typeface, worn by a text through the ordinary property `font`; no font means the editor's own. Uploaded and picked exactly like a texture, and stored the same way. See [Fonts](Fonts). | | **Sound** | An uploaded audio file. Uploaded, picked and stored exactly like a typeface — but nothing *wears* it: it is neither on screen nor in the scene, and a graph plays it with `Play sound`. Its own rows say how loud it is and whether it loops; the node scales the one and can overrule the other either way. See [Sounds](Sounds). | | **State machine** | A named set of **states**, edited as nodes in its own tab. A behavior puts it into a state with `Set state`, moves it along a named transition with `State transition` and branches on it with `State branch`. Its **Scope** says where the current state lives: **Local** gives every behavior instance one of its own, **Global** keeps a single one for the whole run. See [State machines](State-machines). | -| **Shader** | A node graph that becomes the GLSL a sprite is drawn with, edited in its own tab. A sprite, a text or a particle source wears it through `shader`; the numbers the graph declares are rows below that dropdown. See [Shaders](Shaders). | +| **Shader** | A node graph that becomes the GLSL a sprite is drawn with, edited in its own tab. A sprite, a text or a particle source wears it through `shader`; the numbers the graph declares are rows below that dropdown. A **shader function** is a part of such a graph kept on its own and used as a node in other shaders. See [Shaders](Shaders). | | **Particles** | A node graph that says what *one* particle is as a function of its age and of a random number drawn once for it — no forces, no simulation. Unlike the other graph assets it **is** placeable: the object made from it is a source, whose rows say how many go out and for how long they live. Because a particle is a formula, the same second always draws the same picture and the tab's slider can jump to any of them exactly. See [Particles](Particles). | | **Text** | An asset that draws a line of text (`text`, `font`, `fontSize`, `color`, `align`) — titles, HUD readouts, button labels. Its content is an ordinary property, so a `Properties` node of type Text with `text` switched to Set changes it while playing. | | **Enabled** | Every placeable asset has the switch `enabled` (default on). A disabled object is not drawn, does not collide and its behavior does not run — **and neither does anything parented below it**. Switching one instance off is a normal override, which makes a container a *screen*: switch it on to show it, off to hide it. From a graph, `Properties` with `enabled` switched to Set does it, and `Is enabled` asks (including the parents); a behavior hears the switch as `On enable` / `On disable`. | diff --git a/Development.md b/Development.md index 2a504d2..cd838c4 100644 --- a/Development.md +++ b/Development.md @@ -18,7 +18,7 @@ php tests/php/accounts.test.php # the API's accoun # has and the web server's container has not (SKIP there) php tests/php/snapshots.test.php # the snapshot store (#231), the same way php tests/php/releases.test.php # the release store (#266), the same way -node tools/seed-e2e.mjs && node tools/e2e.mjs # 1725 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1728 editor checks, screenshots in /tmp/gm-e2e-*.png GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step ./e2e.sh start [GM_STEPS] # the same run detached, see below ./e2e.sh status # live progress, or the last run's result diff --git a/Shaders.md b/Shaders.md index 5f952b7..4a8f32a 100644 --- a/Shaders.md +++ b/Shaders.md @@ -96,6 +96,9 @@ plugged into a colour port, straight from a `Number` or a `Luminance` as well. | **Pow** | in `value` (any), `exponent` (number) · out `result` | `value` to the power of `exponent`: 2 squares it, 0.5 is the square root; an easing curve on `u`, a gamma on a colour. Below 0 the value counts as 0, because GLSL leaves `pow` undefined there (0 to a power of 0 or less is undefined too). Per channel for colours. | | **Sqrt** | in `value` (any) · out `result` | The square root: 0.25 becomes 0.5, 4 becomes 2. The same as `Pow` with exponent 0.5, in one node. Below 0 the value counts as 0, because GLSL leaves `sqrt` undefined there. Per channel for colours. | | **Clamp** | in `value` (any), `min`, `max` (number) · out `result` | Holds the value inside `min`..`max`, per channel for colours. The Output clamps the colour it is given anyway; this one holds a value *inside* the graph, before it is added to or multiplied. | +| **Function input** | out: what you declare | Only in a shader function: the values a shader calling the function hands in. Every input declared in the inspector (`+ Input`, a number or a colour) is an output here and an input on every call. One per function. See *Shader functions*. | +| **Function output** | in: what you declare | Only in a shader function: what it hands back. Every output declared here is an output on every call; unconnected, it is the value typed on it. One per function. | +| *a shader function* | its declared inputs · its declared outputs | Listed in the palette under the function's name: the function's graph, set in here. A double-click opens it. | An input with nothing to type on it (the Output's two, Luminance's colour) that is left unconnected reads the texel: its colour for a colour port, its alpha for the Output's `alpha`. That is why the @@ -294,6 +297,47 @@ Pass and blurred down, is the same 5x5 spot of 1/25 each that a `Blur` *Both* dr 231/60/60, red once halved in each; two Passes in a row, the first the very program of an earlier graph's only Pass, still show the picture and not white. +## Shader functions + +A **shader function** (#303) is a part of a shader graph kept as an asset of its own, so the same +calculation (a greyscale, a noise, a vignette, an outline of eight samples) is built once and used in +any number of shaders. `+` in the Assets panel → **Shader function** makes one; it opens in the +Shader tab like a shader. It has no Output, no wearer and no preview of its own: the panel beside +the graph says *A function shows in the shaders calling it*, and the readout counts its inputs and +outputs. + +- **Its ports** are declared on its two ends: **Function input** says what a caller hands in, + **Function output** what comes back. Select one and add ports in the inspector, each a *Number* or + a *Color*; nothing else can be declared. Every port is at once a port of every node calling the + function, in every shader: a new input grows those nodes, a renamed one keeps their edges, a removed + one takes its edges along, and an undo takes all of it back. +- **Calling it:** every shader function is in the Shader tab's palette under its own name, and can be + dragged in from the Assets panel. The node carries the function's name; an input left unconnected is + the value typed on it. A double-click on the node opens the function. +- **Set in, not called.** The compiler copies the function's graph into the shader where the node + stands, as if its nodes had been placed there, so it costs exactly what the same nodes cost in place. + Calling a function twice copies it twice. The nodes that read the picture (`Texture color`, + `Sample texture`, `Blur`, ...) read the picture of whatever wears the calling shader, and `Time` is the + same clock. A Pass inside a function is a pass of the calling shader (a wide blur can be a function). +- **What it may hold:** everything a shader holds except the Output, the Vertex output, `Property` and + `Color property`. Values come in through the inputs; the calling shader hangs its own properties on + them, so it stays clear whose row a property is. Those nodes are not in a function's palette, and one + pasted or left in says *A function cannot hold ...*. The two ends are not in a shader's palette. +- **Changes reach every caller.** Every shader calling a function compiles again when the function + changes, however deep the call sits. +- **Problems** inside a function show on its own nodes in its tab, and at every call, in the + function's words: *In Grayscale: "level" needs a number, not a color*. A function calling itself, + directly or through another, says *A function cannot call itself* and is not set in; the palette + does not offer such a call in the first place. + +A function is part of the shader graph only: a behavior cannot call it, since a behavior runs on the +processor and a shader on the graphics card. + +Measured in the checks (step 28): a function "Grayscale" (a colour in, `Luminance` out as a colour), +called between `Texture color` and the Output, draws the same pixels as the same nodes in place, byte +for byte; a `Multiply` by 0.5 put into the function halves the sprite's grey without the shader being +touched; a double-click on the call opens the function, and a call of itself inside it is marked. + ## Moving the corners Everything above decides what colour a pixel gets. A **Vertex output** node decides where the @@ -571,8 +615,19 @@ transform from world units to that picture. The Game tab and the game link then off the stage and let the picture stand in for it; the Scene tab leaves it drawn, since it shows more than the frame, and lays the picture over it below the symbols (`shadingCamera` and `keepsWorldAroundShader` in `WorldViewport`). +A shader function (`src/core/shader/functions.ts`) never reaches the walk as a node: +`inlineFunctions` first replaces every call by a copy of the function's graph, its node ids prefixed +with the call's (`/`), wires what fed the call's inputs to what the Function input fed, +and what read its outputs to what fed the Function output; an unconnected port becomes a `Number` or +`Color` node with the typed value. Everything after that (passes, Time, the Edge cut, the corner +walk) sees a plain graph. A problem on a copied node is moved back onto the call +(`problemsAtCalls`). The function's own tab walks it from its Function output with any value at its +input (`checkShaderFunction`). The call's ports come from the function through `GraphLookup` +(`shaderFunction`), and the port commands mirror a change at a function's end into every call, as +they do for a behavior's Start and End. `src/viewport/ShaderLibrary.ts` caches the compiled graph per asset, keyed by a fingerprint of what -the fragment depends on — node ids, types, params, declared ports, and which port feeds which — so +the fragment depends on — node ids, types, params, declared ports, and which port feeds which, and +the same of every function it calls (`functionsFingerprint`) — so moving, collapsing or folding compiles nothing again; a changed param or link does, once (`__gm.shaderCompiles()` counts it for the checks). A sprite's picture is a `Mesh` (a unit quad sized by its scale, `symbols.ts`), a sibling of the sprite's `Graphics` node under the