use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use crate::brush::wgsl::type_system::{DabField, UniformField};
use crate::brush::wire::{BrushWireType, ScalarValue};
use crate::nodegraph::{NodeId, PortDef, PortDir};
#[derive(Default, Clone, Debug)]
pub struct NodeWgsl {
pub decls: String,
pub body: String,
pub outputs: HashMap<String, String>,
pub dab_fields: Vec<DabField>,
pub uniform_fields: Vec<UniformField>,
pub terminal_bindings: String,
}
#[derive(Clone, Debug)]
pub enum InputBinding {
Wired(String),
Default(ScalarValue),
}
impl InputBinding {
pub fn as_f32(&self) -> String {
match self {
Self::Wired(expr) => expr.clone(),
Self::Default(v) => format!("{:.6}", v.as_f32()),
}
}
pub fn as_u32(&self) -> String {
match self {
Self::Wired(expr) => format!("u32({})", expr),
Self::Default(v) => format!("{}u", v.as_f32().max(0.0) as u32),
}
}
pub fn as_vec2(&self) -> String {
match self {
Self::Wired(expr) => expr.clone(),
Self::Default(v) => {
let [x, y] = v.as_vec2();
format!("vec2<f32>({:.6}, {:.6})", x, y)
}
}
}
pub fn as_vec4(&self) -> String {
match self {
Self::Wired(expr) => expr.clone(),
Self::Default(v) => {
let [r, g, b, a] = v.as_color();
format!("vec4<f32>({:.6}, {:.6}, {:.6}, {:.6})", r, g, b, a)
}
}
}
}
pub struct CompileWgslCtx<'a> {
pub node_id: NodeId,
pub params: &'a [crate::gpu::params::ParamValue],
pub port_defs: &'a [PortDef<BrushWireType>],
pub inputs: HashMap<String, InputBinding>,
pub lut: Option<&'a crate::brush::curve_math::CurveLut>,
pub consumed_outputs: HashSet<String>,
pub graph_textures: &'a RefCell<Vec<String>>,
}
impl CompileWgslCtx<'_> {
pub fn input(&self, name: &str) -> InputBinding {
if let Some(b) = self.inputs.get(name) {
return b.clone();
}
for port in self.port_defs {
if port.name == name && port.dir == PortDir::Input {
return InputBinding::Default(ScalarValue::Scalar(port.default));
}
}
InputBinding::Default(ScalarValue::Scalar(0.0))
}
pub fn input_is_wired(&self, name: &str) -> bool {
matches!(self.inputs.get(name), Some(InputBinding::Wired(_)))
}
pub fn ident(&self, base: &str) -> String {
format!("{}_{}", base, self.node_id.0)
}
pub fn dab_field_name(&self, base: &str) -> String {
format!("n{}_{}", self.node_id.0, base)
}
pub fn uniform_field_name(&self, base: &str) -> String {
format!("n{}_{}", self.node_id.0, base)
}
pub fn request_texture(&self, name: &str) -> u32 {
let mut list = self.graph_textures.borrow_mut();
if let Some(idx) = list.iter().position(|n| n == name) {
return idx as u32;
}
let idx = list.len() as u32;
list.push(name.to_string());
idx
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ShaderMode {
Stroke,
CursorPreview,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn input_binding_emits_default_literal() {
let b = InputBinding::Default(ScalarValue::Scalar(0.5));
assert!(b.as_f32().starts_with("0.5"));
assert!(b.as_vec2().starts_with("vec2<f32>(0.5"));
}
#[test]
fn input_binding_passes_wired_through() {
let b = InputBinding::Wired("d.foo".into());
assert_eq!(b.as_f32(), "d.foo");
assert_eq!(b.as_vec2(), "d.foo");
}
}