Let a shader declare a color the sprite sets (#298)
A shader could only ask its sprites for numbers. A tint, an outline colour or a glow colour had to be built from three Property nodes. - New node Color property next to Property: Name and Default (a colour, white unless picked), output `color`. It becomes a colour row below the Shader dropdown and a vec3 uniform in the program. - Number and colour properties share one set of names. A clash is the existing "Another property is named" problem. - Like Color it cannot feed the Vertex output. - Placed from the palette it is called tint, then tint2. - The preview in the Shader tab shows a colour field for it. Like the sliders it starts at the sprite's value and never writes into the project. - `declaredRows` takes the row builder, so the particle graph keeps its number rows unchanged. Tests: - Gate: 222 files, 3452 tests (3445 + 7) - e2e 28: 89 checks (28l-v: default colour drawn, then the colour the sprite sets) - e2e total 1704, counted (1703 + 1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4f4c149211
commit
bf27e42e8a
13 files changed
+303
-57
No files matched your search
@@ -4,7 +4,7 @@ import { flatDef, type FlatDef, type PropertyDef } from './PropertySchema';
|
||||
|
||||
/** The params of a `Property` node in a shader or particle graph: its name and the row it declares. */
|
||||
export const PROPERTY_NAME_PARAM = 'name';
|
||||
const PROPERTY_DEFAULT_PARAM = 'default';
|
||||
export const PROPERTY_DEFAULT_PARAM = 'default';
|
||||
const PROPERTY_MIN_PARAM = 'min';
|
||||
const PROPERTY_MAX_PARAM = 'max';
|
||||
|
||||
@@ -25,23 +25,30 @@ export interface Declaration {
|
||||
}
|
||||
|
||||
/**
|
||||
* The number rows a graph declares, in node order: one per declaring node with a valid name. A
|
||||
* second node of the same name declares nothing new – both read the one value.
|
||||
* The rows a graph declares, in node order: one per declaring node with a valid name. A second node
|
||||
* of the same name declares nothing new – both read the one value.
|
||||
*/
|
||||
export function declaredNumberRows(graph: Graph, of: Declaration): FlatDef<'number'>[] {
|
||||
const rows: FlatDef<'number'>[] = [];
|
||||
export function declaredRows<R extends FlatDef>(
|
||||
graph: Graph,
|
||||
of: Declaration & { rowOf(name: string, node: GraphNode): R },
|
||||
): R[] {
|
||||
const rows: R[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const node of graph.nodes) {
|
||||
if (!of.isDeclaration(node)) continue;
|
||||
const name = of.nameOf(node);
|
||||
if (!of.isValidName(name) || seen.has(name)) continue;
|
||||
seen.add(name);
|
||||
rows.push(numberRow(name, node.params));
|
||||
rows.push(of.rowOf(name, node));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
function numberRow(name: string, params: PropertyBag): FlatDef<'number'> {
|
||||
export function declaredNumberRows(graph: Graph, of: Declaration): FlatDef<'number'>[] {
|
||||
return declaredRows(graph, { ...of, rowOf: (name, node) => numberRow(name, node.params) });
|
||||
}
|
||||
|
||||
export function numberRow(name: string, params: PropertyBag): FlatDef<'number'> {
|
||||
const min = numberParam(params, PROPERTY_MIN_PARAM, 0);
|
||||
const max = numberParam(params, PROPERTY_MAX_PARAM, 1);
|
||||
const row: FlatDef<'number'> = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Graph, GraphNode } from '../graph/Graph';
|
||||
import { type PropertyBag, textOf } from '../model/Properties';
|
||||
import { declaredNumberRows, PROPERTY_NAME_PARAM } from './declaredRows';
|
||||
import { type FlatDef, isSpriteProp, isValidFieldName } from './PropertySchema';
|
||||
import { colorOf, type PropertyBag, textOf } from '../model/Properties';
|
||||
import { declaredRows, numberRow, PROPERTY_DEFAULT_PARAM, PROPERTY_NAME_PARAM } from './declaredRows';
|
||||
import { type FlatDef, flatDef, isSpriteProp, isValidFieldName } from './PropertySchema';
|
||||
|
||||
export { PROPERTY_NAME_PARAM } from './declaredRows';
|
||||
|
||||
@@ -13,13 +13,19 @@ export const SHADER_KEY = 'shader';
|
||||
* GLSL uniform, `default`/`min`/`max` are the row the inspector shows.
|
||||
*/
|
||||
export const SHADER_PROPERTY = 'ShaderProperty';
|
||||
/** The same for a colour (#298): a colour row on the sprite and a vec3 uniform. */
|
||||
export const SHADER_COLOR_PROPERTY = 'ShaderColorProperty';
|
||||
export const DEFAULT_PROPERTY_COLOR = 0xffffff;
|
||||
|
||||
/** A row a shader graph adds to its wearers. */
|
||||
export type ShaderParamDef = FlatDef<'number'> | FlatDef<'color'>;
|
||||
|
||||
export function shaderOf(bag: PropertyBag): string {
|
||||
return textOf(bag[SHADER_KEY]);
|
||||
}
|
||||
|
||||
export function isPropertyNode(node: GraphNode): boolean {
|
||||
return node.type === SHADER_PROPERTY;
|
||||
return node.type === SHADER_PROPERTY || node.type === SHADER_COLOR_PROPERTY;
|
||||
}
|
||||
|
||||
export function propertyName(node: GraphNode): string {
|
||||
@@ -36,17 +42,25 @@ function isValidPropertyName(name: string): boolean {
|
||||
}
|
||||
|
||||
/** The properties a shader graph declares: one row on the sprite and one uniform each. */
|
||||
export function shaderParamDefs(graph: Graph): FlatDef<'number'>[] {
|
||||
return declaredNumberRows(graph, {
|
||||
export function shaderParamDefs(graph: Graph): ShaderParamDef[] {
|
||||
return declaredRows<ShaderParamDef>(graph, {
|
||||
isDeclaration: isPropertyNode,
|
||||
nameOf: propertyName,
|
||||
isValidName: isValidPropertyName,
|
||||
rowOf: (name, node) =>
|
||||
node.type === SHADER_COLOR_PROPERTY
|
||||
? flatDef(name, 'color', { value: propertyColor(node.params) })
|
||||
: numberRow(name, node.params),
|
||||
});
|
||||
}
|
||||
|
||||
export function propertyColor(params: PropertyBag): number {
|
||||
return colorOf(params[PROPERTY_DEFAULT_PARAM] ?? DEFAULT_PROPERTY_COLOR, DEFAULT_PROPERTY_COLOR);
|
||||
}
|
||||
|
||||
/** What a property placed from the palette is called: `value`, then `value2`, `value3` … */
|
||||
export function nextPropertyName(graph: Graph): string {
|
||||
export function nextPropertyName(graph: Graph, base = 'value'): string {
|
||||
const taken = new Set(graph.nodes.filter(isPropertyNode).map(propertyName));
|
||||
if (!taken.has('value')) return 'value';
|
||||
for (let n = 2; ; n++) if (!taken.has(`value${n}`)) return `value${n}`;
|
||||
if (!taken.has(base)) return base;
|
||||
for (let n = 2; ; n++) if (!taken.has(`${base}${n}`)) return `${base}${n}`;
|
||||
}
|
||||
Reference in new issue
Block a user