Loading Entities: Making progress, but still not fully implemented

attempt/001
Elijah C. Voigt 1 year ago
parent 21e67e04ca
commit 8f3472c4cd

@ -0,0 +1,6 @@
uuid 739619d7-b8a1-42b4-8803-2b8cfedeebf1
name "Editor Camera"
editor_tag
camera render_target window b90f31a2-34d9-42c2-9e53-646d9c9c6c70
fly_camera
transform transform 10.0 10.0 10.0

@ -0,0 +1,4 @@
uuid 08e1a4d9-6c76-471d-af02-1aa7db1b435a
editor_tag
ui_node
target_camera 739619d7-b8a1-42b4-8803-2b8cfedeebf1

@ -0,0 +1,4 @@
uuid 0088bc7d-851e-402b-9d20-fadfe5cab106
editor_tag
ui_text text "Welcome to the editor" color 1.0 1.0 1.0 1.0
parent 08e1a4d9-6c76-471d-af02-1aa7db1b435a

@ -0,0 +1,4 @@
uuid b90f31a2-34d9-42c2-9e53-646d9c9c6c70
editor_tag
name "Editor Window"
window title "Editor" visible false

@ -1,5 +1,3 @@
use bevy::render::primitives::Aabb;
use crate::prelude::*; use crate::prelude::*;
pub(crate) struct EditorPlugin; pub(crate) struct EditorPlugin;
@ -7,18 +5,12 @@ pub(crate) struct EditorPlugin;
impl Plugin for EditorPlugin { impl Plugin for EditorPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_state::<EditorState>() app.init_state::<EditorState>()
.add_systems(Startup, init_editor) .add_systems(OnEnter(EditorState::Open), spawn_editor)
.add_systems(OnExit(EditorState::Open), despawn_editor)
.add_systems( .add_systems(
Update, Update,
toggle_editor_state.run_if(input_just_pressed(KeyCode::F3)), toggle_editor_state.run_if(input_just_pressed(KeyCode::F3)),
) )
.add_systems(
Update,
(
toggle_editor_window,
toggle_entity_aabb,
).run_if(state_changed::<EditorState>),
)
.add_systems( .add_systems(
Update, Update,
( (
@ -64,57 +56,6 @@ impl std::convert::From<&EditorState> for bool {
#[derive(Component)] #[derive(Component)]
struct EditorTag; struct EditorTag;
/// At startup initialize some editor components
fn init_editor(mut commands: Commands) {
// Editor window
let editor_window = commands
.spawn((
EditorTag,
Window {
visible: false,
title: "Editor".into(),
..default()
},
))
.id();
// Editor camera
let editor_camera = commands
.spawn((
EditorTag,
FlyCamera,
Camera3dBundle {
camera: Camera {
target: RenderTarget::Window(WindowRef::Entity(editor_window)),
..default()
},
transform: Transform::from_xyz(10.0, 10.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
},
))
.id();
// Editor UI elements
commands
.spawn((
EditorTag,
TargetCamera(editor_camera),
NodeBundle { ..default() },
))
.with_children(|parent| {
parent.spawn((
EditorTag,
TextBundle::from_section(
"Welcome to the editor",
TextStyle {
color: WHITE.into(),
..default()
},
),
));
});
}
fn toggle_editor_state( fn toggle_editor_state(
curr_state: Res<State<EditorState>>, curr_state: Res<State<EditorState>>,
mut next_state: ResMut<NextState<EditorState>>, mut next_state: ResMut<NextState<EditorState>>,
@ -132,18 +73,16 @@ fn toggle_editor_state(
} }
} }
fn toggle_editor_window( fn spawn_editor(
state: Res<State<EditorState>>, mut _commands: Commands,
mut windows: Query<&mut Window, With<EditorTag>>,
mut cameras: Query<&mut Camera, With<EditorTag>>,
) { ) {
let curr = state.get().into(); todo!("Spawn editor");
cameras.iter_mut().for_each(|mut camera| { }
camera.is_active = curr;
}); fn despawn_editor(
windows.iter_mut().for_each(|mut window| { mut _comands: Commands,
window.visible = curr; ) {
}) todo!("Despawn editor");
} }
/// Handles window close requests which are only weird because of the editor /// Handles window close requests which are only weird because of the editor
@ -184,44 +123,4 @@ fn plane_gizmos(mut gizmos: Gizmos) {
gizmos.arrow(Vec3::ZERO, Vec3::Y, DARK_GREEN); gizmos.arrow(Vec3::ZERO, Vec3::Y, DARK_GREEN);
gizmos.arrow(Vec3::ZERO, Vec3::Z, BLUE); gizmos.arrow(Vec3::ZERO, Vec3::Z, BLUE);
} }
}
fn toggle_entity_aabb(
state: Res<State<EditorState>>,
query: Query<Entity, With<Handle<SaveEntity>>>,
children: Query<&Children>,
aabbs: Query<(Entity, &Aabb)>,
mut commands: Commands,
) {
let add_component = match state.get() {
EditorState::Open => true,
EditorState::Closed => false,
};
query.iter().for_each(|root| {
if add_component {
commands.entity(root).insert(ShowAabbGizmo { ..default() });
} else {
commands.entity(root).remove::<ShowAabbGizmo>();
}
let mut child_has_bounding_box = false;
for child in children.iter_descendants(root) {
if add_component {
commands.entity(child).insert(ShowAabbGizmo { ..default() });
} else {
commands.entity(child).remove::<ShowAabbGizmo>();
}
if aabbs.contains(child) {
commands.entity(child).insert(Aabb { center: Vec3A::ZERO, half_extents: Vec3A::ONE });
} else {
child_has_bounding_box = true;
}
}
if !aabbs.contains(root) && !child_has_bounding_box {
commands.entity(root).insert(Aabb { center: Vec3A::ZERO, half_extents: Vec3A::ONE });
} else {
commands.entity(root).remove::<Aabb>();
}
})
} }

@ -1,14 +1,15 @@
pub(crate) use bevy::{ pub(crate) use bevy::{
app::AppExit, app::AppExit,
asset::{io::Reader, AssetLoader, LoadContext, AsyncReadExt}, asset::{io::Reader, AssetLoader, LoadContext, AsyncReadExt},
color::palettes::css::{WHITE, GRAY, RED, DARK_GREEN, BLUE}, color::palettes::css::{GRAY, RED, DARK_GREEN, BLUE},
gltf::Gltf, gltf::Gltf,
input::common_conditions::input_just_pressed, input::common_conditions::input_just_pressed,
math::Vec3A,
prelude::*, prelude::*,
render::camera::RenderTarget, render::camera::RenderTarget,
window::{WindowRef, WindowCloseRequested, PrimaryWindow}, window::{WindowRef, WindowCloseRequested, PrimaryWindow},
}; };
pub(crate) use bevy::ecs::reflect::ReflectCommandExt;
pub(crate) use nom::bytes::complete::take;
pub(crate) use nom::{ pub(crate) use nom::{
IResult, IResult,
bytes::complete::{tag, take_until1}, bytes::complete::{tag, take_until1},
@ -20,7 +21,5 @@ pub(crate) use uuid::Uuid;
pub(crate) use thiserror::Error; pub(crate) use thiserror::Error;
pub(crate) use crate::{ pub(crate) use crate::{
camera::*,
conditions::*, conditions::*,
save::*,
}; };

@ -5,7 +5,11 @@ pub(crate) struct SavePlugin;
impl Plugin for SavePlugin { impl Plugin for SavePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_asset::<SaveEntity>() app
.register_type::<parse::EntityUuid>()
.register_type::<parse::SaveCameraRenderTarget>()
.register_type::<GltfScene>()
.init_asset::<SaveEntity>()
.init_asset_loader::<SaveEntityLoader>() .init_asset_loader::<SaveEntityLoader>()
.add_systems(Startup, test_save_entity) .add_systems(Startup, test_save_entity)
.add_systems( .add_systems(
@ -15,17 +19,16 @@ impl Plugin for SavePlugin {
} }
} }
#[derive(Asset, TypePath, Debug, Default)] #[derive(Asset, TypePath, Default)]
pub(crate) struct SaveEntity { pub(crate) struct SaveEntity {
transform: Option<Transform>, components: Vec<Box<dyn Reflect>>,
name: Option<Name>,
uuid: Option<EntityUuid>,
model: Option<(Handle<Gltf>, String)>,
// TODO: Option<bool> feels like an antipattern as this is either Some(true) or None and never Some(false).
// Either encode more Camera data in this or replace it with Option<()> or just bool.
camera: Option<bool>,
} }
// TODO: SCALABILITY: SaveEntity should maybe be a HashSet or Vec of Box<into Bundle>
// Then we tell the parse "For each line, run through each of these concrete parsers"
// If it matches, add it to the set of Box<Bundle>
// Ironically we basically want DynamicEntity: https://docs.rs/bevy/latest/bevy/scene/struct.DynamicEntity.html
impl SaveEntity { impl SaveEntity {
fn parse( fn parse(
text: &str, text: &str,
@ -34,25 +37,32 @@ impl SaveEntity {
let lines = text.split('\n'); let lines = text.split('\n');
let mut entity = SaveEntity { ..default() }; let mut entity = SaveEntity { ..default() };
lines.into_iter().for_each(|line| { lines.into_iter().for_each(|line| {
if let Ok(name) = parse::parse_save_name(line) { if let Ok(name) = parse::parse_save_name(line) {
entity.name = Some(name); entity.components.push(name.clone_value());
} else if let Ok(transform) = parse::parse_save_transform(line) { } else if let Ok(transform) = parse::parse_save_transform(line) {
entity.transform = Some(transform); entity.components.push(transform.clone_value());
} else if let Ok(uuid) = parse::parse_save_uuid(line) { } else if let Ok(uuid) = parse::parse_save_uuid(line) {
entity.uuid = Some(EntityUuid(uuid)); entity.components.push(uuid.clone_value());
} else if let Ok((gltf_path, scene_name)) = parse::parse_save_model(line) { } else if let Ok((gltf_file, scene_name)) = parse::parse_save_model(line) {
let handle = load_context.load(gltf_path); let gltf_scene = GltfScene { gltf: load_context.load(gltf_file), name: scene_name };
entity.model = Some((handle, scene_name)); entity.components.push(gltf_scene.clone_value());
} else if let Ok(true) = parse::parse_save_camera(line) { } else if let Ok(target) = parse::parse_save_camera(line) {
entity.camera = Some(true); entity.components.push(target.clone_value());
} else {
error!("Failed to parse line component `{:?}`", line);
} }
}); });
Ok(entity) Ok(entity)
} }
} }
#[derive(Component, Clone, Debug)] #[derive(Component, Clone, Debug, Reflect, PartialEq)]
struct EntityUuid(Uuid); struct GltfScene {
gltf: Handle<Gltf>,
name: String,
}
mod parse { mod parse {
use super::*; use super::*;
@ -67,6 +77,8 @@ mod parse {
Transform, Transform,
#[error("Failed to parse Entity")] #[error("Failed to parse Entity")]
Nom(nom::Err<nom::error::Error<Box<str>>>), Nom(nom::Err<nom::error::Error<Box<str>>>),
#[error("Failed to parse camera")]
Camera,
} }
// Convert Nom error to parse error // Convert Nom error to parse error
@ -107,12 +119,6 @@ mod parse {
} }
/// ///
/// ```
/// let parsed = parse_save_transform("transform translation 1.0 2.0 3.0 rotation 1.0 0.0 0.0 0.0 scale 1.0 1.0 1.0").unwrap();
/// let expected = Transform { translation: Vec3::new(1.0, 1.0, 1.0), rotation: Quat::from_xzyw(1.0, 0.0, 0.0, 0.0), scale: Vec3::ONE };
///
/// assert_eq!(parsed, expected);
/// ```
/// ///
pub(crate) fn parse_save_transform(line: &str) -> Result<Transform, SaveEntityParseError> { pub(crate) fn parse_save_transform(line: &str) -> Result<Transform, SaveEntityParseError> {
let (rem, _) = tag("transform")(line)?; let (rem, _) = tag("transform")(line)?;
@ -121,7 +127,6 @@ mod parse {
let mut curr = rem.trim_start(); let mut curr = rem.trim_start();
for _ in 0..3 { for _ in 0..3 {
println!("Curr: {:?}", curr);
if let Ok((rem, (_, _, (x, y, z)))) = tuple((parse_word("translation"), space1, parse_xyz))(curr.trim_start()) { if let Ok((rem, (_, _, (x, y, z)))) = tuple((parse_word("translation"), space1, parse_xyz))(curr.trim_start()) {
transform.translation = Vec3::new(x, y, z); transform.translation = Vec3::new(x, y, z);
curr = rem.trim_start(); curr = rem.trim_start();
@ -156,12 +161,6 @@ mod parse {
} }
/// ///
/// ```
/// let parsed = parse_save_name("name asfd").unwrap();
/// let expected = Name::new("asdf");
///
/// assert_eq!(parsed, expected);
/// ```
/// ///
pub(crate) fn parse_save_name(line: &str) -> Result<Name, SaveEntityParseError> { pub(crate) fn parse_save_name(line: &str) -> Result<Name, SaveEntityParseError> {
let (remainder, _) = tag("name")(line)?; let (remainder, _) = tag("name")(line)?;
@ -192,31 +191,31 @@ mod parse {
} }
} }
#[derive(Component, Clone, Debug, Reflect, PartialEq)]
pub(crate) struct EntityUuid(Uuid);
/// ///
/// ```
/// let parsed = parse_save_uuid("uuid 339dd18f-761a-4997-b515-ef57f4dc2447").unwrap();
/// let expected = Uuid::parse_str("339dd18f-761a-4997-b515-ef57f4dc2447").unwrap();
///
/// assert_eq!(parsed, expected);
/// ```
/// ///
pub(crate) fn parse_save_uuid(line: &str) -> Result<Uuid, SaveEntityParseError> { pub(crate) fn parse_save_uuid(line: &str) -> Result<EntityUuid, SaveEntityParseError> {
let (remainder, _) = tag("uuid")(line)?; let (remainder, _) = tag("uuid")(line)?;
let uuid = Uuid::try_parse(remainder.trim())?; let uuid = Uuid::try_parse(remainder.trim())?;
Ok(uuid) Ok(EntityUuid(uuid))
} }
#[test] #[test]
fn test_parse_uuid() { fn test_parse_uuid() {
let line = "uuid 1c16ab9a-5f79-4340-8469-4086f69c64f2"; let line = "uuid 1c16ab9a-5f79-4340-8469-4086f69c64f2";
let parsed = parse_save_uuid(line).unwrap(); let parsed = parse_save_uuid(line).unwrap();
let expected = Uuid::parse_str("1c16ab9a-5f79-4340-8469-4086f69c64f2").unwrap(); let expected = EntityUuid(Uuid::parse_str("1c16ab9a-5f79-4340-8469-4086f69c64f2").unwrap());
assert_eq!(parsed, expected); assert_eq!(parsed, expected);
} }
///
///
pub(crate) fn parse_save_model(line: &str) -> Result<(String, String), SaveEntityParseError> { pub(crate) fn parse_save_model(line: &str) -> Result<(String, String), SaveEntityParseError> {
let (rem, (_, _, gltf_name, _, scene_name)) = tuple(( let (rem, (_, _, gltf_path, _, scene_name)) = tuple((
tag("model"), tag("model"),
space1, space1,
parse_string, parse_string,
@ -224,9 +223,9 @@ mod parse {
parse_string, parse_string,
))(line)?; ))(line)?;
debug_assert_eq!(rem, ""); debug_assert!(rem == "");
Ok((String::from(gltf_name), String::from(scene_name))) Ok((gltf_path.into(), scene_name.into()))
} }
#[test] #[test]
@ -238,9 +237,45 @@ mod parse {
assert_eq!(parsed, expected); assert_eq!(parsed, expected);
} }
pub(crate) fn parse_save_camera(line: &str) -> Result<bool, SaveEntityParseError> { #[derive(Debug, Default, PartialEq, Reflect)]
let (_rem, cam) = tag("camera")(line)?; pub(crate) enum SaveCameraRenderTarget {
Ok(!cam.is_empty()) #[default]
Default,
Window(Uuid),
}
pub(crate) fn parse_save_camera(line: &str) -> Result<SaveCameraRenderTarget, SaveEntityParseError> {
if let Ok((rem, _camera)) = tag::<&str, &str, ()>("camera")(line) {
if let Ok((rem, (_, _target))) = tuple((space1::<&str, ()>, tag("target")))(rem) {
if let Ok((rem, (_, _window))) = tuple((space1::<&str, ()>, tag("window")))(rem) {
// Camera + target + window + UUID
if let Ok((rem, (_, uuid))) = tuple((space1::<&str, ()>, take(36usize)))(rem) {
debug_assert!(rem == "");
let parsed_uuid = Uuid::parse_str(uuid)?;
Ok(SaveCameraRenderTarget::Window(parsed_uuid))
// Camera + target + widow
} else {
debug_assert!(rem == "");
Ok(SaveCameraRenderTarget::Default)
}
// camera + target
} else {
debug_assert!(rem == "");
Err(SaveEntityParseError::Camera)
}
// Just camera
} else {
debug_assert!(rem == "");
Ok(SaveCameraRenderTarget::Default)
}
// Nothing parsed well
} else {
Err(SaveEntityParseError::Camera)
}
} }
#[test] #[test]
@ -248,7 +283,25 @@ mod parse {
{ {
let line = "camera"; let line = "camera";
let parsed = parse_save_camera(line).unwrap(); let parsed = parse_save_camera(line).unwrap();
let expected = true; let expected = SaveCameraRenderTarget::Default;
assert_eq!(parsed, expected);
}
{
let line = "camera target window";
let parsed = parse_save_camera(line).unwrap();
let expected = SaveCameraRenderTarget::Default;
assert_eq!(parsed, expected);
}
{
let line = "camera target";
let parsed = parse_save_camera(line);
assert!(parsed.is_err());
}
{
let line = "camera target window 9a1367e0-71e5-4c79-b4ad-02aa44b68af0";
let target_uuid = Uuid::parse_str("9a1367e0-71e5-4c79-b4ad-02aa44b68af0").unwrap();
let parsed = parse_save_camera(line).unwrap();
let expected = SaveCameraRenderTarget::Window(target_uuid);
assert_eq!(parsed, expected); assert_eq!(parsed, expected);
} }
{ {
@ -307,7 +360,6 @@ fn check_loaded_entity_assets(
mut events: EventReader<AssetEvent<SaveEntity>>, mut events: EventReader<AssetEvent<SaveEntity>>,
save_entities: Res<Assets<SaveEntity>>, save_entities: Res<Assets<SaveEntity>>,
mut commands: Commands, mut commands: Commands,
gltfs: Res<Assets<Gltf>>,
) { ) {
events.read().for_each(|event| { events.read().for_each(|event| {
if let AssetEvent::LoadedWithDependencies { id } = event { if let AssetEvent::LoadedWithDependencies { id } = event {
@ -316,51 +368,11 @@ fn check_loaded_entity_assets(
.filter(|(_, handle)| handle.id() == *id) .filter(|(_, handle)| handle.id() == *id)
.for_each(|(entity, _handle)| { .for_each(|(entity, _handle)| {
let saved = save_entities.get(*id).unwrap(); let saved = save_entities.get(*id).unwrap();
debug!(
"Updating entity {:?} ({:?}) because asset changed",
saved.name, saved.uuid
);
let mut e = commands.entity(entity); let mut e = commands.entity(entity);
// Apply camera saved.components.iter().for_each(|dc| {
// Should be applied early to avoid clobbering transform e.insert_reflect(dc.clone_value());
if let Some(is_camera) = &saved.camera { });
if *is_camera {
e.insert(Camera3dBundle { ..default() });
}
} else {
e.remove::<(Camera, Camera3d)>();
}
// Apply transform
if let Some(transform) = &saved.transform {
// TODO: Only update if different
e.insert(*transform);
} else {
e.remove::<Transform>();
}
// Apply Name
if let Some(name) = &saved.name {
// TODO: Only update if different
e.insert(name.clone());
} else {
e.remove::<Name>();
}
// Apply Uuid
if let Some(uuid) = &saved.uuid {
// TODO: Only update if different
e.insert(uuid.clone());
} else {
e.remove::<EntityUuid>();
}
// Apply Model
if let Some((gltf_handle, scene_name)) = &saved.model {
// Find scene and update
let gltf = gltfs.get(gltf_handle).unwrap();
let scene_handle = gltf.named_scenes.get(scene_name.as_str().into()).unwrap();
e.insert(scene_handle.clone());
} else {
e.remove::<Handle<Scene>>();
}
}); });
} }
}) })

Loading…
Cancel
Save