|
|
|
@ -1,4 +1,4 @@
|
|
|
|
use bevy::{math::Vec3A, render::primitives::Aabb};
|
|
|
|
use bevy::{gltf::Gltf, math::Vec3A, render::primitives::Aabb};
|
|
|
|
use nom::IResult;
|
|
|
|
use nom::IResult;
|
|
|
|
|
|
|
|
|
|
|
|
use crate::prelude::*;
|
|
|
|
use crate::prelude::*;
|
|
|
|
@ -11,7 +11,10 @@ impl Plugin for SavePlugin {
|
|
|
|
app.init_asset::<SaveEntity>()
|
|
|
|
app.init_asset::<SaveEntity>()
|
|
|
|
.init_asset_loader::<SaveEntityLoader>()
|
|
|
|
.init_asset_loader::<SaveEntityLoader>()
|
|
|
|
.add_systems(Startup, test_save_entity)
|
|
|
|
.add_systems(Startup, test_save_entity)
|
|
|
|
.add_systems(Update, check_loaded_entity_assets.run_if(on_event::<AssetEvent<SaveEntity>>()));
|
|
|
|
.add_systems(
|
|
|
|
|
|
|
|
Update,
|
|
|
|
|
|
|
|
check_loaded_entity_assets.run_if(on_event::<AssetEvent<SaveEntity>>()),
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -20,11 +23,15 @@ struct SaveEntity {
|
|
|
|
transform: Option<Transform>,
|
|
|
|
transform: Option<Transform>,
|
|
|
|
name: Option<Name>,
|
|
|
|
name: Option<Name>,
|
|
|
|
uuid: Option<EntityUuid>,
|
|
|
|
uuid: Option<EntityUuid>,
|
|
|
|
|
|
|
|
model: Option<(Handle<Gltf>, String)>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl SaveEntity {
|
|
|
|
impl SaveEntity {
|
|
|
|
fn parse(s: &str) -> Result<SaveEntity, parse::SaveEntityParseError> {
|
|
|
|
fn parse(
|
|
|
|
let lines = s.split('\n');
|
|
|
|
text: &str,
|
|
|
|
|
|
|
|
load_context: &mut LoadContext,
|
|
|
|
|
|
|
|
) -> Result<SaveEntity, parse::SaveEntityParseError> {
|
|
|
|
|
|
|
|
let lines = text.split('\n');
|
|
|
|
let mut entity = SaveEntity { ..default() };
|
|
|
|
let mut entity = SaveEntity { ..default() };
|
|
|
|
lines.into_iter().for_each(|line| {
|
|
|
|
lines.into_iter().for_each(|line| {
|
|
|
|
if let Ok(name) = parse::parse_save_name(line) {
|
|
|
|
if let Ok(name) = parse::parse_save_name(line) {
|
|
|
|
@ -33,6 +40,9 @@ impl SaveEntity {
|
|
|
|
entity.transform = Some(transform);
|
|
|
|
entity.transform = Some(transform);
|
|
|
|
} else if let Ok(uuid) = parse::parse_save_uuid(line) {
|
|
|
|
} else if let Ok(uuid) = parse::parse_save_uuid(line) {
|
|
|
|
entity.uuid = Some(EntityUuid(uuid));
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
Ok(entity)
|
|
|
|
Ok(entity)
|
|
|
|
@ -87,6 +97,11 @@ mod parse {
|
|
|
|
.map(|(s, (w, _, x, _, y, _, z))| (s, (w, x, y, z)))
|
|
|
|
.map(|(s, (w, _, x, _, y, _, z))| (s, (w, x, y, z)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn parse_string<'a>(i: &'a str) -> IResult<&'a str, &'a 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 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();
|
|
|
|
@ -119,9 +134,25 @@ mod parse {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Assert there are no trailing characters on the line
|
|
|
|
|
|
|
|
debug_assert_eq!(curr, "");
|
|
|
|
|
|
|
|
|
|
|
|
Ok(transform)
|
|
|
|
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 parsed = parse_save_name("name asfd").unwrap();
|
|
|
|
@ -141,37 +172,6 @@ mod parse {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
|
|
/// 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)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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]
|
|
|
|
#[test]
|
|
|
|
fn test_parse_name() {
|
|
|
|
fn test_parse_name() {
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -190,6 +190,20 @@ mod parse {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
|
|
/// 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]
|
|
|
|
#[test]
|
|
|
|
fn test_parse_uuid() {
|
|
|
|
fn test_parse_uuid() {
|
|
|
|
let line = "uuid 1c16ab9a-5f79-4340-8469-4086f69c64f2";
|
|
|
|
let line = "uuid 1c16ab9a-5f79-4340-8469-4086f69c64f2";
|
|
|
|
@ -198,6 +212,29 @@ mod parse {
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(parsed, expected);
|
|
|
|
assert_eq!(parsed, expected);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub(crate) fn parse_save_model(line: &str) -> Result<(String, String), SaveEntityParseError> {
|
|
|
|
|
|
|
|
println!("{}", line);
|
|
|
|
|
|
|
|
let (rem, (_, _, gltf_name, _, scene_name)) = tuple((
|
|
|
|
|
|
|
|
tag("model"),
|
|
|
|
|
|
|
|
take(1usize),
|
|
|
|
|
|
|
|
parse_string,
|
|
|
|
|
|
|
|
take(1usize),
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -221,14 +258,14 @@ impl AssetLoader for SaveEntityLoader {
|
|
|
|
&'a self,
|
|
|
|
&'a self,
|
|
|
|
reader: &'a mut Reader,
|
|
|
|
reader: &'a mut Reader,
|
|
|
|
_settings: &'a (),
|
|
|
|
_settings: &'a (),
|
|
|
|
_load_context: &'a mut LoadContext,
|
|
|
|
load_context: &'a mut LoadContext,
|
|
|
|
) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
|
|
|
|
) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
|
|
|
|
Box::pin(async move {
|
|
|
|
Box::pin(async move {
|
|
|
|
let mut bytes = Vec::new();
|
|
|
|
let mut bytes = Vec::new();
|
|
|
|
reader.read_to_end(&mut bytes).await?;
|
|
|
|
reader.read_to_end(&mut bytes).await?;
|
|
|
|
|
|
|
|
|
|
|
|
let s = std::str::from_utf8(bytes.as_slice()).unwrap();
|
|
|
|
let s = std::str::from_utf8(bytes.as_slice()).unwrap();
|
|
|
|
Ok(SaveEntity::parse(s)?)
|
|
|
|
Ok(SaveEntity::parse(s, load_context)?)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -239,7 +276,15 @@ impl AssetLoader for SaveEntityLoader {
|
|
|
|
|
|
|
|
|
|
|
|
fn test_save_entity(loader: Res<AssetServer>, mut commands: Commands) {
|
|
|
|
fn test_save_entity(loader: Res<AssetServer>, mut commands: Commands) {
|
|
|
|
let handle: Handle<SaveEntity> = loader.load("levels/00/entities/van.entity");
|
|
|
|
let handle: Handle<SaveEntity> = loader.load("levels/00/entities/van.entity");
|
|
|
|
commands.spawn((SpatialBundle { ..default() }, handle, ShowAabbGizmo { ..default() }, Aabb { center: Vec3A::ZERO, half_extents: Vec3A::ONE }));
|
|
|
|
commands.spawn((
|
|
|
|
|
|
|
|
SpatialBundle { ..default() },
|
|
|
|
|
|
|
|
handle,
|
|
|
|
|
|
|
|
ShowAabbGizmo { ..default() },
|
|
|
|
|
|
|
|
Aabb {
|
|
|
|
|
|
|
|
center: Vec3A::ZERO,
|
|
|
|
|
|
|
|
half_extents: Vec3A::ONE,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn check_loaded_entity_assets(
|
|
|
|
fn check_loaded_entity_assets(
|
|
|
|
@ -247,6 +292,7 @@ fn check_loaded_entity_assets(
|
|
|
|
mut events: EventReader<AssetEvent<SaveEntity>>,
|
|
|
|
mut events: EventReader<AssetEvent<SaveEntity>>,
|
|
|
|
save_entities: Res<Assets<SaveEntity>>,
|
|
|
|
save_entities: Res<Assets<SaveEntity>>,
|
|
|
|
mut commands: Commands,
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
events.read().for_each(|event| {
|
|
|
|
events.read().for_each(|event| {
|
|
|
|
match event {
|
|
|
|
match event {
|
|
|
|
@ -256,7 +302,10 @@ fn check_loaded_entity_assets(
|
|
|
|
.filter(|(_, handle)| handle.id() == *id)
|
|
|
|
.filter(|(_, handle)| handle.id() == *id)
|
|
|
|
.for_each(|(entity, _handle)| {
|
|
|
|
.for_each(|(entity, _handle)| {
|
|
|
|
let saved = save_entities.get(*id).unwrap();
|
|
|
|
let saved = save_entities.get(*id).unwrap();
|
|
|
|
debug!("Updating entity {:?} ({:?}) because asset changed", saved.name, saved.uuid);
|
|
|
|
debug!(
|
|
|
|
|
|
|
|
"Updating entity {:?} ({:?}) because asset changed",
|
|
|
|
|
|
|
|
saved.name, saved.uuid
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
let mut e = commands.entity(entity);
|
|
|
|
let mut e = commands.entity(entity);
|
|
|
|
// Apply transform
|
|
|
|
// Apply transform
|
|
|
|
@ -280,9 +329,18 @@ fn check_loaded_entity_assets(
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
e.remove::<EntityUuid>();
|
|
|
|
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).unwrap();
|
|
|
|
|
|
|
|
e.insert(scene_handle.clone());
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
e.remove::<Handle<Scene>>();
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => ()
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|