Very basic entity file parsing

Implements basic parsing for name, uuid, and transform.
Eventually will add Gltf Scene and Parent attribution.
Next need to convert these loaded values to entity components.

Pretty sure it doesn't work, just that it compiles...
attempt/001
Elijah C. Voigt 1 year ago
parent 63fab851ac
commit 8027af5303

3
Cargo.lock generated

@ -78,6 +78,9 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bevy", "bevy",
"bevy_mod_picking", "bevy_mod_picking",
"nom",
"thiserror",
"uuid",
"wee_alloc", "wee_alloc",
] ]

@ -7,6 +7,10 @@ edition = "2021"
bevy = "0.13" bevy = "0.13"
wee_alloc = "*" wee_alloc = "*"
bevy_mod_picking = "0.18" bevy_mod_picking = "0.18"
thiserror = "1"
nom = "7"
# Entity Uuid parsing
uuid = "1.7.0"
[profile.wasm-release] [profile.wasm-release]
inherits = "release" inherits = "release"

@ -0,0 +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

Binary file not shown.

Binary file not shown.

@ -8,6 +8,8 @@ pub(crate) mod editor;
pub(crate) mod menu; pub(crate) mod menu;
/// Helper module containing common imports across the project /// Helper module containing common imports across the project
pub(crate) mod prelude; pub(crate) mod prelude;
/// Level saving/loading logic
pub(crate) mod save;
/// Window handling /// Window handling
pub(crate) mod window; pub(crate) mod window;
@ -24,6 +26,7 @@ fn main() {
.add_plugins(camera::CameraPlugin) .add_plugins(camera::CameraPlugin)
.add_plugins(editor::EditorPlugin) .add_plugins(editor::EditorPlugin)
.add_plugins(menu::MenuPlugin) .add_plugins(menu::MenuPlugin)
.add_plugins(save::SavePlugin)
.add_plugins(window::WindowPlugin) .add_plugins(window::WindowPlugin)
.run(); .run();
} }

@ -1,10 +1,18 @@
pub(crate) use bevy::app::AppExit; pub(crate) use bevy::app::AppExit;
pub(crate) use bevy::asset::AsyncReadExt;
pub(crate) use bevy::asset::{io::Reader, AssetLoader, LoadContext};
pub(crate) use bevy::input::common_conditions::input_just_pressed; pub(crate) use bevy::input::common_conditions::input_just_pressed;
pub(crate) use bevy::prelude::*; pub(crate) use bevy::prelude::*;
pub(crate) use bevy::render::camera::RenderTarget; pub(crate) use bevy::render::camera::RenderTarget;
pub(crate) use bevy::utils::Uuid;
pub(crate) use bevy::utils::{thiserror::Error, BoxedFuture};
pub(crate) use bevy::window::PrimaryWindow; pub(crate) use bevy::window::PrimaryWindow;
pub(crate) use bevy::window::WindowCloseRequested; pub(crate) use bevy::window::WindowCloseRequested;
pub(crate) use bevy::window::WindowRef; pub(crate) use bevy::window::WindowRef;
pub(crate) use nom::bytes::complete::tag;
pub(crate) use nom::bytes::complete::take;
pub(crate) use nom::number::complete::float;
pub(crate) use nom::sequence::tuple;
pub(crate) use crate::camera::*; pub(crate) use crate::camera::*;
pub(crate) use crate::conditions::*; pub(crate) use crate::conditions::*;

