scenevm 0.9.7

A GPU-based layer renderer with configurable compute shaders for Eldiron.
Documentation
struct USdf {
  background: vec4<f32>,
  fb_size: vec2<u32>,
  _pad0: vec2<u32>,
  gp0: vec4<f32>,
  gp1: vec4<f32>,
  gp2: vec4<f32>,
  gp3: vec4<f32>,
  gp4: vec4<f32>,
  gp5: vec4<f32>,
  gp6: vec4<f32>,
  gp7: vec4<f32>,
  gp8: vec4<f32>,
  gp9: vec4<f32>,
  cam_pos: vec4<f32>,
  cam_fwd: vec4<f32>,
  cam_right: vec4<f32>,
  cam_up: vec4<f32>,
  cam_vfov_deg: f32,
  cam_ortho_half_h: f32,
  cam_near: f32,
  cam_far: f32,
  cam_kind: u32, // 0=OrthoIso, 1=OrbitPersp, 2=FirstPersonPersp
  _pad1: u32,
  _pad2: u32,
  _pad3: u32,
  data_len: u32,
  vm_flags: u32,
  anim_counter: u32,
  _pad4: u32,
  viewport_rect: vec4<f32>, // [x, y, width, height] in screen pixels. width=0 means full screen.
  palette: array<vec4<f32>, 256>,
};

@group(0) @binding(0) var<uniform> U: USdf;
@group(0) @binding(1) var color_out: texture_storage_2d<rgba8unorm, write>;
@group(0) @binding(5) var prev_layer: texture_2d<f32>;
struct SdfDataBuffer {
  data: array<vec4<f32>>,
};

@group(0) @binding(2) var<storage, read_write> SDF_DATA: SdfDataBuffer;
@group(0) @binding(3) var atlas_tex: texture_2d<f32>;
@group(0) @binding(4) var atlas_smp: sampler;

fn sdf_data_len() -> u32 {
  // arrayLength returns the number of vec4<f32> elements, not bytes
  return arrayLength(&SDF_DATA.data);
}

fn sdf_data_at(i: u32) -> vec4<f32> {
  let len = max(sdf_data_len(), 1u);
  return SDF_DATA.data[i % len];
}

fn set_sdf_data_at(i: u32, v: vec4<f32>) {
  let len = max(sdf_data_len(), 1u);
  SDF_DATA.data[i % len] = v;
}

fn clear_if_needed(px: vec2<u32>) {
  // Layer texture is already cleared by render pass - no action needed
}

fn sv_write(px: u32, py: u32, c: vec4<f32>) {
  textureStore(color_out, vec2<i32>(i32(px), i32(py)), c);
}

fn sdf_sample_atlas(rect: vec4<f32>, uv: vec2<f32>) -> vec4<f32> {
  let uv_atlas = rect.xy + rect.zw * uv;
  return textureSampleLevel(atlas_tex, atlas_smp, uv_atlas, 0.0);
}

struct Ray {
  origin: vec3<f32>,
  dir: vec3<f32>,
};

// Build a world-space ray from screen uv (0..1) using the camera in USdf.
fn sdf_create_ray(screen_uv: vec2<f32>) -> Ray {
  let u = clamp(screen_uv.x, 0.0, 1.0);
  let v = clamp(screen_uv.y, 0.0, 1.0);
  let ndc_x = u * 2.0 - 1.0;
  let ndc_y = (v * 2.0 - 1.0) * -1.0;
  let fb_w = max(f32(U.fb_size.x), 1.0);
  let fb_h = max(f32(U.fb_size.y), 1.0);

  let kind = U.cam_kind;
  if (kind == 0u) { // OrthoIso
    let aspect = fb_w / fb_h;
    let half_w = U.cam_ortho_half_h * aspect;
    let origin = U.cam_pos.xyz
      + U.cam_right.xyz * (ndc_x * half_w)
      + U.cam_up.xyz * (ndc_y * U.cam_ortho_half_h);
    return Ray(origin, normalize(U.cam_fwd.xyz));
  } else {
    let tan_half = tan(radians(U.cam_vfov_deg) * 0.5);
    let aspect = fb_w / fb_h;
    let dx = ndc_x * aspect * tan_half;
    let dy = ndc_y * tan_half;
    let dir = normalize(U.cam_fwd.xyz + U.cam_right.xyz * dx + U.cam_up.xyz * dy);
    return Ray(U.cam_pos.xyz, dir);
  }
}