Skip to content

Commit

Permalink
Alternative solution for editor crash (#201)
Browse files Browse the repository at this point in the history
* Creates default images for assets
* Update meshless_visualizer.rs
* extra-tests
---------

Co-authored-by: rewin <[email protected]>
  • Loading branch information
naomijub and rewin123 authored Feb 27, 2024
1 parent a2d3951 commit 0a84c16
Show file tree
Hide file tree
Showing 8 changed files with 1,325 additions and 41 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ convert_case = "0.6"
egui_dock = "0.11"
egui_extras = { version = "0.26", features = ["all_loaders"] }
egui_file = "0.15"
image = {version = "0.24.8", feature = ["png"] }
egui-gizmo = "0.16.1"

pretty-type-name = "1"
ron = "0.8"
resvg = "0.37"
serde = "1"

# Community Modules
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ The following explains how to integrate `space_editor` as a game plugin to use t

Add this line to your Cargo.toml file
```toml
space_editor = "0.3.1"
space_editor = "0.4.0"

# For now it is recomendended to use the following patches of the libraries we are using
[patch.crates-io]
egui-gizmo = { git = "https://github.com/naomijub/egui-gizmo.git" }
bevy-inspector-egui ={ git = "https://github.com/naomijub/bevy-inspector-egui.git" }
```

### Prefab spawn system
Expand Down Expand Up @@ -142,6 +147,10 @@ Game mode can be changed between 3D and 2D in `settings > GameMode`. This change
- [Extended Documentation](docs/README.md)
- [Shortcuts/Hotkeys Configuration](docs/shortcuts.md)

|bevy|space_editor crates|
|---|---|
|0.12| 0.3 - 0.4|

### Contributing
Any request for adding functionality to the editor is welcome. Open an issue on the [issue tracker](https://github.com/rewin123/space_editor/issues).
Any pull request is welcome too:)
Expand Down
File renamed without changes
134 changes: 95 additions & 39 deletions crates/editor_ui/src/meshless_visualizer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use anyhow::anyhow;
use bevy::{prelude::*, render::view::RenderLayers, utils::HashMap};
use bevy_asset_loader::{
asset_collection::AssetCollection,
Expand All @@ -23,21 +24,34 @@ pub struct MeshlessVisualizerPlugin;

impl Plugin for MeshlessVisualizerPlugin {
fn build(&self, app: &mut App) {
app.add_loading_state(
LoadingState::new(EditorState::Loading)
.continue_to_state(EditorState::Editor)
.load_collection::<EditorIconAssets>()
.register_dynamic_asset_collection::<EditorIconAssetCollection>()
.with_dynamic_assets_file::<EditorIconAssetCollection>("icons/editor.icons.ron"),
)
if std::fs::metadata("assets/icons/").is_ok() {
app.add_loading_state(
LoadingState::new(EditorState::Loading)
.continue_to_state(EditorState::Editor)
.load_collection::<EditorIconAssets>()
.register_dynamic_asset_collection::<EditorIconAssetCollection>()
.with_dynamic_assets_file::<EditorIconAssetCollection>(
"icons/editor.icons.ron",
),
)
.add_plugins(RonAssetPlugin::<EditorIconAssetCollection>::new(&[
"icons.ron",
]))
} else {
error!("Failed to dynamic load assets. Loading defaults from memory");
app.add_systems(OnEnter(EditorState::Editor), register_assets)
.add_systems(
Startup,
|mut next_editor_state: ResMut<NextState<EditorState>>| {
next_editor_state.set(EditorState::Editor);
},
)
}
.insert_resource(RaycastBackendSettings {
raycast_visibility: RaycastVisibility::Ignore,
..Default::default()
})
.add_plugins(BillboardPlugin)
.add_plugins(RonAssetPlugin::<EditorIconAssetCollection>::new(&[
"icons.ron",
]))
.add_systems(
Update,
(visualize_meshless, visualize_custom_meshless).in_set(EditorSet::Editor),
Expand Down Expand Up @@ -104,6 +118,46 @@ pub struct EditorIconAssets {
pub sphere: Handle<Mesh>,
}

fn register_assets(mut commands: Commands, asset_server: Res<AssetServer>) {
use space_shared::asset_fs::*;
let assets = EditorIconAssets {
unknown: asset_server.add(
create_unknown_image()
.inspect_err(|err| error!("failed to load image `Unknown`: {err}"))
.unwrap(),
),
directional: asset_server.add(
create_dir_light_image()
.inspect_err(|err| error!("failed to load image `DirectionalLight`: {err}"))
.unwrap(),
),
point: asset_server.add(
create_point_light_image()
.inspect_err(|err| error!("failed to load image `PointLight`: {err}"))
.unwrap(),
),
spot: asset_server.add(
create_spot_light_image()
.inspect_err(|err| error!("failed to load image `SpotLight`: {err}"))
.unwrap(),
),
camera: asset_server.add(
create_camera_image()
.inspect_err(|err| error!("failed to load image `Camera`: {err}"))
.unwrap(),
),
square: asset_server.add(shape::Quad::new(Vec2::splat(2.)).into()),
sphere: asset_server.add(
Mesh::try_from(shape::Icosphere {
radius: 0.75,
..default()
})
.unwrap(),
),
};
commands.insert_resource(assets);
}

#[derive(serde::Deserialize, Asset, TypePath)]
pub struct EditorIconAssetCollection(HashMap<String, EditorIconAssetType>);

Expand All @@ -114,6 +168,7 @@ impl DynamicAssetCollection for EditorIconAssetCollection {
}
}
}

/// Supported types of icons within the editor to be loaded in
#[derive(serde::Deserialize, Debug, Clone)]
enum EditorIconAssetType {
Expand All @@ -139,7 +194,7 @@ impl DynamicAsset for EditorIconAssetType {
let cell = world.cell();
let asset_server = cell
.get_resource::<AssetServer>()
.expect("Failed to get the AssetServer");
.ok_or_else(|| anyhow!("Failed to get the AssetServer"))?;
match self {
Self::Image { path } => {
let handle = asset_server.load::<Image>(path);
Expand All @@ -148,7 +203,7 @@ impl DynamicAsset for EditorIconAssetType {
Self::Quad { size } => {
let mut meshes = cell
.get_resource_mut::<Assets<Mesh>>()
.expect("Failed to get Mesh Assets");
.ok_or_else(|| anyhow!("Failed to get Mesh Assets"))?;
let handle = meshes
.add(Mesh::from(shape::Quad {
size: *size,
Expand All @@ -160,7 +215,7 @@ impl DynamicAsset for EditorIconAssetType {
Self::Sphere { radius } => {
let mut meshes = cell
.get_resource_mut::<Assets<Mesh>>()
.expect("Failed to get Mesh Assets");
.ok_or_else(|| anyhow!("Failed to get Mesh Assets"))?;
let handle = meshes
.add(
Mesh::try_from(shape::Icosphere {
Expand Down Expand Up @@ -280,7 +335,7 @@ pub fn visualize_meshless(
/// Additionally, the user can either choose their own mesh and material to use or default to the white sphere
pub fn visualize_custom_meshless(
mut commands: Commands,
ass: Res<AssetServer>,
asset_server: Res<AssetServer>,
objects: Query<(Entity, &CustomMeshless, Option<&Children>)>,
editor_icons: Res<EditorIconAssets>,
visualized: Query<&BillboardMeshHandle>,
Expand All @@ -295,38 +350,39 @@ pub fn visualize_custom_meshless(
MeshlessModel::Billboard {
ref mesh,
ref texture,
} => commands
.spawn((
BillboardTextureBundle {
mesh: BillboardMeshHandle(mesh.clone().unwrap_or_else(|| {
ass.add(shape::Quad::new(Vec2::splat(2.)).into())
})),
texture: BillboardTextureHandle(
texture
.clone()
.unwrap_or_else(|| ass.load("icons/unknown.png")),
),
..default()
},
RenderLayers::layer(LAST_RENDER_LAYER),
))
.with_children(|adult| {
adult.spawn((
MaterialMeshBundle::<StandardMaterial> {
mesh: editor_icons.sphere.clone(),
visibility: Visibility::Hidden,
} => {
let Some(texture) = texture.clone() else {
return;
};
commands
.spawn((
BillboardTextureBundle {
mesh: BillboardMeshHandle(mesh.clone().unwrap_or_else(|| {
asset_server.add(shape::Quad::new(Vec2::splat(2.)).into())
})),
texture: BillboardTextureHandle(texture),
..default()
},
SelectParent { parent: entity },
));
})
.id(),
RenderLayers::layer(LAST_RENDER_LAYER),
))
.with_children(|adult| {
adult.spawn((
MaterialMeshBundle::<StandardMaterial> {
mesh: editor_icons.sphere.clone(),
visibility: Visibility::Hidden,
..default()
},
SelectParent { parent: entity },
));
})
.id()
}
MeshlessModel::Object { mesh, material } => commands
.spawn((
MaterialMeshBundle {
mesh: mesh.clone().unwrap_or_else(|| editor_icons.sphere.clone()),
material: material.clone().unwrap_or_else(|| {
ass.add(StandardMaterial {
asset_server.add(StandardMaterial {
unlit: true,
..default()
})
Expand Down
2 changes: 2 additions & 0 deletions crates/shared/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ homepage.workspace = true
[dependencies]
bevy.workspace = true

anyhow.workspace = true
bevy-inspector-egui.workspace = true
egui_file.workspace = true
image.workspace = true

[lints]
workspace = true
86 changes: 86 additions & 0 deletions crates/shared/src/asset_fs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use anyhow::Result;
use bevy::prelude::*;
use image;

use crate::gizmos::{
CAMERA_GIZMO, DIRECTION_LIGHT_GIZMO, POINT_LIGHT_GIZMO, SPOT_LIGHT_GIZMO, UNKNOWN,
};

pub fn create_camera_image() -> Result<Image> {
let image = image::load_from_memory_with_format(CAMERA_GIZMO, image::ImageFormat::Png)?;
let image = Image::from_dynamic(image, false);
Ok(image)
}

pub fn create_unknown_image() -> Result<Image> {
let image = image::load_from_memory_with_format(UNKNOWN, image::ImageFormat::Png)?;
let image = Image::from_dynamic(image, false);
Ok(image)
}

pub fn create_dir_light_image() -> Result<Image> {
let image =
image::load_from_memory_with_format(DIRECTION_LIGHT_GIZMO, image::ImageFormat::Png)?;
let image = Image::from_dynamic(image, false);
Ok(image)
}
pub fn create_spot_light_image() -> Result<Image> {
let image = image::load_from_memory_with_format(SPOT_LIGHT_GIZMO, image::ImageFormat::Png)?;
let image = Image::from_dynamic(image, false);
Ok(image)
}
pub fn create_point_light_image() -> Result<Image> {
let image = image::load_from_memory_with_format(POINT_LIGHT_GIZMO, image::ImageFormat::Png)?;
let image = Image::from_dynamic(image, false);
Ok(image)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn point_light_image() {
let image = create_point_light_image();

assert!(image.is_ok());
let image = image.unwrap();
assert_eq!(image.size(), UVec2::new(128, 128));
}

#[test]
fn spot_light_image() {
let image = create_spot_light_image();

assert!(image.is_ok());
let image = image.unwrap();
assert_eq!(image.size(), UVec2::new(128, 128));
}

#[test]
fn dir_light_image() {
let image = create_dir_light_image();

assert!(image.is_ok());
let image = image.unwrap();
assert_eq!(image.size(), UVec2::new(120, 120));
}

#[test]
fn camera_image() {
let image = create_camera_image();

assert!(image.is_ok());
let image = image.unwrap();
assert_eq!(image.size(), UVec2::new(128, 128));
}

#[test]
fn unknown_image() {
let image = create_unknown_image();

assert!(image.is_ok());
let image = image.unwrap();
assert_eq!(image.size(), UVec2::new(128, 128));
}
}
Loading

0 comments on commit 0a84c16

Please sign in to comment.