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
parent
63fab851ac
commit
8027af5303
@ -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.
@ -1,10 +1,18 @@
|
||||
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::prelude::*;
|
||||
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::WindowCloseRequested;
|
||||
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::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…
Reference in New Issue