Compare commits

..

3 Commits

Author SHA1 Message Date
Elijah Voigt 957ec64ee0 Cleaning up some assets and parsing logic 1 year ago
Elijah Voigt 804021debf Work log, remove one billion row challenge 1 year ago
Elijah Voigt 2f9664940d Converted all functions to return Box<dyn Reflect>
this is nice because it makes things a little more uniform.
1 year ago

@ -1,6 +1,5 @@
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
editorTag
camera target window "window.entity"
flyCamera
transform translation 10.0 10.0 10.0 rotation 0.0 0.0 0.0 1.0 scale 1.0 1.0 1.0

@ -1,4 +1,3 @@
uuid 08e1a4d9-6c76-471d-af02-1aa7db1b435a
editor_tag
ui_node
target_camera 739619d7-b8a1-42b4-8803-2b8cfedeebf1
editorTag
uiNode
targetCamera "camera.entity"

@ -1,4 +1,3 @@
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
editorTag
uiText "Welcome to the editor" color #ffffff size 12.0
parent "ui_container.entity"

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

@ -1,4 +1,3 @@
name camera
uuid 2e45b7e9-6722-4d50-8ea5-67f25b8b0f62
transform translation 2.0 2.0 0.0 rotation 0.0 0.0 0.0 1.0 scale 1.0 1.0 1.0
camera
camera

@ -1,4 +1,3 @@
name van
uuid 5c270e84-814c-4d51-9ccd-ab79d9e01f1d
transform translation 0.0 0.0 0.0 rotation 0.0 0.0 0.0 1.0 scale 1.0 1.0 1.0
model "models/van.glb" "Scene"
model "models/van.glb" "Scene"

@ -1,6 +0,0 @@
[package]
name = "obrc"
version = "0.1.0"
edition = "2021"
[dependencies]

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

@ -53,8 +53,9 @@ impl std::convert::From<&EditorState> for bool {
}
/// Tag component for editor entities
#[derive(Component)]
struct EditorTag;
#[derive(Component, Reflect, Debug, Default)]
#[reflect(Component, Default)]
pub(crate) struct EditorTag;
fn toggle_editor_state(
curr_state: Res<State<EditorState>>,
@ -73,15 +74,11 @@ fn toggle_editor_state(
}
}
fn spawn_editor(
mut _commands: Commands,
) {
fn spawn_editor(mut _commands: Commands) {
todo!("Spawn editor");
}
fn despawn_editor(
mut _comands: Commands,
) {
fn despawn_editor(mut _comands: Commands) {
todo!("Despawn editor");
}
@ -123,4 +120,4 @@ fn plane_gizmos(mut gizmos: Gizmos) {
gizmos.arrow(Vec3::ZERO, Vec3::Y, DARK_GREEN);
gizmos.arrow(Vec3::ZERO, Vec3::Z, BLUE);
}
}
}

@ -32,4 +32,4 @@ fn main() {
.add_plugins(save::SavePlugin)
.add_plugins(window::WindowPlugin)
.run();
}
}

