use crate::render3d::math::{Mat4, Vec3, Vec4};
#[derive(Debug, Clone, Copy)]
pub struct TransformedVertex {
pub screen_pos: Vec3,
pub world_pos: Vec3,
pub world_normal: Vec3,
pub uv: [f32; 2],
pub inv_w: f32,
}
#[derive(Debug, Clone, Copy)]
pub struct ClipVertex {
pub clip: Vec4,
pub world_pos: Vec3,
pub world_normal: Vec3,
pub uv: [f32; 2],
}
pub fn transform_to_clip(
position: Vec3,
normal: Vec3,
uv: [f32; 2],
model: &Mat4,
view_proj: &Mat4,
normal_matrix: &Mat4,
) -> ClipVertex {
let world_pos = model.transform_point3(position);
let world_normal = normal_matrix.transform_vector3(normal).normalize_or_zero();
let clip = *view_proj * Vec4::new(world_pos.x, world_pos.y, world_pos.z, 1.0);
ClipVertex {
clip,
world_pos,
world_normal,
uv,
}
}
fn lerp_clip(a: &ClipVertex, b: &ClipVertex, t: f32) -> ClipVertex {
let lerp3 = |x: Vec3, y: Vec3| x + (y - x) * t;
ClipVertex {
clip: a.clip + (b.clip - a.clip) * t,
world_pos: lerp3(a.world_pos, b.world_pos),
world_normal: lerp3(a.world_normal, b.world_normal).normalize_or_zero(),
uv: [
a.uv[0] + (b.uv[0] - a.uv[0]) * t,
a.uv[1] + (b.uv[1] - a.uv[1]) * t,
],
}
}
pub fn clip_near(tri: [ClipVertex; 3], near: f32) -> ([ClipVertex; 4], usize) {
let mut out = [tri[0]; 4];
let mut n = 0;
for i in 0..3 {
let a = tri[i];
let b = tri[(i + 1) % 3];
let a_in = a.clip.w >= near;
let b_in = b.clip.w >= near;
if a_in {
out[n] = a;
n += 1;
}
if a_in != b_in {
let t = (near - a.clip.w) / (b.clip.w - a.clip.w);
out[n] = lerp_clip(&a, &b, t);
n += 1;
}
}
(out, n)
}
pub fn clip_to_screen(
v: &ClipVertex,
viewport_width: f32,
viewport_height: f32,
) -> TransformedVertex {
let inv_w = 1.0 / v.clip.w;
let ndc = Vec3::new(v.clip.x * inv_w, v.clip.y * inv_w, v.clip.z * inv_w);
let screen_x = (ndc.x + 1.0) * 0.5 * viewport_width;
let screen_y = (1.0 - ndc.y) * 0.5 * viewport_height; let screen_z = (ndc.z + 1.0) * 0.5; TransformedVertex {
screen_pos: Vec3::new(screen_x, screen_y, screen_z),
world_pos: v.world_pos,
world_normal: v.world_normal,
uv: v.uv,
inv_w,
}
}