enginerenderer 0.0.1

A zero-dependency offline rendering engine in pure Rust — CPU path tracing, BVH acceleration, 16-band spectral rendering, PBR materials, animation & video export.
Documentation
use crate::core::engine::rendering::raytracing::Vec3;

#[derive(Debug, Clone)]
pub struct VoronoiCell {
    pub center: Vec3,
    pub vertices: Vec<Vec3>,
    pub broken: bool,
}

#[derive(Debug, Clone)]
pub struct Bond {
    pub a: usize,
    pub b: usize,
    pub strength: f64,
    pub broken: bool,
}

pub struct FractureBody {
    pub cells: Vec<VoronoiCell>,
    pub bonds: Vec<Bond>,
    pub threshold: f64,
}

impl FractureBody {
    pub fn generate(bounds_min: Vec3, bounds_max: Vec3, seed_count: usize, seed: u64) -> Self {
        let centers = lcg_points(bounds_min, bounds_max, seed_count, seed);
        let cells: Vec<VoronoiCell> = centers
            .into_iter()
            .map(|c| VoronoiCell {
                center: c,
                vertices: Vec::new(),
                broken: false,
            })
            .collect();

        let n = cells.len();
        let mut bonds = Vec::new();
        for i in 0..n {
            for j in i + 1..n {
                let dist = (cells[i].center - cells[j].center).length();
                let extent = (bounds_max - bounds_min).length();
                let neighbor_threshold = extent / (seed_count as f64).sqrt() * 1.5;
                if dist < neighbor_threshold {
                    bonds.push(Bond {
                        a: i,
                        b: j,
                        strength: 1.0,
                        broken: false,
                    });
                }
            }
        }

        Self {
            cells,
            bonds,
            threshold: 0.5,
        }
    }

    pub fn apply_impulse(&mut self, point: Vec3, force_magnitude: f64) {
        for bond in &mut self.bonds {
            if bond.broken {
                continue;
            }
            let ca = self.cells[bond.a].center;
            let cb = self.cells[bond.b].center;
            let midpoint = (ca + cb) * 0.5;
            let dist = (midpoint - point).length().max(1e-6);
            let stress = force_magnitude / (dist * dist);
            if stress > bond.strength * self.threshold {
                bond.broken = true;
            }
        }

        for cell in &mut self.cells {
            let dist = (cell.center - point).length().max(1e-6);
            let stress = force_magnitude / (dist * dist);
            if stress > self.threshold * 2.0 {
                cell.broken = true;
            }
        }
    }

    pub fn fractured_cells(&self) -> Vec<&VoronoiCell> {
        self.cells.iter().filter(|c| c.broken).collect()
    }

    pub fn intact_cells(&self) -> Vec<&VoronoiCell> {
        self.cells.iter().filter(|c| !c.broken).collect()
    }

    pub fn is_fully_fractured(&self) -> bool {
        self.bonds.iter().all(|b| b.broken)
    }

    pub fn intact_bond_count(&self) -> usize {
        self.bonds.iter().filter(|b| !b.broken).count()
    }
}

fn lcg_points(min: Vec3, max: Vec3, count: usize, seed: u64) -> Vec<Vec3> {
    let mut s = seed.wrapping_add(0x0DDF_ACC1_7777_7777);
    let mut points = Vec::with_capacity(count);
    let range = max - min;
    for _ in 0..count {
        s = s
            .wrapping_mul(6364136223846793005)
            .wrapping_add(1442695040888963407);
        let fx = ((s >> 33) as f64) / (u32::MAX as f64);
        s = s
            .wrapping_mul(6364136223846793005)
            .wrapping_add(1442695040888963407);
        let fy = ((s >> 33) as f64) / (u32::MAX as f64);
        s = s
            .wrapping_mul(6364136223846793005)
            .wrapping_add(1442695040888963407);
        let fz = ((s >> 33) as f64) / (u32::MAX as f64);
        points.push(Vec3::new(
            min.x + fx * range.x,
            min.y + fy * range.y,
            min.z + fz * range.z,
        ));
    }
    points
}