device 0.0.4

A generative engine
%% let mut binding = Counter::default();

@group(0)
@binding({{ binding.next() }})
var filtering_sampler: sampler;

@group(0)
@binding({{ binding.next() }})
var frequencies: texture_1d<f32>;

@group(0)
@binding({{ binding.next() }})
var source: texture_2d<f32>;

@group(0)
@binding({{ binding.next() }})
var non_filtering_sampler: sampler;

@group(0)
@binding({{ binding.next() }})
var samples: texture_1d<f32>;

@group(0)
@binding({{ binding.next() }})
var<uniform> uniforms: Uniforms;

const ERROR = vec4f(0, 1, 0, 1);
const TRANSPARENT = vec4f(0, 0, 0, 0);

%% for field in Field::iter() {
const {{ field.constant() }}: u32 = {{ field.number() }};
%% }

struct Uniforms {
  alpha: f32,
  base: f32,
  color: mat4x3f,
  coordinates: u32,
  field: u32,
  frequency_range: f32,
  front_offset: vec2f,
  gain: f32,
  grid: f32,
  grid_alpha: f32,
  interpolate: u32,
  mirror: vec4f,
  offset: vec2f,
  parameter: f32,
  position: mat3x2f,
  repeat: u32,
  resolution: f32,
  rms: f32,
  sample_range: f32,
  tiling: u32,
  wrap: u32,
}

fn coefficient() -> f32 {
  return uniforms.base + uniforms.rms / 10 * uniforms.gain;
}

fn field_all(p: vec2f) -> bool {
  return true;
}

fn field_bottom(p: vec2f) -> bool {
  return field_top(-p);
}

fn field_circle(p: vec2f) -> bool {
  return length(p) < uniforms.parameter * coefficient();
}

fn field_cross(p: vec2f) -> bool {
  let x = abs(p.x);
  let y = abs(p.y);
  return min(x, y) < 0.25 * coefficient() && x < 1.0 && y < 1.0;
}

fn field_frequencies(p: vec2f) -> bool {
  let x = (p.x + 1) * 0.5 * uniforms.frequency_range;
  let level = textureSample(frequencies, non_filtering_sampler, x).x * uniforms.gain;
  return level > (-p.y + 1) * 0.5;
}

fn field_left(p: vec2f) -> bool {
  return p.x + 1 < coefficient();
}

fn field_none(p: vec2f) -> bool {
  return false;
}

fn field_right(p: vec2f) -> bool {
  return field_left(-p);
}

fn field_samples(p: vec2f) -> bool {
  let x = (p.x + 1) * 0.5 * uniforms.sample_range;
  let level = textureSample(samples, non_filtering_sampler, x).x * uniforms.gain;
  return level < p.y;
}

fn field_square(p: vec2f) -> bool {
  return max(abs(p.x), abs(p.y)) < 0.5 * coefficient();
}

fn field_top(p: vec2f) -> bool {
  return p.y + 1 < coefficient();
}

fn field_triangle(p: vec2f) -> bool {
  return tan(radians(60)) * p.x - 0.5 * coefficient() < p.y
    && tan(radians(-60)) * p.x - 0.5  * coefficient() < p.y
    && p.y < 0.25 * coefficient();
}

fn field_x(p: vec2f) -> bool {
  let pixel = 2.0 / uniforms.resolution;
  return abs(abs(p.x) - abs(p.y)) < sqrt(2) * 0.25 * coefficient() - 0.5 * pixel;
}

fn grid(uv: vec2f) -> vec2f {
  return round(vec2(uv.x, (uv.y - 1) * -1)  * uniforms.grid)
    / uniforms.grid * uniforms.grid_alpha;
}

fn mod_floor(x: vec2f, y: f32) -> vec2f {
  return x - y * floor(x / y);
}

fn mirror(uv: vec2f) -> vec2f {
  let triangle = 1.0 - abs(2.0 * uv - 1.0);
  let inverse = 1.0 - triangle;
  let chosen = mix(triangle, inverse, uniforms.mirror.zw);
  return mix(uv, chosen, uniforms.mirror.xy);
}

@fragment
fn fragment(@builtin(position) position: vec4f) -> @location(0) vec4f {
  // subtract offset get tile coordinates
  let tile = position.xy - uniforms.offset;

  // convert to uv coordinates
  let source_uv = tile / vec2(uniforms.resolution, uniforms.resolution);

  let mirrored_uv = mirror(source_uv);

  // convert tile coordinates to [-1, 1]
  let centered = mirrored_uv * 2 - 1;

  // apply position transform
  var transformed = uniforms.position * vec3(centered, 1);

  // convert position to uv coordinates
  var uv = (transformed + 1) / 2;

  // wrap transformed coordinates
  if bool(uniforms.wrap) {
    transformed = mod_floor(transformed + 1.0, 2.0) - 1.0;
  }

  // scale to compensate for tiles not taking up full front texture
  let tile_scale = vec2(uniforms.resolution, uniforms.resolution) * f32(uniforms.tiling)
    / vec2f(textureDimensions(source, 0));

  var input_color = TRANSPARENT;

  if bool(uniforms.repeat) || all(uv >= vec2(0.0, 0.0)) && all(uv <= vec2(1.0, 1.0)) {
    if bool(uniforms.coordinates) {
      input_color = vec4(uv, 1.0, 1.0);
    } else {
      // convert uv coordinates to tile source coordinates
      let tile_uv = (uv / f32(uniforms.tiling) + uniforms.front_offset) * tile_scale;

      // read input color
      if bool(uniforms.interpolate) {
        input_color = textureSample(source, filtering_sampler, tile_uv);
      } else {
        input_color = textureSample(source, non_filtering_sampler, tile_uv);
      }
    }
  }

  // Sample original color
  let original_color = textureSample(
    source,
    non_filtering_sampler,
    (mirrored_uv / f32(uniforms.tiling) + uniforms.front_offset) * tile_scale,
  );

  var on: bool;

  switch uniforms.field {
%% for field in Field::iter() {
    case {{ field.constant() }} {
      on = {{ field.function() }}(transformed);
    }
%% }
    default {
      return ERROR;
    }
  }

  var alpha = 0.0;

  if on {
    alpha = uniforms.alpha;
  }

  // convert back to rgb
  var transformed_color = uniforms.color * vec4(input_color.rgb, 1.0);

  let grid = grid(mirrored_uv);
  transformed_color.g += grid.x;
  transformed_color.b += grid.y;

  // blend transformed and original color
  let blend = mix(original_color.rgb, transformed_color, alpha);

  // return blend with opaque alpha channel
  return vec4(blend, 1);
}