Basic model loading works!

Going to look into camera and light entity loading next
attempt/001
Elijah C. Voigt 1 year ago
parent 84744a9397
commit edb15ffe49

@ -0,0 +1,17 @@
# Assets
## Custom Assets
### `.entity` files
Files ending in `.entity` store entity information.
Note: This data is not auto-magically de/serialized by Bevy, we write our own parser using `nom`.
This custom reading/writing can be found in `src/save.rs`.
All `.entity` files list one component per line.
Components include:
* `name <string>`
* `uuid <UUID string>`
* `transform translation <f32> <f32> <f32> rotation <f32> <f32> <f32> <f32> scale <f32> <f32> <f32>`
* `model "<path string>" "<string>"`

@ -1,3 +1,4 @@
name van name van
uuid 5c270e84-814c-4d51-9ccd-ab79d9e01f1d uuid 5c270e84-814c-4d51-9ccd-ab79d9e01f1d
transform translation 1.0 1.0 1.0 rotation 0.0 0.0 0.0 1.0 scale 0.5 0.5 0.5 transform translation 0.0 0.0 0.0 rotation 0.0 0.0 0.0 1.0 scale 1.0 1.0 1.0
model "models/van.glb" "Scene"

@ -11,6 +11,7 @@ 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::tag;
pub(crate) use nom::bytes::complete::take; pub(crate) use nom::bytes::complete::take;
pub(crate) use nom::bytes::complete::take_until1;
pub(crate) use nom::number::complete::float; pub(crate) use nom::number::complete::float;
pub(crate) use nom::sequence::tuple; pub(crate) use nom::sequence::tuple;

@ -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)
@ -43,7 +53,7 @@ impl SaveEntity {
struct EntityUuid(Uuid); struct EntityUuid(Uuid);
mod parse { mod parse {
use super::*; use super::*;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub(crate) enum SaveEntityParseError { pub(crate) enum SaveEntityParseError {
@ -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,6 +172,24 @@ mod parse {
} }
} }
#[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 parsed = parse_save_uuid("uuid 339dd18f-761a-4997-b515-ef57f4dc2447").unwrap();
@ -155,49 +204,37 @@ mod parse {
Ok(uuid) Ok(uuid)
} }
mod test { #[test]
use super::*; fn test_parse_uuid() {
let line = "uuid 1c16ab9a-5f79-4340-8469-4086f69c64f2";
#[test] let parsed = parse_save_uuid(line).unwrap();
fn test_parse_transform() { let expected = Uuid::parse_str("1c16ab9a-5f79-4340-8469-4086f69c64f2").unwrap();
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); assert_eq!(parsed, expected);
} }
#[test] pub(crate) fn parse_save_model(line: &str) -> Result<(String, String), SaveEntityParseError> {
fn test_parse_name() { println!("{}", line);
{ let (rem, (_, _, gltf_name, _, scene_name)) = tuple((
let line = "name Van"; tag("model"),
let parsed = parse_save_name(line).unwrap(); take(1usize),
let expected = Name::new("Van"); parse_string,
take(1usize),
parse_string,
))(line)?;
assert_eq!(parsed, expected); debug_assert_eq!(rem, "");
}
{
let line = "name Big Mike";
let parsed = parse_save_name(line).unwrap();
let expected = Name::new("Big Mike");
assert_eq!(parsed, expected); Ok((String::from(gltf_name), String::from(scene_name)))
} }
}
#[test] #[test]
fn test_parse_uuid() { fn test_parse_model() {
let line = "uuid 1c16ab9a-5f79-4340-8469-4086f69c64f2"; let line = "model \"models/foo.glb\" \"My Scene\"";
let parsed = parse_save_uuid(line).unwrap(); let parsed = parse_save_model(line).unwrap();
let expected = Uuid::parse_str("1c16ab9a-5f79-4340-8469-4086f69c64f2").unwrap(); let expected = (String::from("models/foo.glb"), String::from("My Scene"));
assert_eq!(parsed, expected); 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,24 +276,36 @@ 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(
query: Query<(Entity, &Handle<SaveEntity>)>, query: Query<(Entity, &Handle<SaveEntity>)>,
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 {
AssetEvent::LoadedWithDependencies { id } => { AssetEvent::LoadedWithDependencies { id } => {
query query
.iter() .iter()
.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>>();
}
}); });
} }
_ => () _ => (),
} }
}) })
} }

Loading…
Cancel
Save