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.

368 lines
12 KiB
Rust

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)
.add_systems(
Update,
check_loaded_entity_assets.run_if(on_event::<AssetEvent<SaveEntity>>()),
);
}
}
#[derive(Asset, TypePath, Debug, Default)]
pub(crate) struct SaveEntity {
transform: Option<Transform>,
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>,
}
impl SaveEntity {
fn parse(
text: &str,
load_context: &mut LoadContext,
) -> Result<SaveEntity, parse::SaveEntityParseError> {
let lines = text.split('\n');
let mut entity = SaveEntity { ..default() };
lines.into_iter().for_each(|line| {
if let Ok(name) = parse::parse_save_name(line) {
entity.name = Some(name);
} else if let Ok(transform) = parse::parse_save_transform(line) {
entity.transform = Some(transform);
} else if let Ok(uuid) = parse::parse_save_uuid(line) {
entity.uuid = Some(EntityUuid(uuid));
} else if let Ok((gltf_path, scene_name)) = parse::parse_save_model(line) {
let handle = load_context.load(gltf_path);
entity.model = Some((handle, scene_name));
} else if let Ok(true) = parse::parse_save_camera(line) {
entity.camera = Some(true);
}
});
Ok(entity)
}
}
#[derive(Component, Clone, Debug)]
struct EntityUuid(Uuid);
mod parse {
use super::*;
#[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)]
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] parse::SaveEntityParseError),
}
impl AssetLoader for SaveEntityLoader {
type Asset = SaveEntity;
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 save_entity = SaveEntity::parse(s, load_context)?;
Ok(save_entity)
}
fn extensions(&self) -> &[&str] {
&["entity"]
}
}
fn test_save_entity(loader: Res<AssetServer>, mut commands: Commands) {
let handle: Handle<SaveEntity> = loader.load("levels/00/entities/van.entity");
commands.spawn((
SpatialBundle { ..default() },
handle,
));
}
fn check_loaded_entity_assets(
query: Query<(Entity, &Handle<SaveEntity>)>,
mut events: EventReader<AssetEvent<SaveEntity>>,
save_entities: Res<Assets<SaveEntity>>,
mut commands: Commands,
gltfs: Res<Assets<Gltf>>,
) {
events.read().for_each(|event| {
if let AssetEvent::LoadedWithDependencies { id } = event {
query
.iter()
.filter(|(_, handle)| handle.id() == *id)
.for_each(|(entity, _handle)| {
let saved = save_entities.get(*id).unwrap();
debug!(
"Updating entity {:?} ({:?}) because asset changed",
saved.name, saved.uuid
);
let mut e = commands.entity(entity);
// Apply camera
// Should be applied early to avoid clobbering transform
if let Some(is_camera) = &saved.camera {
if *is_camera {
e.insert(Camera3dBundle { ..default() });
}
} 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>>();
}
});
}
})
}