diff --git a/packages/shader/README.md b/packages/shader/README.md index 851cc7814..42fc5a860 100644 --- a/packages/shader/README.md +++ b/packages/shader/README.md @@ -1,3 +1,16 @@ # @strudel/shader -Helpers for drawing shader +Helpers for drawing shader. + +## Todos + +Here are the things that needs to be implemented: + +- [ ] Shader source error reporting +- [ ] Shader import from url, like shadertoy or git +- [ ] Display attribution +- [ ] Compilation error reporting, e.g. to show the line number +- [ ] Multiple instance and custom canvas position +- [ ] Multiple program, to be swapped like a pattern +- [ ] Texture inputs +- [ ] Buffer inputs, e.g. to generate a texture diff --git a/packages/shader/index.mjs b/packages/shader/index.mjs index 41aa0e1de..931421e83 100644 --- a/packages/shader/index.mjs +++ b/packages/shader/index.mjs @@ -1,2 +1,2 @@ -export {loadShader} from './shader.mjs'; +export * from './shader.mjs'; export * from './uniform.mjs'; diff --git a/packages/shader/shader.mjs b/packages/shader/shader.mjs index e81a0cf50..8d9f0430c 100644 --- a/packages/shader/shader.mjs +++ b/packages/shader/shader.mjs @@ -59,7 +59,7 @@ const decayModulation = (decay) => { // Set an uniform value (from a pattern). export function setUniform(instanceName, name, value, position) { - const instance = _instances[instanceName || 'default']; + const instance = _instances[instanceName]; if (!instance) { logger('[shader] not loaded yet', 'warning'); return; @@ -104,7 +104,8 @@ function setupUniforms(uniforms, program) { const uname = name.replace('[0]', ''); const count = uniform.count | 0; if (!uniforms[uname] || uniforms[uname].count != count) { - // TODO: keep the previous value when the count change... + // TODO: keep the previous values when the count change: + // if the count decreased, then drop the excess, else append new values uniforms[uname] = { name, count, @@ -174,6 +175,7 @@ async function initializeShaderInstance(name, code) { updateUniforms(instance.drawFrame, elapsed, instance.uniforms); instance.drawFrame.draw(); + // After sometime, if no update happened, stop the animation loop if (instance.age++ < 100) requestAnimationFrame(instance.update); else instance.drawing = false; }; diff --git a/packages/shader/uniform.mjs b/packages/shader/uniform.mjs index e1ae7f732..100b83b01 100644 --- a/packages/shader/uniform.mjs +++ b/packages/shader/uniform.mjs @@ -7,27 +7,75 @@ This program is free software: you can redistribute it and/or modify it under th import { register, logger } from '@strudel/core'; import { setUniform } from './shader.mjs'; -export const uniform = register('uniform', (options, pat) => { - // Keep track of the pitches value: Map String Int - const pitches = { _count: 0 }; +/** + * Update a shader. The destination name consist of + * + * - the uniform name + * - optional array mapping, either a number or an assignment mode ('seq' or 'pitch') + * + * @name uniform + * @example + * s("bd").uniform("iColor") + * @example + * s("bd").uniform("iColors:seq") + * @example + * note("c3 e3").uniform("iMorph:pitch") + */ +function parseUniformTarget(name) { + if (typeof name === 'string') + return {name, mapping: 'single', position: 0} + else if (name.length == 2) { + const mapping = typeof name[1] === 'string' ? name[1] : 'single' + const position = typeof name[1] === 'string' ? null : name[1] + return { + name: name[0], + mapping, + position + } + } +} + +// Keep track of the pitches value per uniform +let _pitches = {} +export const uniform = register('uniform', (target, pat) => { + // TODO: support multiple shader instance + const instance = "default" + + // Decode the uniform defintion + const uniformTarget = parseUniformTarget(target) + + // Get the pitches + if (!_pitches[uniformTarget.name]) + _pitches[uniformTarget.name] = {_count: 0} + const pitches = _pitches[uniformTarget.name] return pat.onTrigger((time_deprecate, hap, currentTime, cps, targetTime) => { - const instance = options.instance; + // TODO: figure out how to get the desired value, e.g. is this pattern for pan, gain, velocity, ... + const value = hap.value ? (hap.value.gain || 1.0) : 1.0; - const value = options.gain || 1.0; - if (options.pitch !== undefined) { + // Get the uniform mapping position + let position = null + if (uniformTarget.mapping == 'pitch') { + // Assign one position per pitch const note = hap.value.note || hap.value.s; if (pitches[note] === undefined) { // Assign new value, the first note gets 0, then 1, then 2, ... pitches[note] = Object.keys(pitches).length; } - setUniform(instance, options.pitch, value, pitches[note]); - } else if (options.seq !== undefined) { - setUniform(instance, options.seq, value, pitches._count++); - } else if (options.uniform !== undefined) { - setUniform(instance, options.uniform, value); + position = pitches[note] + } else if (uniformTarget.mapping == 'seq') { + console.log("HERE", pitches) + // Assign a new position per event + position = pitches._count++ + } else if (uniformTarget.mapping == 'single') { + // Assign a fixed position + position = uniformTarget.position } else { - console.error('Unknown shader options, need either pitch or uniform', options); + console.error('Unknown uniform target', uniformTarget) } + + // Update the uniform + if (position !== null) + setUniform(instance, uniformTarget.name, value, position); }, false); });