use crate::camera3d::Camera3D;
use crate::mesh3d::{Mesh3D, Vec3};
use crate::pbr::{Bloom, HDRProcessor, PBRLight, PBRMaterial, PBRShader, ShadowMap, SSAO};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RenderMode {
Forward, Deferred, }
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum RenderQuality {
Low, Medium, High, Ultra, }
pub struct GBuffer {
pub position: Vec<Vec3>,
pub normal: Vec<Vec3>,
pub albedo: Vec<[f32; 3]>,
pub material: Vec<(f32, f32)>, pub depth: Vec<f32>,
}
impl GBuffer {
pub fn new(width: u32, height: u32) -> Self {
let size = (width * height) as usize;
Self {
position: vec![Vec3::new(0.0, 0.0, 0.0); size],
normal: vec![Vec3::new(0.0, 1.0, 0.0); size],
albedo: vec![[0.0, 0.0, 0.0]; size],
material: vec![(0.0, 0.5); size],
depth: vec![f32::INFINITY; size],
}
}
pub fn clear(&mut self) {
for depth in &mut self.depth {
*depth = f32::INFINITY;
}
}
}
pub struct AdvancedRenderer3D {
pub camera: Camera3D,
pub width: u32,
pub height: u32,
pub zbuffer: Vec<f32>,
pub gbuffer: GBuffer,
pub hdr_buffer: Vec<[f32; 3]>,
pub bloom_buffer: Vec<[f32; 3]>,
pub pbr_shader: PBRShader,
pub shadow_map: ShadowMap,
pub ssao: SSAO,
pub hdr: HDRProcessor,
pub bloom: Bloom,
pub render_mode: RenderMode,
pub quality: RenderQuality,
pub wireframe: bool,
pub backface_culling: bool,
pub triangles_rendered: u32,
pub triangles_culled: u32,
}
impl AdvancedRenderer3D {
pub fn new(width: u32, height: u32) -> Self {
let buffer_size = (width * height) as usize;
Self {
camera: Camera3D::new(width, height),
width,
height,
zbuffer: vec![f32::INFINITY; buffer_size],
gbuffer: GBuffer::new(width, height),
hdr_buffer: vec![[0.0, 0.0, 0.0]; buffer_size],
bloom_buffer: vec![[0.0, 0.0, 0.0]; buffer_size],
pbr_shader: PBRShader::new(),
shadow_map: ShadowMap::new(1024, 1024),
ssao: SSAO::new(),
hdr: HDRProcessor::new(),
bloom: Bloom::new(),
render_mode: RenderMode::Forward,
quality: RenderQuality::High,
wireframe: false,
backface_culling: true,
triangles_rendered: 0,
triangles_culled: 0,
}
}
pub fn set_quality(&mut self, quality: RenderQuality) {
self.quality = quality;
match quality {
RenderQuality::Low => {
self.shadow_map.width = 512;
self.shadow_map.height = 512;
self.ssao.enabled = false;
self.bloom.enabled = false;
self.hdr.enabled = false;
}
RenderQuality::Medium => {
self.shadow_map.width = 1024;
self.shadow_map.height = 1024;
self.ssao.enabled = false;
self.bloom.enabled = false;
self.hdr.enabled = true;
}
RenderQuality::High => {
self.shadow_map.width = 2048;
self.shadow_map.height = 2048;
self.ssao.enabled = true;
self.bloom.enabled = false;
self.hdr.enabled = true;
}
RenderQuality::Ultra => {
self.shadow_map.width = 4096;
self.shadow_map.height = 4096;
self.ssao.enabled = true;
self.bloom.enabled = true;
self.hdr.enabled = true;
}
}
}
pub fn add_light(&mut self, light: PBRLight) {
self.pbr_shader.add_light(light);
}
pub fn clear(&mut self) {
for z in &mut self.zbuffer {
*z = f32::INFINITY;
}
for color in &mut self.hdr_buffer {
*color = [0.0, 0.0, 0.0];
}
for color in &mut self.bloom_buffer {
*color = [0.0, 0.0, 0.0];
}
if self.render_mode == RenderMode::Deferred {
self.gbuffer.clear();
}
self.triangles_rendered = 0;
self.triangles_culled = 0;
}
pub fn render_mesh_pbr(
&mut self,
mesh: &Mesh3D,
material: &PBRMaterial,
frame: &mut [u8],
) {
match self.render_mode {
RenderMode::Forward => self.render_forward(mesh, material, frame),
RenderMode::Deferred => self.render_deferred(mesh, material, frame),
}
}
fn render_forward(&mut self, mesh: &Mesh3D, material: &PBRMaterial, frame: &mut [u8]) {
for i in (0..mesh.indices.len()).step_by(3) {
let idx0 = mesh.indices[i] as usize;
let idx1 = mesh.indices[i + 1] as usize;
let idx2 = mesh.indices[i + 2] as usize;
if idx0 >= mesh.vertices.len()
|| idx1 >= mesh.vertices.len()
|| idx2 >= mesh.vertices.len()
{
continue;
}
let v0 = &mesh.vertices[idx0];
let v1 = &mesh.vertices[idx1];
let v2 = &mesh.vertices[idx2];
let p0 = self.transform_vertex(&v0.position, mesh);
let p1 = self.transform_vertex(&v1.position, mesh);
let p2 = self.transform_vertex(&v2.position, mesh);
if self.backface_culling {
let edge1 = Vec3::new(p1.x - p0.x, p1.y - p0.y, p1.z - p0.z);
let edge2 = Vec3::new(p2.x - p0.x, p2.y - p0.y, p2.z - p0.z);
let normal = edge1.cross(&edge2);
let to_camera = Vec3::new(
self.camera.position.x - p0.x,
self.camera.position.y - p0.y,
self.camera.position.z - p0.z,
);
if normal.dot(&to_camera) < 0.0 {
self.triangles_culled += 1;
continue;
}
}
if let (Some(s0), Some(s1), Some(s2)) = (
self.camera.project(&p0, self.width, self.height),
self.camera.project(&p1, self.width, self.height),
self.camera.project(&p2, self.width, self.height),
) {
self.triangles_rendered += 1;
let edge1 = Vec3::new(p1.x - p0.x, p1.y - p0.y, p1.z - p0.z);
let edge2 = Vec3::new(p2.x - p0.x, p2.y - p0.y, p2.z - p0.z);
let normal = edge1.cross(&edge2).normalize();
let center = Vec3::new(
(p0.x + p1.x + p2.x) / 3.0,
(p0.y + p1.y + p2.y) / 3.0,
(p0.z + p1.z + p2.z) / 3.0,
);
let view_dir = Vec3::new(
self.camera.position.x - center.x,
self.camera.position.y - center.y,
self.camera.position.z - center.z,
)
.normalize();
let mut lit_color = self.pbr_shader.shade(material, ¢er, &normal, &view_dir);
if self.quality >= RenderQuality::Medium {
if self.shadow_map.is_in_shadow(¢er) {
lit_color[0] *= 0.3;
lit_color[1] *= 0.3;
lit_color[2] *= 0.3;
}
}
if self.quality >= RenderQuality::High {
let ao = self.ssao.calculate_occlusion(
¢er,
&normal,
&self.zbuffer,
self.width,
self.height,
);
lit_color[0] *= ao;
lit_color[1] *= ao;
lit_color[2] *= ao;
}
let x = s0.0 as u32;
let y = s0.1 as u32;
if x >= self.width || y >= self.height {
continue;
}
let buffer_idx = (y as usize * self.width as usize + x as usize);
if buffer_idx < self.hdr_buffer.len() {
self.hdr_buffer[buffer_idx] = lit_color;
}
if self.bloom.enabled && buffer_idx < self.bloom_buffer.len() {
let bloom_color = self.bloom.extract_bright(lit_color);
self.bloom_buffer[buffer_idx] = bloom_color;
}
let final_color = if self.bloom.enabled && buffer_idx < self.bloom_buffer.len() {
self.bloom.apply(lit_color, self.bloom_buffer[buffer_idx])
} else {
lit_color
};
let tone_mapped = self.hdr.tone_map_aces(final_color);
let color = [
(tone_mapped[0] * 255.0) as u8,
(tone_mapped[1] * 255.0) as u8,
(tone_mapped[2] * 255.0) as u8,
255,
];
if self.wireframe {
self.draw_line(frame, s0.0, s0.1, s1.0, s1.1, color);
self.draw_line(frame, s1.0, s1.1, s2.0, s2.1, color);
self.draw_line(frame, s2.0, s2.1, s0.0, s0.1, color);
} else {
self.draw_triangle(frame, s0, s1, s2, color);
}
}
}
}
fn render_deferred(&mut self, mesh: &Mesh3D, material: &PBRMaterial, frame: &mut [u8]) {
self.render_forward(mesh, material, frame);
}
fn transform_vertex(&self, vertex: &Vec3, mesh: &Mesh3D) -> Vec3 {
let mut v = Vec3::new(
vertex.x * mesh.scale.x,
vertex.y * mesh.scale.y,
vertex.z * mesh.scale.z,
);
let cos_x = mesh.rotation.x.cos();
let sin_x = mesh.rotation.x.sin();
let cos_y = mesh.rotation.y.cos();
let sin_y = mesh.rotation.y.sin();
let cos_z = mesh.rotation.z.cos();
let sin_z = mesh.rotation.z.sin();
let temp_x = v.x * cos_y + v.z * sin_y;
let temp_z = -v.x * sin_y + v.z * cos_y;
v.x = temp_x;
v.z = temp_z;
let temp_y = v.y * cos_x - v.z * sin_x;
let temp_z = v.y * sin_x + v.z * cos_x;
v.y = temp_y;
v.z = temp_z;
let temp_x = v.x * cos_z - v.y * sin_z;
let temp_y = v.x * sin_z + v.y * cos_z;
v.x = temp_x;
v.y = temp_y;
v.x += mesh.position.x;
v.y += mesh.position.y;
v.z += mesh.position.z;
v
}
fn draw_line(
&self,
frame: &mut [u8],
x0: i32,
y0: i32,
x1: i32,
y1: i32,
color: [u8; 4],
) {
let dx = (x1 - x0).abs();
let dy = (y1 - y0).abs();
let sx = if x0 < x1 { 1 } else { -1 };
let sy = if y0 < y1 { 1 } else { -1 };
let mut err = dx - dy;
let mut x = x0;
let mut y = y0;
loop {
if x >= 0 && x < self.width as i32 && y >= 0 && y < self.height as i32 {
let idx = ((y as u32 * self.width + x as u32) * 4) as usize;
if idx + 3 < frame.len() {
frame[idx] = color[0];
frame[idx + 1] = color[1];
frame[idx + 2] = color[2];
frame[idx + 3] = color[3];
}
}
if x == x1 && y == y1 {
break;
}
let e2 = 2 * err;
if e2 > -dy {
err -= dy;
x += sx;
}
if e2 < dx {
err += dx;
y += sy;
}
}
}
fn draw_triangle(
&mut self,
frame: &mut [u8],
p0: (i32, i32, f32),
p1: (i32, i32, f32),
p2: (i32, i32, f32),
color: [u8; 4],
) {
let min_x = p0.0.min(p1.0).min(p2.0).max(0);
let max_x = p0.0.max(p1.0).max(p2.0).min(self.width as i32 - 1);
let min_y = p0.1.min(p1.1).min(p2.1).max(0);
let max_y = p0.1.max(p1.1).max(p2.1).min(self.height as i32 - 1);
for y in min_y..=max_y {
for x in min_x..=max_x {
if x < 0 || y < 0 || x >= self.width as i32 || y >= self.height as i32 {
continue;
}
if self.point_in_triangle(x, y, p0, p1, p2) {
let x_u = x as usize;
let y_u = y as usize;
let width_u = self.width as usize;
if let Some(row_offset) = y_u.checked_mul(width_u) {
if let Some(pixel_idx) = row_offset.checked_add(x_u) {
if let Some(idx) = pixel_idx.checked_mul(4) {
if idx + 3 < frame.len() {
frame[idx] = color[0];
frame[idx + 1] = color[1];
frame[idx + 2] = color[2];
frame[idx + 3] = 255;
}
}
}
}
}
}
}
}
fn point_in_triangle(
&self,
px: i32,
py: i32,
p0: (i32, i32, f32),
p1: (i32, i32, f32),
p2: (i32, i32, f32),
) -> bool {
let sign = |p1: (i32, i32), p2: (i32, i32), p3: (i32, i32)| -> i32 {
(p1.0 - p3.0) * (p2.1 - p3.1) - (p2.0 - p3.0) * (p1.1 - p3.1)
};
let d1 = sign((px, py), (p0.0, p0.1), (p1.0, p1.1));
let d2 = sign((px, py), (p1.0, p1.1), (p2.0, p2.1));
let d3 = sign((px, py), (p2.0, p2.1), (p0.0, p0.1));
let has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
let has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
!(has_neg && has_pos)
}
pub fn remove_light(&mut self, index: usize) {
if index < self.pbr_shader.lights.len() {
self.pbr_shader.lights.remove(index);
}
}
pub fn clear_lights(&mut self) {
self.pbr_shader.lights.clear();
}
pub fn set_ambient_light(&mut self, r: f32, g: f32, b: f32) {
self.pbr_shader.set_ambient(r, g, b);
}
pub fn resize(&mut self, width: u32, height: u32) {
self.width = width;
self.height = height;
let buffer_size = (width * height) as usize;
self.zbuffer = vec![f32::INFINITY; buffer_size];
self.hdr_buffer = vec![[0.0, 0.0, 0.0]; buffer_size];
self.bloom_buffer = vec![[0.0, 0.0, 0.0]; buffer_size];
self.gbuffer = GBuffer::new(width, height);
self.camera = Camera3D::new(width, height);
}
pub fn render_batch_pbr(
&mut self,
meshes: &[(Mesh3D, PBRMaterial)],
frame: &mut [u8],
) {
for (mesh, material) in meshes {
self.render_mesh_pbr(mesh, material, frame);
}
}
pub fn render_mesh_simple(
&mut self,
mesh: &Mesh3D,
color: [u8; 4],
frame: &mut [u8],
) {
for i in (0..mesh.indices.len()).step_by(3) {
let idx0 = mesh.indices[i] as usize;
let idx1 = mesh.indices[i + 1] as usize;
let idx2 = mesh.indices[i + 2] as usize;
if idx0 >= mesh.vertices.len()
|| idx1 >= mesh.vertices.len()
|| idx2 >= mesh.vertices.len()
{
continue;
}
let v0 = &mesh.vertices[idx0];
let v1 = &mesh.vertices[idx1];
let v2 = &mesh.vertices[idx2];
let p0 = self.transform_vertex(&v0.position, mesh);
let p1 = self.transform_vertex(&v1.position, mesh);
let p2 = self.transform_vertex(&v2.position, mesh);
if self.backface_culling {
let edge1 = Vec3::new(p1.x - p0.x, p1.y - p0.y, p1.z - p0.z);
let edge2 = Vec3::new(p2.x - p0.x, p2.y - p0.y, p2.z - p0.z);
let normal = edge1.cross(&edge2);
let to_camera = Vec3::new(
self.camera.position.x - p0.x,
self.camera.position.y - p0.y,
self.camera.position.z - p0.z,
);
if normal.dot(&to_camera) < 0.0 {
self.triangles_culled += 1;
continue;
}
}
if let (Some(s0), Some(s1), Some(s2)) = (
self.camera.project(&p0, self.width, self.height),
self.camera.project(&p1, self.width, self.height),
self.camera.project(&p2, self.width, self.height),
) {
self.triangles_rendered += 1;
if self.wireframe {
self.draw_line(frame, s0.0, s0.1, s1.0, s1.1, color);
self.draw_line(frame, s1.0, s1.1, s2.0, s2.1, color);
self.draw_line(frame, s2.0, s2.1, s0.0, s0.1, color);
} else {
self.draw_triangle(frame, s0, s1, s2, color);
}
}
}
}
pub fn get_stats(&self) -> RenderStats {
RenderStats {
triangles_rendered: self.triangles_rendered,
triangles_culled: self.triangles_culled,
lights_count: self.pbr_shader.lights.len() as u32,
shadow_map_size: self.shadow_map.width,
quality: self.quality,
ssao_samples: if self.ssao.enabled { self.ssao.samples } else { 0 },
bloom_passes: if self.bloom.enabled { 3 } else { 0 },
}
}
}
#[derive(Debug, Clone)]
pub struct RenderStats {
pub triangles_rendered: u32,
pub triangles_culled: u32,
pub lights_count: u32,
pub shadow_map_size: u32,
pub quality: RenderQuality,
pub ssao_samples: u32,
pub bloom_passes: u32,
}