1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
mod hook;
pub(super) mod systems;

use bevy::{
    asset::{Asset, Handle},
    ecs::{bundle::Bundle, component::Component},
    math::Mat4,
    reflect::TypePath,
    render::view::Visibility,
    transform::components::Transform,
};

use crate::VoxelModelCollection;
pub use hook::VoxelSceneHook;

/// A component bundle for spawning Voxel Scenes.
///
/// 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.
/// ```
/// # 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.
/// In addition to the standard components bevy uses to organise and render pbr meshes,
/// spawned entities will also have [`VoxelLayer`] and [`VoxelModelInstance`] components added.
/// The [`VoxelSceneHook`] allows you to modify entities spawned within the hierarchy.
/// A typical use-case would be adding additional components based on an entity's [`bevy::core::Name`]
/// or [`VoxelLayer`].
/// ```
/// # use bevy::{prelude::*, app::AppExit, utils::HashSet};
/// # use bevy_vox_scene::{VoxelSceneHook, VoxelSceneHookBundle};
/// #
/// # #[derive(Component)]
/// # struct Fish;
/// #
/// # fn setup(
/// #     mut commands: Commands,
/// #     assets: Res<AssetServer>,
/// # ) {
/// VoxelSceneHookBundle {
///     scene: assets.load("study.vox#tank"),
///     hook: VoxelSceneHook::new(move |entity, commands| {
///         let Some(name) = entity.get::<Name>() else { return };
///         match name.as_str() {
///             "tank/goldfish" | "tank/tetra" => {
///                 commands.insert(Fish);
///             }
///             _ => {},
///         }
///     }),
///     ..default()
/// };
/// # }
/// ```
#[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>,
    pub(crate) model_collection: Handle<VoxelModelCollection>,
}

#[derive(Debug, Clone, Default)]
pub(crate) struct VoxelNode {
    pub name: Option<String>,
    pub transform: Mat4,
    pub children: Vec<VoxelNode>,
    pub model_id: Option<usize>,
    pub is_hidden: bool,
    pub layer_id: u32,
}

#[derive(Debug, Clone)]
pub(crate) struct LayerInfo {
    pub name: Option<String>,
    pub is_hidden: bool,
}

/// Component wrapping the handle to the [`VoxelModel`]
///
/// When the scene is spawned this component gets added to entities with a voxel mesh.
#[derive(Component, Clone)]
pub struct VoxelModelInstance {
    /// Handle to the collection that this model is instanced from
    pub collection: Handle<VoxelModelCollection>,
    /// The name of the model within the collection
    pub model_name: String,
}

/// 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>,
}