@ -0,0 +1,232 @@
use nom::IResult;
use crate::prelude::*;
/// Menu Plugin; empty struct for Plugin impl
pub(crate) struct SavePlugin;
impl Plugin for SavePlugin {
fn build(&self, app: &mut App) {
app.init_asset::<SaveEntity>()
.init_asset_loader::<SaveEntityLoader>()
.add_systems(Startup, test_save_entity);
}
}
#[derive(Asset, TypePath, Debug, Default)]
struct SaveEntity {
transform: Option<Transform>,
name: Option<Name>,
uuid: Option<Uuid>,
}
impl SaveEntity {
fn parse(s: &str) -> Result<SaveEntity, SaveEntityParseError> {
let lines = s.split('\n');
let mut entity = SaveEntity { ..default() };
lines.into_iter().for_each(|line| {
if let Ok(name) = parse_save_name(line) {
entity.name = Some(name);
} else if let Ok(transform) = parse_save_transform(line) {
entity.transform = Some(transform);
} else if let Ok(uuid) = parse_save_uuid(line) {
entity.uuid = Some(uuid);
}
});
Ok(entity)
}
}
#[derive(Error, Debug)]
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_thing<'a>(s: &'a str, l: &'a str) -> IResult<&'a str, &'a str> {
tag(l)(s)
}
fn parse_xyz<'a>(i: &'a str) -> IResult<&'a str, (f32, f32, f32)> {
tuple((float, take(1usize), float, take(1usize), float))(i)
.map(|(s, (x, _, y, _, z))| (s, (x, y, z)))
}
fn parse_wxyz<'a>(i: &'a str) -> IResult<&'a str, (f32, f32, f32, f32)> {
tuple((
float,
take(1usize),
float,
take(1usize),
float,
take(1usize),
float,
))(i)
.map(|(s, (w, _, x, _, y, _, z))| (s, (w, x, y, z)))
}
///
/// ```
/// 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);
/// ```
///
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((input, _)) = parse_thing(curr, "translation") {
let (rem, (x, y, z)) = parse_xyz(input.trim_start())?;
transform.translation = Vec3::new(x, y, z);
curr = rem.trim_start();
} else if let Ok((input, _)) = parse_thing(curr.trim_start(), "rotation") {
let (rem, (x, y, z, w)) = parse_wxyz(input.trim_start())?;
transform.rotation = Quat::from_xyzw(x, y, z, w);
curr = rem.trim_start();
} else if let Ok((input, _)) = parse_thing(curr.trim_start(), "scale") {
let (rem, (x, y, z)) = parse_xyz(input.trim_start())?;
transform.scale = Vec3::new(x, y, z);
curr = rem.trim_start();
} else {
return Err(SaveEntityParseError::Transform);
}
}
Ok(transform)
}
///
/// ```
/// let parsed = parse_save_name("name asfd").unwrap();
/// let expected = Name::new("asdf");
///
/// assert_eq!(parsed, expected);
/// ```
///
fn parse_save_name(line: &str) -> Result<Name, SaveEntityParseError> {
let (remainder, _) = tag("name")(line)?;
let n = remainder.trim().to_string();
if n.len() == 0 {
Err(SaveEntityParseError::Name)
} else {
let name = Name::new(n);
Ok(name)
}
}
///
/// ```
/// 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);
/// ```
///
fn parse_save_uuid(line: &str) -> Result<Uuid, SaveEntityParseError> {
let (remainder, _) = tag("uuid")(line)?;
let uuid = Uuid::try_parse(remainder.trim())?;
Ok(uuid)
}
mod test {
use super::*;
#[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);
}
#[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);
}
}
#[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);
}
}
#[derive(Default)]
struct SaveEntityLoader;
#[derive(Error, Debug)]
enum SaveEntityLoaderError {
#[error("Could not load asset: {0}")]
Io(#[from] std::io::Error),
#[error("Could not parse entity: {0}")]
Parse(#[from] SaveEntityParseError),
}
impl AssetLoader for SaveEntityLoader {
type Asset = SaveEntity;
type Settings = ();
type Error = SaveEntityLoaderError;
fn load<'a>(
&'a self,
reader: &'a mut Reader,
_settings: &'a (),
_load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
Box::pin(async move {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
let s = std::str::from_utf8(bytes.as_slice()).unwrap();
Ok(SaveEntity::parse(s)?)
})
}
fn extensions(&self) -> &[&str] {
&["entity"]
}
}
fn test_save_entity(loader: Res<AssetServer>, mut handle: Local<Handle<SaveEntity>>) {
*handle = loader.load("levels/00/entities/van.entity");
}
Loading…
Cancel
Save