pub mod addr;
pub mod chunks;
pub mod edit;
pub mod render;
pub mod snapshot;
use std::collections::HashMap;
use glam::{DQuat, DVec3, IVec3, UVec3};
use roxlap_formats::vxl::Vxl;
use serde::{Deserialize, Serialize};
pub use addr::{grid_local_to_world, voxel_global, voxel_split, world_to_grid_local, GridLocalPos};
pub const CHUNK_SIZE_XY: u32 = 128;
pub const CHUNK_SIZE_Z: u32 = 256;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct GridId(u32);
impl GridId {
#[must_use]
pub const fn raw(self) -> u32 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct GridTransform {
pub origin: DVec3,
pub rotation: DQuat,
}
impl GridTransform {
#[must_use]
pub fn identity() -> Self {
Self {
origin: DVec3::ZERO,
rotation: DQuat::IDENTITY,
}
}
#[must_use]
pub fn at(origin: DVec3) -> Self {
Self {
origin,
rotation: DQuat::IDENTITY,
}
}
}
impl Default for GridTransform {
fn default() -> Self {
Self::identity()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct GridAddr {
pub grid: GridId,
pub chunk: IVec3,
pub voxel: UVec3,
}
#[derive(Debug)]
pub struct Grid {
pub transform: GridTransform,
pub chunks: HashMap<IVec3, Vxl>,
pub render_sky: bool,
pub mip_levels_override: Option<u32>,
}
impl Grid {
#[must_use]
pub fn new(transform: GridTransform) -> Self {
Self {
transform,
chunks: HashMap::new(),
render_sky: true,
mip_levels_override: None,
}
}
}
#[derive(Debug, Default)]
pub struct Scene {
grids: HashMap<GridId, Grid>,
next_grid_id: u32,
}
impl Scene {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn grid_count(&self) -> usize {
self.grids.len()
}
pub fn add_grid(&mut self, transform: GridTransform) -> GridId {
let id = GridId(self.next_grid_id);
self.next_grid_id += 1;
self.grids.insert(id, Grid::new(transform));
id
}
pub fn remove_grid(&mut self, id: GridId) -> Option<Grid> {
self.grids.remove(&id)
}
#[must_use]
pub fn grid(&self, id: GridId) -> Option<&Grid> {
self.grids.get(&id)
}
pub fn grid_mut(&mut self, id: GridId) -> Option<&mut Grid> {
self.grids.get_mut(&id)
}
pub fn grids(&self) -> impl Iterator<Item = (GridId, &Grid)> {
self.grids.iter().map(|(id, g)| (*id, g))
}
pub fn grids_mut(&mut self) -> impl Iterator<Item = (GridId, &mut Grid)> {
self.grids.iter_mut().map(|(id, g)| (*id, g))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_scene_has_no_grids() {
let scene = Scene::new();
assert_eq!(scene.grid_count(), 0);
assert!(scene.grids().next().is_none());
}
#[test]
fn add_grid_returns_fresh_ids() {
let mut scene = Scene::new();
let a = scene.add_grid(GridTransform::identity());
let b = scene.add_grid(GridTransform::at(DVec3::new(100.0, 0.0, 0.0)));
assert_ne!(a, b);
assert_eq!(a.raw(), 0);
assert_eq!(b.raw(), 1);
assert_eq!(scene.grid_count(), 2);
}
#[test]
fn grid_lookup_round_trips() {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::at(DVec3::new(10.0, 20.0, 30.0)));
let g = scene.grid(id).expect("grid registered");
assert_eq!(g.transform.origin, DVec3::new(10.0, 20.0, 30.0));
assert_eq!(g.transform.rotation, DQuat::IDENTITY);
assert!(g.chunks.is_empty());
}
#[test]
fn remove_grid_drops_it_from_scene() {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::identity());
let removed = scene.remove_grid(id);
assert!(removed.is_some());
assert_eq!(scene.grid_count(), 0);
assert!(scene.grid(id).is_none());
let id2 = scene.add_grid(GridTransform::identity());
assert_ne!(id, id2);
assert_eq!(id2.raw(), 1);
}
#[test]
fn remove_unknown_grid_is_none() {
let mut scene = Scene::new();
let bogus = GridId(999);
assert!(scene.remove_grid(bogus).is_none());
}
#[test]
fn grid_mut_can_modify_transform() {
let mut scene = Scene::new();
let id = scene.add_grid(GridTransform::identity());
scene.grid_mut(id).unwrap().transform.origin = DVec3::new(1.0, 2.0, 3.0);
assert_eq!(
scene.grid(id).unwrap().transform.origin,
DVec3::new(1.0, 2.0, 3.0)
);
}
#[test]
fn chunk_size_constants_match_plan() {
assert_eq!(CHUNK_SIZE_XY, 128);
assert_eq!(CHUNK_SIZE_Z, 256);
}
}