dynamis-world 0.7.0

Host scene registry, step orchestration, and readback
Documentation
use super::World;
use crate::shape_pool::{ShapePool, height_field_triangles};
use dynamis_abi::{SHAPE_HEIGHTFIELD, SHAPE_HULL, SHAPE_MESH};
use dynamis_hull::hull;
use dynamis_model::{Shape, ShapeSourceHandle, SolidGeometry, SurfaceDesc, SurfaceTable};

#[derive(Clone)]
pub(crate) struct Shapes {
    pub(crate) pool: ShapePool,
    pub(crate) dirty: bool,
    pub(crate) uploaded: bool,
}

impl Shapes {
    pub(crate) fn new() -> Self {
        Self {
            pool: ShapePool::new(),
            dirty: false,
            uploaded: false,
        }
    }
}

impl World {
    pub fn add_hull(&mut self, vertices: &[[f32; 3]], triangles: &[[u32; 3]]) -> ShapeSourceHandle {
        self.allocate_shape(SHAPE_HULL, vertices, triangles.to_vec(), None)
    }

    pub fn add_mesh(
        &mut self,
        vertices: &[[f32; 3]],
        triangles: &[[u32; 3]],
        surfaces: Option<SurfaceTable<'_>>,
    ) -> ShapeSourceHandle {
        self.allocate_shape(SHAPE_MESH, vertices, triangles.to_vec(), surfaces)
    }

    pub fn add_decomposed_mesh(
        &mut self,
        vertices: &[[f32; 3]],
        triangles: &[[u32; 3]],
        settings: dynamis_hull::DecomposeSettings,
    ) -> Vec<ShapeSourceHandle> {
        let parts = dynamis_hull::decompose(vertices, triangles, &settings);
        parts
            .iter()
            .map(|part| self.add_hull(&part.vertices, &part.triangles))
            .collect()
    }

    pub fn add_hull_from_mesh(
        &mut self,
        vertices: &[[f32; 3]],
        triangles: &[[u32; 3]],
    ) -> ShapeSourceHandle {
        let (hull_vertices, hull_triangles) = hull(vertices, triangles);
        self.allocate_shape(SHAPE_HULL, &hull_vertices, hull_triangles, None)
    }

    pub fn add_height_field(
        &mut self,
        rows: u32,
        cols: u32,
        heights: &[f32],
        cell_size: [f32; 2],
        surfaces: Option<SurfaceTable<'_>>,
    ) -> ShapeSourceHandle {
        let (vertices, triangles) = height_field_triangles(rows, cols, heights, cell_size);
        let expanded = height_field_surfaces(surfaces, rows, cols);
        let table = expanded
            .as_ref()
            .map(|(palette, indices)| SurfaceTable::new(palette, indices));
        self.allocate_shape(SHAPE_HEIGHTFIELD, &vertices, triangles, table)
    }

    pub fn remove_shape(&mut self, handle: ShapeSourceHandle) {
        self.shapes.pool.remove(handle);
        self.shapes.dirty = true;
    }

    pub fn update_mesh(
        &mut self,
        handle: ShapeSourceHandle,
        vertices: &[[f32; 3]],
        triangles: &[[u32; 3]],
        surfaces: Option<SurfaceTable<'_>>,
    ) {
        self.shapes
            .pool
            .update_mesh(handle, vertices, triangles, surfaces);
        self.shapes.dirty = true;
    }

    pub fn update_height_field(
        &mut self,
        handle: ShapeSourceHandle,
        rows: u32,
        cols: u32,
        heights: &[f32],
        cell_size: [f32; 2],
        surfaces: Option<SurfaceTable<'_>>,
    ) {
        let (vertices, triangles) = height_field_triangles(rows, cols, heights, cell_size);
        let expanded = height_field_surfaces(surfaces, rows, cols);
        let table = expanded
            .as_ref()
            .map(|(palette, indices)| SurfaceTable::new(palette, indices));
        self.update_mesh(handle, &vertices, &triangles, table);
    }

    fn allocate_shape(
        &mut self,
        kind: u32,
        vertices: &[[f32; 3]],
        triangles: Vec<[u32; 3]>,
        surfaces: Option<SurfaceTable<'_>>,
    ) -> ShapeSourceHandle {
        let handle = self
            .shapes
            .pool
            .allocate(kind, vertices, &triangles, surfaces);
        self.shapes.dirty = true;
        handle
    }

    pub(super) fn shape_solid(&self, shape: &Shape) -> Option<SolidGeometry> {
        match shape {
            Shape::Hull(handle) => Some(self.shapes.pool.solid(*handle)),
            _ => None,
        }
    }
}

fn height_field_surfaces(
    surfaces: Option<SurfaceTable<'_>>,
    rows: u32,
    cols: u32,
) -> Option<(Vec<SurfaceDesc>, Vec<u32>)> {
    let table = surfaces?;
    let cells = rows.saturating_sub(1) * cols.saturating_sub(1);
    assert_eq!(
        table.count(),
        cells as usize,
        "a height field carries one surface per cell"
    );
    let palette = table.palette().to_vec();
    let mut indices = Vec::with_capacity(table.count() * 2);
    for index in table.indices() {
        indices.push(*index);
        indices.push(*index);
    }
    Some((palette, indices))
}