From f1286af980a770b0328142df1b0feee5deb7a1a0 Mon Sep 17 00:00:00 2001 From: Oliver Dew Date: Sat, 27 Jan 2024 10:34:53 +0000 Subject: [PATCH] Expose VoxelData --- README.md | 7 ++++++- src/lib.rs | 6 +++--- src/model/mod.rs | 29 +++++++++++++++++++++++------ src/model/queryable.rs | 26 +++++++++++++++++++++++++- src/scene/mod.rs | 2 +- src/scene/systems.rs | 15 ++++++++------- 6 files changed, 66 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 5c093da..8e827bf 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,12 @@ All Magica Voxel material types except "cloud" are supported. Bevy's screen spac ## Usage -1. Add the crate using cargo: `cargo add bevy_vox_scene` +1. Add the crate using the command line (`cargo add bevy_vox_scene`) or by adding it to `Cargo.toml`: + +```toml +[dependencies] +bevy_vox_scene = "0.11.4" +``` Then in code: diff --git a/src/lib.rs b/src/lib.rs index 789f23d..015cc07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(missing_docs, unsafe_code)] +#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))] //! A plugin for the Bevy engine which allows loading Magica Voxel .vox files as scene graphs. //! //!``` @@ -43,8 +45,6 @@ //! # exit.send(AppExit); //! # } //!``` -#![forbid(missing_docs, unsafe_code)] -#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))] use bevy::{ app::{App, Plugin, SpawnScene}, @@ -65,7 +65,7 @@ use load::VoxSceneLoader; pub use model::{ modify::{ModifyVoxelCommandsExt, VoxelRegion, VoxelRegionMode}, queryable::VoxelQueryable, - Voxel, VoxelModel, + Voxel, VoxelData, VoxelModel, }; pub use scene::{ VoxelLayer, VoxelModelInstance, VoxelScene, VoxelSceneBundle, VoxelSceneHook, diff --git a/src/model/mod.rs b/src/model/mod.rs index b36dfe0..0e74e80 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -1,5 +1,6 @@ use bevy::{ - asset::{Asset, Handle}, + asset::{Asset, Assets, Handle}, + ecs::system::ResMut, pbr::StandardMaterial, reflect::TypePath, render::mesh::Mesh, @@ -28,14 +29,30 @@ pub struct VoxelModel { pub(crate) material: Handle, } +impl VoxelModel { + /// Create a new VoxelModel from [`VoxelData`] + pub fn new( + data: VoxelData, + meshes: &mut ResMut>, + material: Handle, + ) -> Self { + let mesh = data.remesh(); + VoxelModel { + data, + mesh: meshes.add(mesh), + material, + } + } +} + /// The voxel data used to create a mesh and a material. /// /// Note that all coordinates are in Bevy's right-handed Y-up space -pub(crate) struct VoxelData { - pub shape: RuntimeShape, - pub voxels: Vec, - pub ior_for_voxel: HashMap, - pub mesh_outer_faces: bool, +pub struct VoxelData { + pub(crate) shape: RuntimeShape, + pub(crate) voxels: Vec, + pub(crate) ior_for_voxel: HashMap, + pub(crate) mesh_outer_faces: bool, } impl Default for VoxelData { diff --git a/src/model/queryable.rs b/src/model/queryable.rs index 86f6201..1d7cc2a 100644 --- a/src/model/queryable.rs +++ b/src/model/queryable.rs @@ -1,4 +1,4 @@ -use super::{Voxel, VoxelData, VoxelModel}; +use super::{RawVoxel, Voxel, VoxelData, VoxelModel}; use bevy::{ math::{BVec3, IVec3, UVec3, Vec3}, transform::components::GlobalTransform, @@ -93,6 +93,30 @@ impl VoxelQueryable for VoxelData { Some(voxel) } } + +#[derive(Debug, Clone)] +pub struct OutOfBoundsError; + +impl VoxelData { + /// Writes a voxel to a point in the model + /// + /// ### Arguments + /// * `voxel` - the [`Voxel`] to be written + /// * `point` - the position at which the voxel will be written + /// + /// ### Returns + /// `Ok(())` if the operation was successful, or [`OutOfBoundsError`] if `point` lies outside the model + pub fn set_voxel(&mut self, voxel: Voxel, point: Vec3) -> Result<(), OutOfBoundsError> { + let position = self + .point_in_model(point.as_ivec3()) + .ok_or(OutOfBoundsError)?; + let leading_padding = UVec3::splat(self.padding() / 2); + let index = self.shape.linearize((position + leading_padding).into()) as usize; + let raw_voxel: RawVoxel = voxel.into(); + self.voxels[index] = raw_voxel; + Ok(()) + } +} trait BitwiseComparable { fn less_than(&self, other: Self) -> BVec3; diff --git a/src/scene/mod.rs b/src/scene/mod.rs index f0efeb5..087486f 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -18,7 +18,7 @@ pub use hook::VoxelSceneHook; /// The root of the spawned scene will be the entity that has this bundle. /// In addition to the standard components bevy uses to organise and render pbr meshes, /// spawned entities will also have [`VoxelLayer`] and [`VoxelModelInstance`] components added. -/// ```no_run +/// ``` /// # use bevy::prelude::*; /// # use bevy_vox_scene::VoxelSceneBundle; /// diff --git a/src/scene/systems.rs b/src/scene/systems.rs index 61df0f2..5bb5560 100644 --- a/src/scene/systems.rs +++ b/src/scene/systems.rs @@ -56,13 +56,14 @@ fn spawn_voxel_node_recursive( } if let Some(model_handle) = &voxel_node.model_id.and_then(|id| scene.models.get(id)) { if let Some(model) = vox_models.get(*model_handle) { - entity_commands.insert(VoxelModelInstance((**model_handle).clone())); - #[cfg(not(test))] - entity_commands.insert(PbrBundle { - mesh: model.mesh.clone(), - material: model.material.clone(), - ..Default::default() - }); + entity_commands.insert(( + VoxelModelInstance((**model_handle).clone()), + PbrBundle { + mesh: model.mesh.clone(), + material: model.material.clone(), + ..Default::default() + }, + )); } else { warn!("Model not found, omitting: {:?}", model_handle); entity_commands.insert(SpatialBundle::default());