forked from Game4all/bevy_vox_mesh
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
743 additions
and
728 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
/target | ||
Cargo.lock | ||
.DS_Store | ||
lcov.info | ||
lcov.info | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
use bevy::{ | ||
ecs::{component::Component, system::EntityCommands, world::EntityRef}, | ||
log::warn, | ||
}; | ||
|
||
/// A component containing a closure that will be run against every entity spawned from a Voxel Scene | ||
/// | ||
/// Assign this component to an entity that also has a [Handle<VoxelScene>](VoxelScene) to execute a closure | ||
/// against every entity that gets spawned in the graph of the Voxel Scene. | ||
/// This allows you to specify, before the scene graph has been spawned, how entities at a deeper level | ||
/// than the root should be modified. A common use-case would adding custom components to entities | ||
/// depending on their name or [`VoxelLayer`]. | ||
/// ```rust | ||
/// # use bevy::{prelude::*, app::AppExit, utils::HashSet}; | ||
/// # use bevy_vox_scene::{VoxScenePlugin, VoxelSceneHook, VoxelSceneHookBundle}; | ||
/// # | ||
/// # fn main() { | ||
/// # App::new() | ||
/// # .add_plugins(( | ||
/// # DefaultPlugins, | ||
/// # VoxScenePlugin, | ||
/// # )) | ||
/// # .add_systems(Startup, setup) | ||
/// # .add_systems(Update, assert_scene_loaded) | ||
/// # .run(); | ||
/// # } | ||
/// # | ||
/// #[derive(Component)] | ||
/// struct Fish; | ||
/// | ||
/// fn setup( | ||
/// mut commands: Commands, | ||
/// assets: Res<AssetServer>, | ||
/// ) { | ||
/// commands.spawn(( | ||
/// VoxelSceneHookBundle { | ||
/// scene: assets.load("study.vox#tank"), | ||
/// | ||
/// // This closure will be run against every child Entity that gets spawned in the scene | ||
/// hook: VoxelSceneHook::new(move |entity, commands| { | ||
/// let Some(name) = entity.get::<Name>() else { return }; | ||
/// match name.as_str() { | ||
/// // Node names give the path to the asset, with components separated by /. Here, "goldfish" and "tetra" are two fish types in the "tank" | ||
/// "tank/goldfish" | "tank/tetra" => { | ||
/// // add a marker Component. | ||
/// commands.insert(Fish); | ||
/// } | ||
/// _ => {}, | ||
/// } | ||
/// }), | ||
/// ..default() | ||
/// }, | ||
/// )); | ||
/// } | ||
/// # | ||
/// # fn assert_scene_loaded( | ||
/// # query: Query<&Name, With<Fish>>, | ||
/// # mut exit: EventWriter<AppExit>, | ||
/// # ) { | ||
/// # let all_fish: Vec<&str> = query.iter().map(|n| { n.as_str() }).collect(); | ||
/// # if all_fish.is_empty() { return }; | ||
/// # assert_eq!(all_fish.len(), 5); | ||
/// # let expected_names: HashSet<&str> = ["tank/tetra", "tank/goldfish"].into(); | ||
/// # let all_names: HashSet<&str> = HashSet::from_iter(all_fish); | ||
/// # assert_eq!(expected_names, all_names); | ||
/// # exit.send(AppExit); | ||
/// # } | ||
/// ``` | ||
#[derive(Component)] | ||
pub struct VoxelSceneHook { | ||
pub(crate) hook: Box<dyn Fn(&EntityRef, &mut EntityCommands) + Send + Sync + 'static>, | ||
} | ||
|
||
impl VoxelSceneHook { | ||
/// Create a new hook with the closure `hook`. This will be run against every entity that gets spawned in the scene graph. | ||
pub fn new<F: Fn(&EntityRef, &mut EntityCommands) + Send + Sync + 'static>(hook: F) -> Self { | ||
Self { | ||
hook: Box::new(hook), | ||
} | ||
} | ||
} | ||
|
||
impl Default for VoxelSceneHook { | ||
fn default() -> Self { | ||
Self::new(|_, _| warn!("Default VoxelSceneHook does nothing")) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
mod hook; | ||
pub(crate) mod parse; | ||
pub(super) mod systems; | ||
#[cfg(test)] | ||
mod tests; | ||
use bevy::{ | ||
asset::{Asset, Handle}, | ||
ecs::{bundle::Bundle, component::Component}, | ||
math::Mat4, | ||
pbr::StandardMaterial, | ||
reflect::TypePath, | ||
render::{mesh::Mesh, view::Visibility}, | ||
transform::components::Transform, | ||
}; | ||
pub use hook::VoxelSceneHook; | ||
|
||
use crate::voxel::VoxelData; | ||
|
||
/// A component bundle for spawning Voxel Scenes. | ||
/// | ||
/// The root of the spawned scene will be the entity that has this bundle. | ||
/// ```no_run | ||
/// # use bevy::prelude::*; | ||
/// # use bevy_vox_scene::VoxelSceneBundle; | ||
/// | ||
/// fn setup( | ||
/// mut commands: Commands, | ||
/// assets: Res<AssetServer>, | ||
/// ) { | ||
/// commands.spawn(VoxelSceneBundle { | ||
/// scene: assets.load("study.vox"), | ||
/// ..default() | ||
/// }); | ||
/// | ||
/// commands.spawn(VoxelSceneBundle { | ||
/// // Load a single model using the name assigned to it in MagicaVoxel. | ||
/// // If a model is nested in a named group, than the group will form part of the path | ||
/// // Path components are separated with a slash | ||
/// scene: assets.load("study.vox#workstation/desk"), | ||
/// ..default() | ||
/// }); | ||
/// } | ||
/// ``` | ||
#[derive(Bundle, Default)] | ||
pub struct VoxelSceneBundle { | ||
/// A handle to a [`VoxelScene`], typically loaded from a ".vox" file via the [`bevy::asset::AssetServer`]. | ||
/// This Entity will become the root of the spawned Voxel Scene. | ||
pub scene: Handle<VoxelScene>, | ||
/// The transform of the scene root. This will override whatever the root transform is in the Magica Voxel scene. | ||
pub transform: Transform, | ||
/// The visibility of the scene root. This will override whatever the root visibility is in the Magical Voxel scene. | ||
pub visibility: Visibility, | ||
} | ||
|
||
/// A component bundle for spawning Voxel Scenes, with a [`VoxelSceneHook`]. | ||
/// | ||
/// The root of the spawned scene will be the entity that has this bundle. | ||
/// The [`VoxelSceneHook`] allows you to easily modify Entities deep within the scene hierarchy. | ||
#[derive(Bundle, Default)] | ||
pub struct VoxelSceneHookBundle { | ||
/// A handle to a [`VoxelScene`], typically loaded from a ".vox" file via the [`bevy::asset::AssetServer`]. | ||
/// This Entity will become the root of the spawned Voxel Scene. | ||
pub scene: Handle<VoxelScene>, | ||
/// A [`VoxelSceneHook`] allows you to specify a closure that will be run for each Entity spawned in the scene graph. | ||
pub hook: VoxelSceneHook, | ||
/// The transform of the scene root. This will override whatever the root transform is in the Magica Voxel scene. | ||
pub transform: Transform, | ||
/// The visibility of the scene root. This will override whatever the root visibility is in the Magical Voxel scene. | ||
pub visibility: Visibility, | ||
} | ||
|
||
/// A representation of the Voxel Scene Graph. | ||
/// | ||
/// To spawn a voxel scene, add a [Handle<VoxelScene>](VoxelScene), [`VoxelSceneBundle`], or [`VoxelSceneHookBundle`] to an Entity. | ||
/// Voxel Scenes can be loaded from Magica Voxel .vox files. | ||
#[derive(Asset, TypePath, Debug)] | ||
pub struct VoxelScene { | ||
pub(crate) root: VoxelNode, | ||
pub(crate) layers: Vec<LayerInfo>, | ||
} | ||
|
||
#[derive(Debug, Clone, Default)] | ||
pub(crate) struct VoxelNode { | ||
name: Option<String>, | ||
transform: Mat4, | ||
children: Vec<VoxelNode>, | ||
model: Option<Handle<VoxelModel>>, | ||
is_hidden: bool, | ||
layer_id: u32, | ||
} | ||
|
||
#[derive(Asset, TypePath)] | ||
pub(crate) struct VoxelModel { | ||
pub data: VoxelData, | ||
pub mesh: Handle<Mesh>, | ||
pub material: Handle<StandardMaterial>, | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub(crate) struct LayerInfo { | ||
pub name: Option<String>, | ||
pub is_hidden: bool, | ||
} | ||
|
||
#[derive(Component)] | ||
pub struct VoxelModelInstance(Handle<VoxelModel>); | ||
|
||
/// A component specifying which layer the Entity belongs to, with an optional name. | ||
/// | ||
/// This can be configured in the Magica Voxel world editor. | ||
#[derive(Component, Clone)] | ||
pub struct VoxelLayer { | ||
/// The identifier for the layer. Magic Voxel 0.99.6 allows you to assign nodes to one of 8 layers, | ||
/// so this value will be an index in the range 0 through 7. | ||
pub id: u32, | ||
/// An optional name for the Layer, assignable in Magica Voxel layer editor. | ||
pub name: Option<String>, | ||
} |
Oops, something went wrong.