Shader assets: graph-built shaders, the Shader tab and sprite rows from the graph (#62)
game-maker / suite (push) Failing after 3m59s

A shader is an asset carrying a node graph (Texture color, Output, Property,
Color, Add/Subtract/Multiply/Divide, Mix, Luminance) that compiles to the GLSL
fragment of a Pixi filter. The Shader tab reuses the Behavior canvas; a sprite
wears a shader through its Shader picker, and the rows below it are the
graph's Property nodes. Property schema, resolution and deviation take the
project because a sprite's rows now live in another asset. The built-in
registry of the prototype is gone; "Adjust colors" ships as a graph in the
fixture and the three example games.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Fable 5.1 committed 2026-09-12 22:41:14 +02:00
1 parent 6141d07d4f
commit 42cafa7543
62 files changed
+1682 -144

No files matched your search

+13 -3
View File
@@ -91,6 +91,15 @@ export interface StateMachineAsset extends AssetBase {
graph: Graph;
}
/**
* The graph is GLSL in nodes: `Property` nodes declare the sprite rows, the `Output` node ends
* the fragment (see `core/shader/compile.ts`). A sprite wears it through its `shader` property.
*/
export interface ShaderAsset extends AssetBase {
type: 'shader';
graph: Graph;
}
export type Asset =
| SpriteAsset
| CameraAsset
@@ -101,13 +110,14 @@ export type Asset =
| ContainerAsset
| TextureAsset
| AnimationAsset
| StateMachineAsset;
| StateMachineAsset
| ShaderAsset;
/** The assets edited as a node graph – what the graph canvas and the graph commands work on. */
export type GraphAsset = BehaviorAsset | StateMachineAsset;
export type GraphAsset = BehaviorAsset | StateMachineAsset | ShaderAsset;
export function isGraphAsset(asset: Asset): asset is GraphAsset {
return asset.type === 'behavior' || asset.type === 'statemachine';
return asset.type === 'behavior' || asset.type === 'statemachine' || asset.type === 'shader';
}
export function isSceneAsset(asset: Asset): asset is SceneAsset {
+17 -4
View File
@@ -9,6 +9,7 @@ export const ASSET_TYPES = [
'texture',
'animation',
'statemachine',
'shader',
] as const;
export type AssetType = (typeof ASSET_TYPES)[number];
@@ -24,16 +25,28 @@ export const ASSET_TYPE_LABELS: Record<AssetType, string> = {
texture: 'Texture',
animation: 'Animation',
statemachine: 'State machine',
shader: 'Shader',
};
/**
* Textures and animations are not placeable: they are pictures a sprite wears through `texture`.
* A state machine is not either: a behavior refers to it by id.
* Textures and animations are not placeable: they are pictures a sprite wears through `texture`,
* as a shader is worn through `shader`. A state machine is not either: a behavior refers to it by id.
*/
export type PlaceableAssetType = Exclude<AssetType, 'scene' | 'texture' | 'animation' | 'statemachine'>;
export type PlaceableAssetType = Exclude<
AssetType,
'scene' | 'texture' | 'animation' | 'statemachine' | 'shader'
>;
const NOT_PLACEABLE: ReadonlySet<AssetType> = new Set([
'scene',
'texture',
'animation',
'statemachine',
'shader',
]);
export function isPlaceableType(type: AssetType): type is PlaceableAssetType {
return type !== 'scene' && type !== 'texture' && type !== 'animation' && type !== 'statemachine';
return !NOT_PLACEABLE.has(type);
}
export function isAssetType(value: unknown): value is AssetType {
+1 -1
View File
@@ -12,7 +12,7 @@ export function graphLookup(project: Project): GraphLookup {
behavior: (assetId) => {
const asset = findAssetOf(project, assetId, 'behavior');
if (!asset) return null;
return { name: asset.name, graph: asset.graph, defaults: resolveAssetProps(asset) };
return { name: asset.name, graph: asset.graph, defaults: resolveAssetProps(project, asset) };
},
machine: (assetId) => {
const asset = findAssetOf(project, assetId, 'statemachine');
+1 -1
View File
@@ -48,7 +48,7 @@ export function findPropertyUsages(project: Project, ids: string | readonly stri
const refKeys = new Map<string, string[]>();
const matches = (value: unknown): boolean => typeof value === 'string' && wanted.has(value);
for (const asset of project.assets) {
const keys = schemaForAsset(asset)
const keys = schemaForAsset(project, asset)
.filter((def) => isKindWith('reference', def.kind))
.map((def) => def.key);
refKeys.set(asset.id, keys);