diff --git a/examples/tests/destroy.ts b/examples/tests/destroy.ts index 96afeaa5..cac654c3 100644 --- a/examples/tests/destroy.ts +++ b/examples/tests/destroy.ts @@ -59,7 +59,6 @@ export default async function test({ shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radius', props: { radius: 50, diff --git a/examples/tests/ds-effect-radial-progress.ts b/examples/tests/ds-effect-radial-progress.ts index c4503d00..02bb2e05 100644 --- a/examples/tests/ds-effect-radial-progress.ts +++ b/examples/tests/ds-effect-radial-progress.ts @@ -38,14 +38,12 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radialProgress', props: { progress: 0.6, }, }, { - name: 'e2', type: 'linearGradient', props: { angle: degToRad(90), @@ -66,7 +64,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radialProgress', props: { progress: 1, @@ -74,14 +71,12 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e2', type: 'radialProgress', props: { progress: 0.2, }, }, { - name: 'e3', type: 'linearGradient', props: { angle: degToRad(90), @@ -102,7 +97,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radialProgress', props: { progress: 1, @@ -111,7 +105,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e2', type: 'radialProgress', props: { progress: 0.2, @@ -119,7 +112,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e3', type: 'linearGradient', props: { angle: degToRad(90), @@ -140,7 +132,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radialProgress', props: { progress: 1, @@ -150,7 +141,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e2', type: 'radialProgress', props: { progress: 0.2, @@ -159,7 +149,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e3', type: 'linearGradient', props: { angle: degToRad(90), @@ -180,7 +169,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radialProgress', props: { progress: 1, @@ -191,7 +179,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e2', type: 'radialProgress', props: { progress: 0.2, @@ -201,7 +188,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e3', type: 'linearGradient', props: { angle: degToRad(90), diff --git a/examples/tests/dynamic-shader-circle-border-radius.ts b/examples/tests/dynamic-shader-circle-border-radius.ts index 022bda5f..fd397e1f 100644 --- a/examples/tests/dynamic-shader-circle-border-radius.ts +++ b/examples/tests/dynamic-shader-circle-border-radius.ts @@ -44,7 +44,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radius', props: { radius: 400 / 2, @@ -73,14 +72,12 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radius', props: { radius: 400 / 2, }, }, { - name: 'e2', type: 'border', props: { width: 6, @@ -110,7 +107,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radius', props: { radius: 152 / 2, @@ -139,14 +135,12 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radius', props: { radius: 152 / 2, }, }, { - name: 'e2', type: 'border', props: { width: 6, diff --git a/examples/tests/dynamic-shader.ts b/examples/tests/dynamic-shader.ts index 03943c33..de783259 100644 --- a/examples/tests/dynamic-shader.ts +++ b/examples/tests/dynamic-shader.ts @@ -38,14 +38,12 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radius', props: { radius: 50, }, }, { - name: 'e2', type: 'linearGradient', props: { angle: 0, @@ -56,7 +54,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e3', type: 'border', props: { width: 30, @@ -64,7 +61,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e4', type: 'linearGradient', props: { angle: degToRad(40), @@ -88,7 +84,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'borderTop', props: { width: 30, @@ -96,7 +91,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e2', type: 'borderBottom', props: { width: 30, @@ -116,7 +110,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'linearGradient', props: { angle: degToRad(90), @@ -128,7 +121,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e2', type: 'radius', props: { radius: 50, @@ -148,7 +140,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'borderRight', props: { width: 30, @@ -156,7 +147,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e2', type: 'borderLeft', props: { width: 15, @@ -177,7 +167,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'linearGradient', props: { angle: degToRad(180), @@ -186,7 +175,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e2', type: 'linearGradient', props: { angle: degToRad(-90), @@ -208,14 +196,12 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radius', props: { radius: 100, }, }, { - name: 'e2', type: 'fadeOut', props: { fade: [200, 100, 0, 0], @@ -234,7 +220,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'radialGradient', props: { colors: [0xff0000ff, 0x00ff00ff, 0x00000000], diff --git a/examples/tests/hole-punch-effect.ts b/examples/tests/hole-punch-effect.ts index d619fa8a..b3b0a127 100644 --- a/examples/tests/hole-punch-effect.ts +++ b/examples/tests/hole-punch-effect.ts @@ -40,7 +40,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'holePunch', props: { width: 200, @@ -64,7 +63,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'holePunch', props: { width: 200, @@ -75,7 +73,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e2', type: 'linearGradient', props: { angle: degToRad(40), @@ -99,7 +96,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'linearGradient', props: { angle: degToRad(40), @@ -110,7 +106,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { }, }, { - name: 'e2', type: 'holePunch', props: { width: 400, @@ -134,7 +129,6 @@ export default async function test({ renderer, testRoot }: ExampleSettings) { shader: renderer.createShader('DynamicShader', { effects: [ { - name: 'e1', type: 'holePunch', props: { width: 400, diff --git a/examples/tests/shader-animation.ts b/examples/tests/shader-animation.ts index 05bd437e..8109677f 100644 --- a/examples/tests/shader-animation.ts +++ b/examples/tests/shader-animation.ts @@ -67,13 +67,21 @@ export default async function test({ y: 540, color: 0x00ff00ff, shader: renderer.createDynamicShader([ - renderer.createEffect('r1', 'radius', { - radius: 0, - }), - renderer.createEffect('e1', 'border', { - color: 0xff00ffff, - width: 10, - }), + renderer.createEffect( + 'radius', + { + radius: 0, + }, + 'r1', + ), + renderer.createEffect( + 'border', + { + color: 0xff00ffff, + width: 10, + }, + 'e1', + ), ]), parent: testRoot, }); diff --git a/src/core/CoreShaderManager.ts b/src/core/CoreShaderManager.ts index b1a7f7ee..f9a8ef0c 100644 --- a/src/core/CoreShaderManager.ts +++ b/src/core/CoreShaderManager.ts @@ -230,7 +230,7 @@ export class CoreShaderManager { } loadDynamicShader< - T extends DynamicEffects<[...{ name: string; type: keyof EffectMap }[]]>, + T extends DynamicEffects<[...{ name?: string; type: keyof EffectMap }[]]>, >(props: DynamicShaderProps): DynamicShaderController { if (!this.renderer) { throw new Error(`Renderer is not been defined`); @@ -270,7 +270,7 @@ export class CoreShaderManager { } private _createDynShaderCtr< - T extends DynamicEffects<[...{ name: string; type: keyof EffectMap }[]]>, + T extends DynamicEffects<[...{ name?: string; type: keyof EffectMap }[]]>, >( shader: InstanceType, props: ExtractProps, diff --git a/src/core/renderers/webgl/shaders/DynamicShader.ts b/src/core/renderers/webgl/shaders/DynamicShader.ts index 27963be4..66ef61ca 100644 --- a/src/core/renderers/webgl/shaders/DynamicShader.ts +++ b/src/core/renderers/webgl/shaders/DynamicShader.ts @@ -34,19 +34,19 @@ import type { EffectMap } from '../../../CoreShaderManager.js'; import { assertTruthy } from '../../../../utils.js'; export interface BaseEffectDesc { - name: string; + name?: string; type: keyof EffectMap; // eslint-disable-next-line @typescript-eslint/no-explicit-any props: Record; } export interface EffectDesc< - T extends { name: string; type: keyof EffectMap } = { - name: string; + T extends { name?: string; type: keyof EffectMap } = { + name?: string; type: keyof EffectMap; }, > extends BaseEffectDesc { - name: T['name']; + name?: T['name']; type: T['type']; props: ExtractProps; } diff --git a/src/main-api/DynamicShaderController.ts b/src/main-api/DynamicShaderController.ts index cbd12c54..413bc260 100644 --- a/src/main-api/DynamicShaderController.ts +++ b/src/main-api/DynamicShaderController.ts @@ -8,20 +8,24 @@ import type { ExtractProps } from '../core/CoreTextureManager.js'; import type { EffectDesc } from '../core/renderers/webgl/shaders/DynamicShader.js'; import type { BaseShaderController } from './ShaderController.js'; +type OptionalName = T extends string ? T : never; + type MapEffectProps< - Effects extends [...{ name: string; type: keyof EffectMap }[]], + Effects extends [...{ name?: string; type: keyof EffectMap }[]], > = { - [K in Effects[number] as K['name']]: ExtractProps; + [K in Effects[number] as OptionalName]: ExtractProps< + EffectMap[K['type']] + >; }; export type DynamicEffects< - T extends [...{ name: string; type: keyof EffectMap }[]], + T extends [...{ name?: string; type: keyof EffectMap }[]], > = { [K in keyof T]: EffectDesc; }; export class DynamicShaderController< - Effects extends [...{ name: string; type: keyof EffectMap }[]], + Effects extends [...{ name?: string; type: keyof EffectMap }[]], > implements BaseShaderController { private resolvedProps: ExtractProps; @@ -47,6 +51,9 @@ export class DynamicShaderController< props: effectProps, type: effectType, } = effects[i]!; + if (effectName === undefined) { + continue; + } const definedEffectProps = {}; const propEntries = Object.keys(effectProps); const propEntriesLength = propEntries.length; diff --git a/src/main-api/Renderer.ts b/src/main-api/Renderer.ts index c40ee8d3..68ac2e37 100644 --- a/src/main-api/Renderer.ts +++ b/src/main-api/Renderer.ts @@ -470,18 +470,61 @@ export class RendererMain extends EventEmitter { return this.stage.shManager.loadShader(shaderType, props); } + /** + * Create a new Dynamic Shader controller + * + * @remarks + * A Dynamic Shader is a shader that can be composed of an array of mulitple + * effects. Each effect can be animated or changed after creation (provided + * the effect is given a name). + * + * Example: + * ```ts + * renderer.createNode({ + * shader: renderer.createDynamicShader([ + * renderer.createEffect('radius', { + * radius: 0 + * }, 'effect1'), + * renderer.createEffect('border', { + * color: 0xff00ffff, + * width: 10, + * }, 'effect2'), + * ]), + * }); + * ``` + * + * @param effects + * @returns + */ createDynamicShader< - T extends DynamicEffects<[...{ name: string; type: keyof EffectMap }[]]>, + T extends DynamicEffects<[...{ name?: string; type: keyof EffectMap }[]]>, >(effects: [...T]): DynamicShaderController { return this.stage.shManager.loadDynamicShader({ effects: effects as EffectDescUnion[], }); } - createEffect( - name: Name, + /** + * Create an effect to be used in a Dynamic Shader + * + * @remark + * The {name} parameter is optional but required if you want to animate the effect + * or change the effect's properties after creation. + * + * See {@link createDynamicShader} for an example. + * + * @param type + * @param props + * @param name + * @returns + */ + createEffect< + Type extends keyof EffectMap, + Name extends string | undefined = undefined, + >( type: Type, props: EffectDesc<{ name: Name; type: Type }>['props'], + name?: Name, ): EffectDesc<{ name: Name; type: Type }> { return { name,