Compare commits

..

4 Commits

Author SHA1 Message Date
Elijah C. Voigt 5ae1379e3a Adding parsing stuff (parsing the hard way) 1 year ago
Elijah C. Voigt 87ca1b13f6 No parser crate 1 year ago
Elijah C. Voigt 6207a7f032 Wow I should ahve committed a long time ago
Going down a rabbit hole trying to make a parser macro.
Learning how to make a proc macro is... a steep learning curve.
1 year ago
Elijah C. Voigt 8f3472c4cd Loading Entities: Making progress, but still not fully implemented 1 year ago

@ -25,6 +25,10 @@ rustflags = [
[target.x86_64-pc-windows-msvc] [target.x86_64-pc-windows-msvc]
linker = "rust-lld.exe" # Use LLD Linker linker = "rust-lld.exe" # Use LLD Linker
rustflags = [
"-Zshare-generics=n", # This needs to be off if you use dynamic linking on Windows.
"-Zthreads=0"
]
[target.wasm32-unknown-unknown] [target.wasm32-unknown-unknown]
runner = "wasm-server-runner" runner = "wasm-server-runner"

421
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -12,17 +12,17 @@ nom = "7"
# Entity Uuid parsing # Entity Uuid parsing
uuid = "1.7.0" uuid = "1.7.0"
# Enable a small amount of optimization in debug mode
[profile.dev] [profile.dev]
opt-level = 1 opt-level = 1
# Enable high optimizations for dependencies (incl. Bevy), but not for our code:
[profile.dev.package."*"] [profile.dev.package."*"]
opt-level = 3 opt-level = 3
# Prioritize binary size for wasm [profile.release]
codegen-units = 1
lto = "thin"
[profile.wasm-release] [profile.wasm-release]
inherits = "release" inherits = "release"
opt-level = "z" opt-level = "s"
lto = "fat" strip = "debuginfo"
codegen-units = 1

@ -0,0 +1,4 @@
entities/camera.entity
entities/ui_container.entity
entities/ui_title.entity
entities/window.entity

@ -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

@ -0,0 +1,2 @@
entities/camera.entity
entities/van.entity

@ -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>();
}
})
} }