@ -1,11 +1,7 @@
use nom::character::complete::hex_digit1;
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")]
@ -14,8 +10,6 @@ pub(crate) enum SaveEntityParseError {
Nom(nom::Err<nom::error::Error<Box<str>>>),
#[error("Failed to parse camera")]
Camera,
#[error("Failed to parse marker component")]
Marker,
}
// Convert Nom error to parse error
@ -49,8 +43,9 @@ fn parse_string(i: &str) -> IResult<&str, &str> {
}
///
/// Returns reflected `Transform`
///
pub(crate) fn parse_save_transform(line: &str) -> Result<Transform, SaveEntityParseError> {
pub(crate) fn parse_save_transform(line: &str) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
let (rem, _) = tag("transform")(line)?;
let mut transform = Transform::default();
@ -80,7 +75,7 @@ pub(crate) fn parse_save_transform(line: &str) -> Result<Transform, SaveEntityPa
// Assert there are no trailing characters on the line
debug_assert_eq!(curr, "");
Ok(transform)
Ok(transform.clone_value())
}
#[test]
@ -93,19 +88,23 @@ fn test_parse_transform() {
scale: Vec3::new(1.1, 1.2, 1.3),
};
assert_eq!(parsed, expected);
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
///
/// Returns a reflected `Name`
///
pub(crate) fn parse_save_name(line: &str) -> Result<Name, SaveEntityParseError> {
pub(crate) fn parse_save_name(line: &str) -> Result<Box<dyn Reflect>, 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)
Ok(name.clone_value())
}
}
@ -116,87 +115,104 @@ fn test_parse_name() {
let parsed = parse_save_name(line).unwrap();
let expected = Name::new("Van");
assert_eq!(parsed, expected);
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
{
let line = "name Big Mike";
let parsed = parse_save_name(line).unwrap();
let expected = Name::new("Big Mike");
assert_eq!(parsed, expected);
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
}
#[derive(Component, Clone, Debug, Reflect, PartialEq)]
#[reflect(Component)]
pub(crate) struct EntityUuid {
id: String,
#[derive(Debug, Default, Reflect, PartialEq)]
#[reflect(Component, PartialEq)]
pub(crate) struct SaveModel {
gltf_file: PathBuf,
scene_name: 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 })
}
impl Component for SaveModel {
const STORAGE_TYPE: StorageType = StorageType::Table;
#[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);
fn register_component_hooks(hooks: &mut ComponentHooks) {
todo!("Assign Scene Handle for SaveModel")
}
}
///
/// Returns a reflected `SaveModel`
///
pub(crate) fn parse_save_model(line: &str) -> Result<(String, String), SaveEntityParseError> {
pub(crate) fn parse_save_model(line: &str) -> Result<Box<dyn Reflect>, 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()))
Ok(SaveModel {
gltf_file: gltf_path.into(),
scene_name: scene_name.into(),
}
.clone_value())
}
#[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"));
let expected = SaveModel {
gltf_file: "models/foo.glb".into(),
scene_name: "My Scene".into(),
};
assert_eq!(parsed, expected);
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
#[derive(Component, Debug, Default, PartialEq, Reflect, Clone)]
#[reflect_value(Component, Default)]
#[derive(Debug, Default, PartialEq, Reflect, Clone)]
#[reflect_value(Component, Default, PartialEq)]
pub(crate) enum SaveCameraRenderTarget {
#[default]
Default,
Window(Uuid),
Window(PathBuf),
}
pub(crate) fn parse_save_camera(
line: &str,
) -> Result<SaveCameraRenderTarget, SaveEntityParseError> {
impl Component for SaveCameraRenderTarget {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn register_component_hooks(hooks: &mut ComponentHooks) {
todo!("Assign Render Target")
}
}
///
/// Returns a reflected `SaveCameraRenderTarget
///
pub(crate) fn parse_save_camera(line: &str) -> Result<Box<dyn Reflect>, 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) {
if let Ok((rem, (_, path))) =
tuple((space1::<&str, nom::error::Error<&str>>, parse_string))(rem)
{
debug_assert!(rem == "");
let parsed_uuid = Uuid::parse_str(uuid)?;
Ok(SaveCameraRenderTarget::Window(parsed_uuid))
Ok(SaveCameraRenderTarget::Window(path.into()).clone_value())
// Camera + target + widow
} else {
debug_assert!(rem == "");
Ok(SaveCameraRenderTarget::Default)
Ok(SaveCameraRenderTarget::Default.clone_value())
}
// camera + target
} else {
@ -208,7 +224,7 @@ pub(crate) fn parse_save_camera(
} else {
debug_assert!(rem == "");
Ok(SaveCameraRenderTarget::Default)
Ok(SaveCameraRenderTarget::Default.clone_value())
}
// Nothing parsed well
} else {
@ -222,13 +238,19 @@ fn test_parse_camera() {
let line = "camera";
let parsed = parse_save_camera(line).unwrap();
let expected = SaveCameraRenderTarget::Default;
assert_eq!(parsed, expected);
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
{
let line = "camera target window";
let parsed = parse_save_camera(line).unwrap();
let expected = SaveCameraRenderTarget::Default;
assert_eq!(parsed, expected);
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
{
let line = "camera target";
@ -236,11 +258,13 @@ fn test_parse_camera() {
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 line = "camera target window \"some.entity\"";
let parsed = parse_save_camera(line).unwrap();
let expected = SaveCameraRenderTarget::Window(target_uuid);
assert_eq!(parsed, expected);
let expected = SaveCameraRenderTarget::Window("some.entity".into());
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
{
let line = "notcamera";
@ -251,20 +275,32 @@ fn test_parse_camera() {
/// 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)]
#[derive(PartialEq, Debug, Reflect, Clone)]
#[reflect_value(Component, PartialEq)]
pub(crate) struct SaveParent(String);
impl Component for SaveParent {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn register_component_hooks(hooks: &mut ComponentHooks) {
todo!("Assign parent for entity")
}
}
///
/// Parses a parent entity with this format:
/// ```text
/// parent some_other_file.entity
/// ```
pub(crate) fn parse_save_parent(line: &str) -> Result<SaveParent, SaveEntityParseError> {
///
/// Returns a reflected `SaveParent`
///
pub(crate) fn parse_save_parent(line: &str) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
let (rem, (_, _, parent_file)) = tuple((tag("parent"), space1, parse_string))(line)?;
debug_assert!(rem.is_empty());
Ok(SaveParent(parent_file.into()))
Ok(SaveParent(parent_file.into()).clone_value())
}
#[test]
@ -272,14 +308,19 @@ 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);
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
///
/// 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> {
/// Returns a reflected `Window`
///
pub(crate) fn parse_save_window(line: &str) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
let (rem, (_, _, window_title)) = tuple((tag("window"), space1, parse_string))(line)?;
let (rem, (_, _, _, visibility)) = tuple((space1, tag("visible"), space1, parse_bool))(rem)?;
@ -290,7 +331,8 @@ pub(crate) fn parse_save_window(line: &str) -> Result<Window, SaveEntityParseErr
title: window_title.into(),
visible: visibility,
..default()
})
}
.clone_value())
}
#[test]
@ -302,17 +344,22 @@ fn test_parse_window() {
title: "Editor".into(),
..default()
};
assert_eq!(parsed.visible, expected.visible);
assert_eq!(parsed.title, expected.title);
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
///
/// The UI Text bundle specified as a sparse subset of a bundle
/// ```text
/// text "This is the text" color #abc123 size 12.34
/// uiText "This is the text" color #abc123 size 12.34
/// ```
pub(crate) fn parse_save_ui_text(line: &str) -> Result<TextBundle, SaveEntityParseError> {
let (rem, (_tag_text, _space1, text)) = tuple((tag("text"), space1, parse_string))(line)?;
///
/// Returns a reflected `Text`
///
pub(crate) fn parse_save_ui_text(line: &str) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
let (rem, (_tag_text, _space1, text)) = tuple((tag("uiText"), space1, parse_string))(line)?;
let (rem, (_space, _tag_color, _space1, _hash, hex_color)) =
tuple((space1, tag("color"), space1, char('#'), hex_digit1))(rem)?;
@ -327,15 +374,12 @@ pub(crate) fn parse_save_ui_text(line: &str) -> Result<TextBundle, SaveEntityPar
font_size,
..default()
};
Ok(TextBundle {
text: Text::from_section(text, style),
..default()
})
Ok(Text::from_section(text, style).clone_value())
}
#[test]
fn test_save_ui_text() {
let line = "text \"This is the text\" color #caffee size 14.6";
let line = "uiText \"This is the text\" color #caffee size 14.6";
let parsed = parse_save_ui_text(line).unwrap();
let expected = Text::from_section(
"This is the text",
@ -345,24 +389,22 @@ fn test_save_ui_text() {
..default()
},
);
assert_eq!(parsed.text.sections[0].value, expected.sections[0].value);
assert_eq!(
parsed.text.sections[0].style.color,
expected.sections[0].style.color,
);
assert_eq!(
parsed.text.sections[0].style.font_size,
expected.sections[0].style.font_size,
);
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
///
/// 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 + Default>(
t: &str,
) -> impl FnOnce(&str) -> Result<T, SaveEntityParseError> + '_ {
move |line: &str| -> Result<T, SaveEntityParseError> { Ok(tag(t)(line).map(|_| T::default())?) }
) -> impl FnOnce(&str) -> Result<Box<dyn Reflect>, SaveEntityParseError> + '_ {
move |line: &str| -> Result<Box<dyn Reflect>, SaveEntityParseError> {
Ok(tag(t)(line).map(|_| T::default().clone_value())?)
}
}
#[derive(Component, Reflect, PartialEq, Debug, Default)]
@ -374,24 +416,37 @@ fn test_save_tag() {
let line = "testing";
let parsed = parse_save_tag::<TestingTag>("testing")(line).unwrap();
let expected = TestingTag;
assert_eq!(parsed, expected);
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
#[derive(Component, Clone, Debug, Reflect, PartialEq)]
#[derive(Clone, Debug, Reflect, PartialEq)]
#[reflect(Component)]
pub(crate) struct SaveTargetCamera(String);
impl Component for SaveTargetCamera {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn register_component_hooks(hooks: &mut ComponentHooks) {
todo!("Assign target camera")
}
}
/// Parses the a SaveTargetCamera which at runtime is converted to a TargetCamera
/// ```text
/// target-camera "./level-camera.entity"
/// targetCamera "./level-camera.entity"
/// ```
///
/// Returns reflected `SaveTargetCamera`
pub(crate) fn parse_save_target_camera(
line: &str,
) -> Result<SaveTargetCamera, SaveEntityParseError> {
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
Ok(
tuple((tag("target-camera"), space1, parse_string))(line).map(
tuple((tag("targetCamera"), space1, parse_string))(line).map(
|(_, (_, _, target_camera_entity_file))| {
SaveTargetCamera(target_camera_entity_file.into())
SaveTargetCamera(target_camera_entity_file.into()).clone_value()
},
)?,
)
@ -399,8 +454,12 @@ pub(crate) fn parse_save_target_camera(
#[test]
fn test_target_camera() {
let line = "target-camera \"./level-camera.entity\"";
let line = "targetCamera \"./level-camera.entity\"";
let parsed = parse_save_target_camera(line).unwrap();
let expected = SaveTargetCamera("./level-camera.entity".into());
assert_eq!(parsed, expected);
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}

@ -9,16 +9,18 @@ pub(crate) use bevy::{
render::camera::RenderTarget,
window::{PrimaryWindow, WindowCloseRequested, WindowRef},
};
pub(crate) use nom::bytes::complete::take;
pub(crate) use nom::character::complete::hex_digit1;
pub(crate) use nom::{
branch::{alt, permutation},
branch::alt,
bytes::complete::{tag, take_until1},
character::complete::{char, hex_digit1, space1},
character::complete::{char, space1},
number::complete::float,
sequence::tuple,
IResult,
};
pub(crate) use std::path::PathBuf;
pub(crate) use thiserror::Error;
pub(crate) use uuid::Uuid;
pub(crate) use crate::{conditions::*, parser::*};
pub(crate) use bevy::ecs::component::{ComponentHooks, StorageType};

@ -5,10 +5,9 @@ pub(crate) struct SavePlugin;
impl Plugin for SavePlugin {
fn build(&self, app: &mut App) {
app
.register_type::<EntityUuid>()
.register_type::<SaveCameraRenderTarget>()
app.register_type::<SaveCameraRenderTarget>()
.register_type::<GltfScene>()
.register_type::<SaveModel>()
.init_asset::<SaveEntity>()
.init_asset_loader::<SaveEntityLoader>()
.init_asset_loader::<SaveSceneLoader>()
@ -37,31 +36,40 @@ impl SaveEntity {
) -> Result<SaveEntity, SaveEntityParseError> {
let lines = text.split('\n');
let mut entity = SaveEntity { ..default() };
lines.into_iter().for_each(|line| {
if let Ok(name) = parse_save_name(line) {
entity.components.push(name.clone_value());
} else if let Ok(transform) = parse_save_transform(line) {
entity.components.push(transform.clone_value());
} else if let Ok(uuid) = parse_save_uuid(line) {
entity.components.push(uuid.clone_value());
} else if let Ok((gltf_file, scene_name)) = parse_save_model(line) {
let gltf_scene = GltfScene { gltf: load_context.load(gltf_file), name: scene_name };
entity.components.push(gltf_scene.clone_value());
} 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);
}
});
let fns = [
parse_save_name,
parse_save_transform,
parse_save_model,
parse_save_camera,
parse_save_parent,
parse_save_window,
parse_save_target_camera,
parse_save_ui_text,
// parse_save_tag::<EditorTag>("editor_tag"),
];
lines
.into_iter()
.filter(|line| !line.is_empty())
.for_each(|line| {
// track if this line matched any components
let mut good = false;
// Run line against all parsers
for f in fns {
if let Ok(v) = f(line) {
entity.components.push(v);
good = true;
}
}
if !good {
error!(
file = load_context.path().to_str().unwrap(),
line = line,
"failed to parse component",
);
}
});
Ok(entity)
}
}
@ -129,11 +137,11 @@ impl AssetLoader for SaveSceneLoader {
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();
let _sub_assets: Vec<Handle<SaveEntity>> = s
.lines()
.map(|line| parent_dir.join(line))
.map(|path| load_context.load(path))
.collect();
Ok(DynamicScene::default())
}
@ -143,14 +151,10 @@ impl AssetLoader for SaveSceneLoader {
}
}
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");
commands.spawn((
SpatialBundle { ..default() },
handle,
));
commands.spawn((SpatialBundle { ..default() }, handle));
}
fn spawn_loaded_entities(

@ -0,0 +1 @@
Wed Jul 31 09:09:04 PM PDT 2024 Ended work on T -> Box<dyn Reflect> parsing stuff
Loading…
Cancel
Save