You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

670 lines
21 KiB
Rust

use bevy::{
core_pipeline::{
core_3d::graph::Core3d,
tonemapping::{DebandDither, Tonemapping},
},
render::{
camera::{CameraMainTextureUsages, CameraRenderGraph, Exposure},
primitives::Frustum,
view::{ColorGrading, VisibleEntities},
},
};
use crate::prelude::*;
#[derive(Error, Debug)]
pub(crate) enum SaveEntityParseError {
#[error("Failed to parse `{0}`")]
Component(String),
#[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()))
}
}
#[derive(Debug, PartialEq)]
pub(crate) enum Token {
Tag(String),
Str(String),
Num(f32),
Comment(String),
Etc,
}
///
/// Simple function to tokenize a string into an array of Token values
///
pub(crate) fn tokenize(line: &str) -> Vec<Token> {
let mut l = line;
let mut tokens = Vec::new();
// Check for comment
if let Ok((_, (_start, content))) = tuple((char::<&str, ()>(';'), not_line_ending))(line) {
tokens.push(Token::Comment(content.strip_prefix(" ").unwrap().into()));
} else {
// Check for all other token types in a loop
while l.len() > 0 {
if let Ok((rem, (_, _, s, _, _))) = tuple((
space0,
char::<&str, ()>('"'),
take_until("\""),
char::<&str, ()>('"'),
space0,
))(l)
{
debug!("Parsed string {:?}", s);
tokens.push(Token::Str(s.into()));
l = rem;
} else if let Ok((rem, (_, num, _))) = tuple((space0, float::<&str, ()>, space0))(l) {
debug!("Parsed float {:?}", num);
tokens.push(Token::Num(num.into()));
l = rem;
} else if let Ok((rem, (_, etc, _))) =
tuple((space0, tag::<&str, &str, ()>("..."), space0))(l)
{
debug!("Parsed etc. {:?}", etc);
tokens.push(Token::Etc);
l = rem;
} else if let Ok((rem, (_, tag, _))) =
tuple((space0, alphanumeric1::<&str, ()>, space0))(l)
{
debug!("Parsed tag {:?}", tag);
tokens.push(Token::Tag(tag.into()));
l = rem;
} else {
debug!("Breaking loop");
break;
}
}
}
debug!("Parsed tokens: {:?}", tokens);
tokens
}
#[test]
fn test_tokenize() {
let line = "foo \"bar\" 1.23 baz -3.45 \"asdf\" \"multi word string\" etc";
assert_eq!(
tokenize(line),
vec![
Token::Tag("foo".into()),
Token::Str("bar".into()),
Token::Num(1.23),
Token::Tag("baz".into()),
Token::Num(-3.45),
Token::Str("asdf".into()),
Token::Str("multi word string".into()),
Token::Tag("etc".into())
]
);
}
///
/// Returns reflected `Transform`
///
/// A fairly complicated parse function because Transform is sort of 3 components in one
/// (translation, rotation, scale).
///
/// It might get more complicated as I add more ways to express rotation!
///
pub(crate) fn parse_save_transform(
tokens: &Vec<Token>,
) -> Result<Vec<Box<dyn Reflect>>, SaveEntityParseError> {
if tokens.get(0) == Some(&Token::Tag("transform".into())) {
let mut t = Transform::default();
let mut idx = 1;
while idx < tokens.len() {
match tokens.get(idx) {
Some(Token::Tag(attr)) => {
idx += 1;
match attr.as_str() {
"translation" => {
if let Token::Num(x) = tokens[idx] {
t.translation.x = x;
idx += 1;
}
if let Token::Num(y) = tokens[idx] {
t.translation.y = y;
idx += 1;
}
if let Token::Num(z) = tokens[idx] {
t.translation.z = z;
idx += 1;
}
info!("{:?}", t.translation);
}
"scale" => {
if let Token::Num(x) = tokens[idx] {
t.scale.x = x;
idx += 1;
}
if let Token::Num(y) = tokens[idx] {
t.scale.y = y;
idx += 1;
}
if let Token::Num(z) = tokens[idx] {
t.scale.z = z;
idx += 1;
}
}
"rotation" => {
let x = match tokens.get(idx) {
Some(Token::Num(x)) => {
idx += 1;
*x
}
_ => 0.0,
};
let y = match tokens.get(idx) {
Some(Token::Num(y)) => {
idx += 1;
*y
}
_ => 0.0,
};
let z = match tokens.get(idx) {
Some(Token::Num(z)) => {
idx += 1;
*z
}
_ => 0.0,
};
let w = match tokens.get(idx) {
Some(Token::Num(w)) => {
idx += 1;
*w
}
_ => 1.0,
};
t.rotation = Quat::from_xyzw(x, y, z, w);
}
_ => idx += 1,
}
}
_ => idx += 1,
}
}
println!("Parsed transform: {:?}", t);
Ok(vec![
t.clone_value(),
GlobalTransform::default().clone_value(),
])
} else {
return Err(SaveEntityParseError::Component("Transform".into()));
}
}
#[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 tokens = tokenize(line);
let parsed = parse_save_transform(&tokens).unwrap();
let t = 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),
};
let gt = GlobalTransform::default();
let expected = vec![t.clone_value(), gt.clone_value()];
parsed.iter().zip(expected).for_each(|(p, e)| {
assert!(e.clone_value().reflect_partial_eq(p.as_reflect()).unwrap());
});
}
{
let line = "transform translation ... rotation ... scale ...";
let tokens = tokenize(line);
let parsed = parse_save_transform(&tokens).unwrap();
let t = Transform::default();
let gt = GlobalTransform::default();
let expected = vec![t.clone_value(), gt.clone_value()];
parsed.iter().zip(expected).for_each(|(p, e)| {
assert!(e.clone_value().reflect_partial_eq(p.as_reflect()).unwrap());
});
}
{
let line = "transform ...";
let tokens = tokenize(line);
let parsed = parse_save_transform(&tokens).unwrap();
let t = Transform::default();
let gt = GlobalTransform::default();
let expected = vec![t.clone_value(), gt.clone_value()];
parsed.iter().zip(expected).for_each(|(p, e)| {
assert!(e.clone_value().reflect_partial_eq(p.as_reflect()).unwrap());
});
}
{
let line = "transform translation 1.0 ... rotation 2.0 ... scale 3.0 ...";
let tokens = tokenize(line);
let parsed = parse_save_transform(&tokens).unwrap();
let t = Transform {
translation: Vec3::new(1.0, 0.0, 0.0),
rotation: Quat::from_xyzw(2.0, 0.0, 0.0, 1.0),
scale: Vec3::new(3.0, 1.0, 1.0),
};
let gt = GlobalTransform::default();
let expected = vec![t.clone_value(), gt.clone_value()];
parsed.iter().zip(expected).for_each(|(p, e)| {
assert!(e.clone_value().reflect_partial_eq(p.as_reflect()).unwrap());
});
}
}
///
/// Returns a reflected `Name`
///
pub(crate) fn parse_save_name(
tokens: &Vec<Token>,
) -> Result<Vec<Box<dyn Reflect>>, SaveEntityParseError> {
if let Some((Token::Tag(t), &[Token::Str(ref s)])) = tokens.split_first()
&& *t == String::from("name")
{
Ok(vec![Name::new(s.clone()).clone_value()])
} else {
Err(SaveEntityParseError::Component("Name".into()))
}
}
#[test]
fn test_parse_name() {
{
let line = "name Van";
let tokens = tokenize(line);
let parsed = parse_save_name(&tokens).unwrap();
let expected = vec![Name::new("Van").clone_value()];
parsed.iter().zip(expected).for_each(|(p, e)| {
assert!(e.clone_value().reflect_partial_eq(p.as_reflect()).unwrap());
});
}
{
let line = "name Big Mike";
let tokens = tokenize(line);
let parsed = parse_save_name(&tokens).unwrap();
let expected = vec![Name::new("Big Mike").clone_value()];
parsed.iter().zip(expected).for_each(|(p, e)| {
assert!(e.clone_value().reflect_partial_eq(p.as_reflect()).unwrap());
});
}
}
#[derive(Debug, Default, Reflect, PartialEq)]
#[reflect(Component, PartialEq)]
pub(crate) struct SaveModel {
gltf_file: PathBuf,
scene_name: String,
}
impl Component for SaveModel {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn register_component_hooks(hooks: &mut ComponentHooks) {
todo!("Assign Scene Handle for SaveModel")
}
}
///
/// Returns a reflected `SaveModel`
///
pub(crate) fn parse_save_model(
tokens: &Vec<Token>,
) -> Result<Vec<Box<dyn Reflect>>, SaveEntityParseError> {
if tokens.get(0) == Some(&Token::Tag("model".into())) {
if let Token::Str(gltf_file) = tokens.get(1).expect("model requires gltf file") {
if let Token::Str(scene_name) = tokens.get(2).expect("model requires scene name") {
Ok(vec![SaveModel {
gltf_file: gltf_file.into(),
scene_name: scene_name.clone(),
}
.clone_value()])
} else {
Err(SaveEntityParseError::Component("Model".into()))
}
} else {
Err(SaveEntityParseError::Component("Model".into()))
}
} else {
Err(SaveEntityParseError::Component("Model".into()))
}
}
#[test]
fn test_parse_model() {
let line = "model \"models/foo.glb\" \"My Scene\"";
let tokens = tokenize(line);
let parsed = parse_save_model(&tokens).unwrap();
let expected = vec![SaveModel {
gltf_file: "models/foo.glb".into(),
scene_name: "My Scene".into(),
}];
parsed.iter().zip(expected).for_each(|(p, e)| {
assert!(e.clone_value().reflect_partial_eq(p.as_reflect()).unwrap());
});
}
#[derive(Component, Debug, Default, PartialEq, Reflect, Clone)]
#[reflect_value(Component, Default, PartialEq)]
pub(crate) enum SaveCameraRenderTarget {
#[default]
Default,
Window(PathBuf),
}
///
/// Returns a reflected `Camera3d`
///
pub(crate) fn parse_save_camera(
tokens: &Vec<Token>,
) -> Result<Vec<Box<dyn Reflect>>, SaveEntityParseError> {
if tokens[0] == Token::Tag("camera".into()) {
Ok(vec![
Camera::default().clone_value(),
CameraRenderGraph::new(Core3d).clone_value(),
Projection::default().clone_value(),
VisibleEntities::default().clone_value(),
Frustum::default().clone_value(),
Camera3d::default().clone_value(),
Tonemapping::default().clone_value(),
DebandDither::default().clone_value(),
ColorGrading::default().clone_value(),
Exposure::default().clone_value(),
CameraMainTextureUsages::default().clone_value(),
])
} else {
Err(SaveEntityParseError::Component("camera".into()))
}
}
#[test]
fn test_parse_camera() {
{
let line = "camera";
let tokens = tokenize(line);
let parsed = parse_save_camera(&tokens).unwrap();
let expected = vec![
Camera::default().clone_value(),
CameraRenderGraph::new(Core3d).clone_value(),
Projection::default().clone_value(),
VisibleEntities::default().clone_value(),
Frustum::default().clone_value(),
Camera3d::default().clone_value(),
Tonemapping::default().clone_value(),
DebandDither::default().clone_value(),
ColorGrading::default().clone_value(),
Exposure::default().clone_value(),
CameraMainTextureUsages::default().clone_value(),
];
parsed
.iter()
.zip(expected)
.for_each(|(p, e)| match e.reflect_partial_eq(p.as_reflect()) {
Some(r) => assert!(r),
None => warn!(
"Type {:?} does not support reflection",
e.get_represented_type_info().unwrap()
),
});
}
// {
// let line = "camera target window";
// let tokens = tokenize(line);
// let parsed = parse_save_camera(&tokens).unwrap();
// let expected = SaveCameraRenderTarget::Default;
// assert!(expected
// .clone_value()
// .reflect_partial_eq(parsed.as_reflect())
// .unwrap());
// }
// {
// let line = "camera target";
// let tokens = tokenize(line);
// let parsed = parse_save_camera(&tokens);
// assert!(parsed.is_err());
// }
// {
// let line = "camera target window \"some.entity\"";
// let tokens = tokenize(line);
// let parsed = parse_save_camera(&tokens).unwrap();
// let expected = SaveCameraRenderTarget::Window("some.entity".into());
// assert!(expected
// .clone_value()
// .reflect_partial_eq(parsed.as_reflect())
// .unwrap());
// }
{
let line = "notcamera";
let tokens = tokenize(line);
let parsed = parse_save_camera(&tokens);
assert!(parsed.is_err());
}
}
/// encapsulates the spatial 3d bits of an entity
pub(crate) fn parse_save_visibility(
tokens: &Vec<Token>,
) -> Result<Vec<Box<dyn Reflect>>, SaveEntityParseError> {
if tokens[0] == Token::Tag("visible".into()) {
Ok(vec![
Visibility::default().clone_value(),
InheritedVisibility::default().clone_value(),
ViewVisibility::default().clone_value(),
])
} else {
Err(SaveEntityParseError::Component("visibility".into()))
}
}
#[test]
fn test_parse_visibility() {
let line = "visible";
let tokens = tokenize(line);
let parsed = parse_save_visibility(&tokens).unwrap();
let expected = vec![
Visibility::default().clone_value(),
InheritedVisibility::default().clone_value(),
ViewVisibility::default().clone_value(),
];
parsed.iter().zip(expected).for_each(|(p, e)| {
assert!(e.clone_value().reflect_partial_eq(p.as_reflect()).unwrap());
});
}
/// 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(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
/// ```
///
/// Returns a reflected `SaveParent`
///
pub(crate) fn parse_save_parent(
tokens: &Vec<Token>,
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
todo!("parse_save_parent");
// Ok(SaveParent(parent_file.into()).clone_value())
}
#[test]
fn test_parse_parent() {
let line = "parent \"some_other_file.entity\"";
let tokens = tokenize(line);
let parsed = parse_save_parent(&tokens).unwrap();
let expected = SaveParent("some_other_file.entity".into());
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)
///
/// Returns a reflected `Window`
///
pub(crate) fn parse_save_window(
tokens: &Vec<Token>,
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
todo!("parse_save_window");
/*
Ok(Window {
title: window_title.into(),
visible: visibility,
..default()
}
.clone_value())
*/
}
#[test]
fn test_parse_window() {
let line = "window \"Editor\" visible false";
let tokens = tokenize(line);
let parsed = parse_save_window(&tokens).unwrap();
let expected = Window {
visible: false,
title: "Editor".into(),
..default()
};
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
///
/// The UI Text bundle specified as a sparse subset of a bundle
/// ```text
/// uiText "This is the text" color #abc123 size 12.34
/// ```
///
/// Returns a reflected `Text`
///
pub(crate) fn parse_save_ui_text(
tokens: &Vec<Token>,
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
todo!("parse_save_ui_text");
/*
let style = TextStyle {
color: Color::Srgba(Srgba::hex(hex_color).unwrap()),
font_size,
..default()
};
Ok(Text::from_section(text, style).clone_value())
*/
}
#[test]
fn test_save_ui_text() {
let line = "uiText \"This is the text\" color #caffee size 14.6";
let tokens = tokenize(line);
let parsed = parse_save_ui_text(&tokens).unwrap();
let expected = Text::from_section(
"This is the text",
TextStyle {
color: Srgba::hex("#caffee").unwrap().into(),
font_size: 14.6,
..default()
},
);
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(&Vec<Token>) -> Result<Box<dyn Reflect>, SaveEntityParseError> + '_ {
move |tokens: &Vec<Token>| -> Result<Box<dyn Reflect>, SaveEntityParseError> {
todo!("parse_save_tag")
}
}
#[derive(Component, Reflect, PartialEq, Debug, Default)]
#[reflect(Component)]
struct TestingTag;
#[test]
fn test_save_tag() {
let line = "testing";
let tokens = tokenize(line);
let parsed = parse_save_tag::<TestingTag>("testing")(&tokens).unwrap();
let expected = TestingTag;
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}
#[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
/// targetCamera "./level-camera.entity"
/// ```
///
/// Returns reflected `SaveTargetCamera`
pub(crate) fn parse_save_target_camera(
tokens: &Vec<Token>,
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
todo!("parse_save_target_camera")
}
#[test]
fn test_target_camera() {
let line = "targetCamera \"./level-camera.entity\"";
let tokens = tokenize(line);
let parsed = parse_save_target_camera(&tokens).unwrap();
let expected = SaveTargetCamera("./level-camera.entity".into());
assert!(expected
.clone_value()
.reflect_partial_eq(parsed.as_reflect())
.unwrap());
}