@ -13,6 +13,9 @@ pub(crate) mod save;
/// Window handling /// Window handling
pub(crate) mod window; pub(crate) mod window;
/// Save file parsing
pub(crate) mod parser;
use crate::prelude::*; use crate::prelude::*;
fn main() { fn main() {

@ -0,0 +1,327 @@
use crate::prelude::*;
#[derive(Error, Debug)]
pub(crate) enum SaveEntityParseError {
#[error("Failed to parse Uuid: {0}")]
Uuid(#[from] uuid::Error),
#[error("Failed to parse name")]
Name,
#[error("Failed to parse Transform")]
Transform,
#[error("Failed to parse Entity")]
Nom(nom::Err<nom::error::Error<Box<str>>>),
#[error("Failed to parse camera")]
Camera,
}
// Convert Nom error to parse error
// https://stackoverflow.com/a/77974858/3096574
impl From<nom::Err<nom::error::Error<&str>>> for SaveEntityParseError {
fn from(err: nom::Err<nom::error::Error<&str>>) -> Self {
SaveEntityParseError::Nom(err.map_input(|input| input.into()))
}
}
fn parse_word<'a>(w: &'a str) -> impl Fn(&'a str) -> IResult<&'a str, &'a str> {
move |i: &'a str| tag(w)(i)
}
fn parse_xyz(i: &str) -> IResult<&str, (f32, f32, f32)> {
tuple((float, space1, float, space1, float))(i)
.map(|(s, (x, _, y, _, z))| (s, (x, y, z)))
}
fn parse_wxyz(i: &str) -> IResult<&str, (f32, f32, f32, f32)> {
tuple((
float,
space1,
float,
space1,
float,
space1,
float,
))(i)
.map(|(s, (w, _, x, _, y, _, z))| (s, (w, x, y, z)))
}
fn parse_string(i: &str) -> IResult<&str, &str> {
let (rem, (_, out, _)) = tuple((tag("\""), take_until1("\""), tag("\"")))(i)?;
Ok((rem, out))
}
///
///
pub(crate) fn parse_save_transform(line: &str) -> Result<Transform, SaveEntityParseError> {
let (rem, _) = tag("transform")(line)?;
let mut transform = Transform::default();
let mut curr = rem.trim_start();
for _ in 0..3 {
if let Ok((rem, (_, _, (x, y, z)))) = tuple((parse_word("translation"), space1, parse_xyz))(curr.trim_start()) {
transform.translation = Vec3::new(x, y, z);
curr = rem.trim_start();
} else if let Ok((rem, (_, _, (x, y, z, w)))) = tuple((parse_word("rotation"), space1, parse_wxyz))(curr.trim_start()) {
transform.rotation = Quat::from_xyzw(x, y, z, w);
curr = rem.trim_start();
} else if let Ok((rem, (_, _, (x, y, z)))) = tuple((parse_word("scale"), space1, parse_xyz))(curr.trim_start()) {
transform.scale = Vec3::new(x, y, z);
curr = rem.trim_start();
} else {
return Err(SaveEntityParseError::Transform);
}
}
// Assert there are no trailing characters on the line
debug_assert_eq!(curr, "");
Ok(transform)
}
#[test]
fn test_parse_transform() {
let line = "transform translation 1.0 2.0 3.0 rotation 0.1 0.2 0.3 1.0 scale 1.1 1.2 1.3";
let parsed = parse_save_transform(line).unwrap();
let expected = Transform {
translation: Vec3::new(1.0, 2.0, 3.0),
rotation: Quat::from_xyzw(0.1, 0.2, 0.3, 1.0),
scale: Vec3::new(1.1, 1.2, 1.3),
};
assert_eq!(parsed, expected);
}
///
///
pub(crate) fn parse_save_name(line: &str) -> Result<Name, SaveEntityParseError> {
let (remainder, _) = tag("name")(line)?;
let n = remainder.trim().to_string();
if n.is_empty() {
Err(SaveEntityParseError::Name)
} else {
let name = Name::new(n);
Ok(name)
}
}
#[test]
fn test_parse_name() {
{
let line = "name Van";
let parsed = parse_save_name(line).unwrap();
let expected = Name::new("Van");
assert_eq!(parsed, expected);
}
{
let line = "name Big Mike";
let parsed = parse_save_name(line).unwrap();
let expected = Name::new("Big Mike");
assert_eq!(parsed, expected);
}
}
#[derive(Component, Clone, Debug, Reflect, PartialEq)]
#[reflect(Component)]
pub(crate) struct EntityUuid {
id: String
}
///
///
pub(crate) fn parse_save_uuid(line: &str) -> Result<EntityUuid, SaveEntityParseError> {
let (remainder, _) = tag("uuid")(line)?;
let id = remainder.trim().into();
Ok(EntityUuid { id })
}
#[test]
fn test_parse_uuid() {
let line = "uuid 1c16ab9a-5f79-4340-8469-4086f69c64f2";
let parsed = parse_save_uuid(line).unwrap();
let expected = EntityUuid { id: "1c16ab9a-5f79-4340-8469-4086f69c64f2".into() };
assert_eq!(parsed, expected);
}
///
///
pub(crate) fn parse_save_model(line: &str) -> Result<(String, String), SaveEntityParseError> {
let (rem, (_, _, gltf_path, _, scene_name)) = tuple((
tag("model"),
space1,
parse_string,
space1,
parse_string,
))(line)?;
debug_assert!(rem == "");
Ok((gltf_path.into(), scene_name.into()))
}
#[test]
fn test_parse_model() {
let line = "model \"models/foo.glb\" \"My Scene\"";
let parsed = parse_save_model(line).unwrap();
let expected = (String::from("models/foo.glb"), String::from("My Scene"));
assert_eq!(parsed, expected);
}
#[derive(Component, Debug, Default, PartialEq, Reflect, Clone)]
#[reflect_value(Component, Default)]
pub(crate) enum SaveCameraRenderTarget {
#[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]
fn test_parse_camera() {
{
let line = "camera";
let parsed = parse_save_camera(line).unwrap();
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);
}
{
let line = "notcamera";
let parsed = parse_save_camera(line);
assert!(parsed.is_err());
}
}
/// SaveParent entity which is a reference to which entity this is a child of
/// A run-time system converts this to a bevy Parent component
#[derive(Component, PartialEq, Debug, Reflect, Clone)]
#[reflect_value(Component)]
pub(crate) struct SaveParent(String);
/// Parses a parent entity with this format:
/// ```text
/// parent some_other_file.entity
/// ```
pub(crate) fn parse_save_parent(line: &str) -> Result<SaveParent, SaveEntityParseError> {
todo!()
}
#[test]
fn test_parse_parent() {
let line = "parent some_other_file.entity";
let parsed = parse_save_parent(line).unwrap();
let expected = SaveParent("some_other_file.entity".into());
assert_eq!(parsed, expected);
}
///
/// Parse the Window component (which is very big!)
/// We only sparsely define the bits that we want to edit (for now)
///
pub(crate) fn parse_save_window(line: &str) -> Result<Window, SaveEntityParseError> {
todo!()
}
#[test]
fn test_parse_window() {
let line = "window \"Editor\" visible false";
let parsed = parse_save_window(line).unwrap();
let expected = Window { visible: false, title: "Editor".into(), ..default() };
assert_eq!(parsed.visible, expected.visible);
assert_eq!(parsed.title, expected.title);
}
///
/// The UI Text bundle specified as a sparse subset of a bundle
///
pub(crate) fn parse_save_ui_text(line: &str) -> Result<TextBundle, SaveEntityParseError> {
todo!()
}
#[test]
fn test_save_ui_text() {
todo!()
}
///
/// Returns a parser function for a basic stand-alone tag
/// e.g., "editor_tag" or "ui_node"
pub(crate) fn parse_save_tag<T: Component + Reflect>(tag: &str) -> impl FnOnce(&str) -> Result<T, SaveEntityParseError> {
move |line: &str| -> Result<T, SaveEntityParseError> {
todo!()
}
}
#[test]
fn test_save_tag() {
todo!()
}
#[derive(Component, Clone, Debug, Reflect, PartialEq)]
#[reflect(Component)]
pub(crate) struct SaveTargetCamera(String);
/// Parses the a SaveTargetCamera which at runtime is converted to a TargetCamera
pub(crate) fn parse_save_target_camera(line: &str) -> Result<SaveTargetCamera, SaveEntityParseError> {
todo!()
}
#[test]
fn test_target_camera() {
todo!()
}

@ -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},
@ -19,8 +20,4 @@ pub(crate) use nom::{
pub(crate) use uuid::Uuid; pub(crate) use uuid::Uuid;
pub(crate) use thiserror::Error; pub(crate) use thiserror::Error;
pub(crate) use crate::{ pub(crate) use crate::{conditions::*, parser::*};
camera::*,
conditions::*,
save::*,
};

@ -5,258 +5,72 @@ 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::<EntityUuid>()
.register_type::<SaveCameraRenderTarget>()
.register_type::<GltfScene>()
.init_asset::<SaveEntity>()
.init_asset_loader::<SaveEntityLoader>() .init_asset_loader::<SaveEntityLoader>()
.add_systems(Startup, test_save_entity) .init_asset_loader::<SaveSceneLoader>()
.add_systems(Startup, test_load_entity)
.add_systems( .add_systems(
Update, Update,
check_loaded_entity_assets.run_if(on_event::<AssetEvent<SaveEntity>>()), spawn_loaded_entities.run_if(on_event::<AssetEvent<SaveEntity>>()),
); );
} }
} }
#[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,
load_context: &mut LoadContext, load_context: &mut LoadContext,
) -> Result<SaveEntity, parse::SaveEntityParseError> { ) -> Result<SaveEntity, SaveEntityParseError> {
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) {
entity.name = Some(name); if let Ok(name) = parse_save_name(line) {
} else if let Ok(transform) = parse::parse_save_transform(line) { entity.components.push(name.clone_value());
entity.transform = Some(transform); } else if let Ok(transform) = parse_save_transform(line) {
} else if let Ok(uuid) = parse::parse_save_uuid(line) { entity.components.push(transform.clone_value());
entity.uuid = Some(EntityUuid(uuid)); } else if let Ok(uuid) = parse_save_uuid(line) {
} else if let Ok((gltf_path, scene_name)) = parse::parse_save_model(line) { entity.components.push(uuid.clone_value());
let handle = load_context.load(gltf_path); } else if let Ok((gltf_file, scene_name)) = parse_save_model(line) {
entity.model = Some((handle, scene_name)); let gltf_scene = GltfScene { gltf: load_context.load(gltf_file), name: scene_name };
} else if let Ok(true) = parse::parse_save_camera(line) { entity.components.push(gltf_scene.clone_value());
entity.camera = Some(true); } else if let Ok(target) = parse_save_camera(line) {
entity.components.push(target.clone_value());
} else if let Ok(parent) = parse_save_parent(line) {
entity.components.push(parent.clone_value());
} else if let Ok(window) = parse_save_window(line) {
entity.components.push(window.clone_value());
// } else if let Ok(ui_text_bundle) = parse_save_ui_text(line) {
// entity.components.push(ui_text_bundle.clone_value());
} else if let Ok(target_camera) = parse_save_target_camera(line) {
entity.components.push(target_camera.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); #[reflect(Component)]
struct GltfScene {
mod parse { gltf: Handle<Gltf>,
use super::*; name: String,
#[derive(Error, Debug)]
pub(crate) enum SaveEntityParseError {
#[error("Failed to parse Uuid: {0}")]
Uuid(#[from] uuid::Error),
#[error("Failed to parse name")]
Name,
#[error("Failed to parse Transform")]
Transform,
#[error("Failed to parse Entity")]
Nom(nom::Err<nom::error::Error<Box<str>>>),
}
// Convert Nom error to parse error
// https://stackoverflow.com/a/77974858/3096574
impl From<nom::Err<nom::error::Error<&str>>> for SaveEntityParseError {
fn from(err: nom::Err<nom::error::Error<&str>>) -> Self {
SaveEntityParseError::Nom(err.map_input(|input| input.into()))
}
}
fn parse_word<'a>(w: &'a str) -> impl Fn(&'a str) -> IResult<&'a str, &'a str> {
move |i: &'a str| {
tag(w)(i)
}
}
fn parse_xyz(i: &str) -> IResult<&str, (f32, f32, f32)> {
tuple((float, space1, float, space1, float))(i)
.map(|(s, (x, _, y, _, z))| (s, (x, y, z)))
}
fn parse_wxyz(i: &str) -> IResult<&str, (f32, f32, f32, f32)> {
tuple((
float,
space1,
float,
space1,
float,
space1,
float,
))(i)
.map(|(s, (w, _, x, _, y, _, z))| (s, (w, x, y, z)))
}
fn parse_string(i: &str) -> IResult<&str, &str> {
let (rem, (_, out, _)) = tuple((tag("\""), take_until1("\""), tag("\"")))(i)?;
Ok((rem, out))
}
///
/// ```
/// 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> {
let (rem, _) = tag("transform")(line)?;
let mut transform = Transform::default();
let mut curr = rem.trim_start();
for _ in 0..3 {
println!("Curr: {:?}", curr);
if let Ok((rem, (_, _, (x, y, z)))) = tuple((parse_word("translation"), space1, parse_xyz))(curr.trim_start()) {
transform.translation = Vec3::new(x, y, z);
curr = rem.trim_start();
} else if let Ok((rem, (_, _, (x, y, z, w)))) = tuple((parse_word("rotation"), space1, parse_wxyz))(curr.trim_start()) {
transform.rotation = Quat::from_xyzw(x, y, z, w);
curr = rem.trim_start();
} else if let Ok((rem, (_, _, (x, y, z)))) = tuple((parse_word("scale"), space1, parse_xyz))(curr.trim_start()) {
transform.scale = Vec3::new(x, y, z);
curr = rem.trim_start();
} else {
return Err(SaveEntityParseError::Transform);
}
}
// Assert there are no trailing characters on the line
debug_assert_eq!(curr, "");
Ok(transform)
}
#[test]
fn test_parse_transform() {
let line = "transform translation 1.0 2.0 3.0 rotation 0.1 0.2 0.3 1.0 scale 1.1 1.2 1.3";
let parsed = parse_save_transform(line).unwrap();
let expected = Transform {
translation: Vec3::new(1.0, 2.0, 3.0),
rotation: Quat::from_xyzw(0.1, 0.2, 0.3, 1.0),
scale: Vec3::new(1.1, 1.2, 1.3),
};
assert_eq!(parsed, expected);
}
///
/// ```
/// 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> {
let (remainder, _) = tag("name")(line)?;
let n = remainder.trim().to_string();
if n.is_empty() {
Err(SaveEntityParseError::Name)
} else {
let name = Name::new(n);
Ok(name)
}
}
#[test]
fn test_parse_name() {
{
let line = "name Van";
let parsed = parse_save_name(line).unwrap();
let expected = Name::new("Van");
assert_eq!(parsed, expected);
}
{
let line = "name Big Mike";
let parsed = parse_save_name(line).unwrap();
let expected = Name::new("Big Mike");
assert_eq!(parsed, expected);
}
}
///
/// ```
/// 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> {
let (remainder, _) = tag("uuid")(line)?;
let uuid = Uuid::try_parse(remainder.trim())?;
Ok(uuid)
}
#[test]
fn test_parse_uuid() {
let line = "uuid 1c16ab9a-5f79-4340-8469-4086f69c64f2";
let parsed = parse_save_uuid(line).unwrap();
let expected = Uuid::parse_str("1c16ab9a-5f79-4340-8469-4086f69c64f2").unwrap();
assert_eq!(parsed, expected);
}
pub(crate) fn parse_save_model(line: &str) -> Result<(String, String), SaveEntityParseError> {
let (rem, (_, _, gltf_name, _, scene_name)) = tuple((
tag("model"),
space1,
parse_string,
space1,
parse_string,
))(line)?;
debug_assert_eq!(rem, "");
Ok((String::from(gltf_name), String::from(scene_name)))
}
#[test]
fn test_parse_model() {
let line = "model \"models/foo.glb\" \"My Scene\"";
let parsed = parse_save_model(line).unwrap();
let expected = (String::from("models/foo.glb"), String::from("My Scene"));
assert_eq!(parsed, expected);
}
pub(crate) fn parse_save_camera(line: &str) -> Result<bool, SaveEntityParseError> {
let (_rem, cam) = tag("camera")(line)?;
Ok(!cam.is_empty())
}
#[test]
fn test_parse_camera() {
{
let line = "camera";
let parsed = parse_save_camera(line).unwrap();
let expected = true;
assert_eq!(parsed, expected);
}
{
let line = "notcamera";
let parsed = parse_save_camera(line);
assert!(parsed.is_err());
}
}
} }
#[derive(Default)] #[derive(Default)]
@ -267,7 +81,7 @@ enum SaveEntityLoaderError {
#[error("Could not load asset: {0}")] #[error("Could not load asset: {0}")]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
#[error("Could not parse entity: {0}")] #[error("Could not parse entity: {0}")]
Parse(#[from] parse::SaveEntityParseError), Parse(#[from] SaveEntityParseError),
} }
impl AssetLoader for SaveEntityLoader { impl AssetLoader for SaveEntityLoader {
@ -294,7 +108,44 @@ impl AssetLoader for SaveEntityLoader {
} }
} }
fn test_save_entity(loader: Res<AssetServer>, mut commands: Commands) { #[derive(Default)]
struct SaveSceneLoader;
impl AssetLoader for SaveSceneLoader {
type Asset = DynamicScene;
type Settings = ();
type Error = SaveEntityLoaderError;
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
_settings: &'a (),
load_context: &'a mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
let s = std::str::from_utf8(bytes.as_slice()).unwrap();
let asset_path = load_context.path().to_path_buf();
let parent_dir = asset_path.parent().unwrap();
let _sub_assets: Vec<Handle<SaveEntity>> = s.lines().map(|line| {
parent_dir.join(line)
}).map(|path| {
load_context.load(path)
}).collect();
Ok(DynamicScene::default())
}
fn extensions(&self) -> &[&str] {
&["scene"]
}
}
fn test_load_entity(loader: Res<AssetServer>, mut commands: Commands) {
let _: Handle<DynamicScene> = loader.load("editor/_.scene");
let handle: Handle<SaveEntity> = loader.load("levels/00/entities/van.entity"); let handle: Handle<SaveEntity> = loader.load("levels/00/entities/van.entity");
commands.spawn(( commands.spawn((
SpatialBundle { ..default() }, SpatialBundle { ..default() },
@ -302,12 +153,11 @@ fn test_save_entity(loader: Res<AssetServer>, mut commands: Commands) {
)); ));
} }
fn check_loaded_entity_assets( fn spawn_loaded_entities(
query: Query<(Entity, &Handle<SaveEntity>)>, query: Query<(Entity, &Handle<SaveEntity>)>,
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 +166,15 @@ 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
);
// Get entity with SaveEntity handle
let mut e = commands.entity(entity); let mut e = commands.entity(entity);
// Apply camera // Clear the entity
// Should be applied early to avoid clobbering transform e.despawn_descendants();
if let Some(is_camera) = &saved.camera { // Populate with reflected components
if *is_camera { saved.components.iter().for_each(|dc| {
e.insert(Camera3dBundle { ..default() }); e.insert_reflect(dc.clone_value());
} });
} 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