aboutsummaryrefslogtreecommitdiffstats
path: root/alv-lib
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2025-04-05 17:48:08 +0000
committers-ol <s+removethis@s-ol.nu>2025-04-07 10:33:17 +0000
commit6d05263540b4a50f8d2ccb70b69ed6760e2ab691 (patch)
tree36c130068e18712181a6d7db095ed8092d12b694 /alv-lib
parentpropagate dynamic scope in require/import (diff)
downloadalive-6d05263540b4a50f8d2ccb70b69ed6760e2ab691.tar.gz
alive-6d05263540b4a50f8d2ccb70b69ed6760e2ab691.zip
template strings as syntax sugar for Ops
Diffstat (limited to 'alv-lib')
-rw-r--r--alv-lib/glsl-view.moon132
1 files changed, 75 insertions, 57 deletions
diff --git a/alv-lib/glsl-view.moon b/alv-lib/glsl-view.moon
index c2d57ee..e0b8678 100644
--- a/alv-lib/glsl-view.moon
+++ b/alv-lib/glsl-view.moon
@@ -1,4 +1,4 @@
-import Constant, Op, TemplateString, Input, T, Array, Struct, sig, const from require 'alv.base'
+import Constant, PureOp, Op, template_subst, Input, T, Array, Struct, sig, const from require 'alv.base'
import Message, Bundle, Timetag, add_item from require 'alv-lib._osc'
import dns, udp from require 'socket'
@@ -107,86 +107,103 @@ tsv_input = Constant.meta
with @inputs.socket!
\send Message.pack { address: "/texture/#{name}/tsv", types: "ss", "TEXTURE_#{type}", name }
-shader = Constant.meta
+shader_ = Constant.meta
meta:
name: 'shader'
- summary: "control a GLSL shader via OSC."
- examples: { '(shader [socket] code-tpl-str)' }
+ summary: "compile a GLSL shader with uniforms."
+ examples: { '#shader"code…"' }
description: "Syncs code and parameters with glsl-view via OSC.
-- `socket` must be an `osc/out~` or read from `*oscout*`
-- `code-tpl-str` is a `str=` that can contain `~-stream` template substitutions
- that are mapped as follows:
+- `code…` is a template string that can contain `~-stream` template substitutions that are used as follows:
- `str`: substituted directly as GLSL
- `{type: str val: …}`: reference to uniform of type `type` (instead of infered as follows)
- `num`, `bool`: reference to `uniform float/bool`
- `num[I]`, `bool[I]`: reference to `uniform vecI/bvecI`
- `num[I][J]`: reference to `uniform matIxJ`"
- value: class extends Op
- pattern = -sig['osc/out'] + const.str + sig!^0
+ value: class extends PureOp
+ pattern: const! + sig!^0
- default_sender = (input) -> input\type!, input!
- get_code: =>
- uniforms = { '#version 330 core' }
+ name: (i) => "alv_u_#{i}"
- @state = {}
- code = TemplateString.subst @inputs.code!, (i) ->
- param = @inputs.params[i]
- type = param\type!
- return param! if type == T.str
+ type: (args) =>
+ uniforms = {}
+ for i, val in ipairs args[2]
+ type = val\type!
+ if type == T.str
+ continue
+ elseif type.__class == Struct
+ assert (type\get 'type') == T.str, "struct substituton needs type key"
+ type = assert (type\get 'val'), "struct substituton needs val key"
- id = tostring math.random!
- name = "alv_param_#{id\sub 3}"
+ uniforms[@name i] = type
- typestr = GLSL_TYPES[tostring type]
+ uniforms = Struct uniforms
+ Struct shader: T.str, :uniforms
- -- NOTE: can't close over @ or input or breaks when forked
- if type.__class == Struct
- assert type.types.type == T.str, "struct substituton needs type key"
- val_type = assert type.types.val, "struct substituton needs val key"
+ tick: =>
+ uniforms = {}
+
+ preamble = { '#version 330 core' }
+ pieces = for i, input in ipairs @inputs[2]
+ name, input = (@name i), input
+ val, type = input!, input\type!
- typestr = param!.type
- @state[i] = => "/uniform/#{name}", val_type, @inputs.params[i]!.val
+ typestr = GLSL_TYPES[tostring type]
+ if type == T.str
+ val
else
+ if type.__class == Struct
+ typestr = val.type
+ val, type = val.val, val.type
+
assert typestr, "unsupported type #{type}"
- @state[i] = => "/uniform/#{name}", type, @inputs.params[i]!
+ table.insert preamble, "uniform #{typestr} #{name};"
+ uniforms[name] = val
+ name
- table.insert uniforms, "uniform #{typestr} #{name};"
+ shader = template_subst @inputs[1]!, pieces
- name
+ table.insert preamble, shader
+ shader = table.concat preamble, '\n'
- table.insert uniforms, code
- table.concat uniforms, '\n'
+ @out\set :shader, :uniforms
- setup: (inputs, scope) =>
- { socket, code, params } = pattern\match inputs
+draw = Constant.meta
+ meta:
+ name: 'draw'
+ summary: "draw a GLSL shader via OSC."
+ examples: { '(draw [socket] shader)' }
+ description: "Syncs code and parameters with glsl-view via OSC.
+- `socket` must be an `osc/out~` or read from `*oscout*`
+- `shader` is a struct~ with
+ - `shader` (`str`): GLSL source
+ - `uniforms`: struct mapping uniform names to values"
+
+ value: class extends Op
+ pattern = -sig['osc/out'] + sig!
+
+ setup: (inputs, scope) =>
+ { socket, shader } = pattern\match inputs
super
socket: Input.hot socket or scope\get '*oscout*'
- code: Input.hot code
- params: [Input.hot param for param in *params]
-
- tick: (setup) =>
- bundle = Bundle.new Timetag.new!
- recompile = setup or @inputs.code\dirty! or @inputs.socket\dirty!
- if recompile
- bundle\add content: { address: '/shader', types: 's', @get_code! }
-
- for i, input in ipairs @inputs.params
- continue unless recompile or input\dirty!
-
- if sender = @state[i]
- addr, type, val = sender @
- bundle\add with msg = Message.new addr
- add_item msg, type, val
- elseif not recompile
- -- text-only piece is dirty, force recompile
- return @tick true
-
- return unless #bundle.content > 0
- with @inputs.socket!
- \send Bundle.pack bundle.content
+ shader: Input.hot shader
+
+ tick: =>
+ { :socket, :shader } = @unwrap_all!
+
+ with Bundle.new Timetag.new!
+ \add content: { address: '/shader', types: 's', shader.shader }
+
+ types = @inputs.shader\type!\get 'uniforms'
+ for key, val in pairs shader.uniforms
+ type = types\get key
+ msg = Message.new "/uniform/#{key}"
+ add_item msg, type, val
+ \add msg
+
+ socket\send Bundle.pack .content
Constant.meta
meta:
@@ -197,4 +214,5 @@ Constant.meta
:video
'tsv-input': tsv_input
:texture
- :shader
+ shader: shader_
+ :draw