Compare commits
9 Commits
4bc704aff0
...
21e67e04ca
| Author | SHA1 | Date |
|---|---|---|
|
|
21e67e04ca | 1 year ago |
|
|
07bbbfa25b | 1 year ago |
|
|
9a83c06716 | 1 year ago |
|
|
a54255c363 | 1 year ago |
|
|
edb15ffe49 | 1 year ago |
|
|
84744a9397 | 1 year ago |
|
|
cfe3a9ce17 | 1 year ago |
|
|
8027af5303 | 1 year ago |
|
|
63fab851ac | 1 year ago |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,18 @@
|
||||
# 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>` Human readable name for entity
|
||||
* `uuid <UUID string>` Globally unique ID for entity
|
||||
* `transform translation <f32> <f32> <f32> rotation <f32> <f32> <f32> <f32> scale <f32> <f32> <f32>` 3D Location, Rotation, and Scale for entity
|
||||
* `model "<path string>" "<string>"` Declares the entity's 3d model
|
||||
* `camera` Marks the entity as a camera
|
||||
@ -0,0 +1,4 @@
|
||||
name camera
|
||||
uuid 2e45b7e9-6722-4d50-8ea5-67f25b8b0f62
|
||||
transform translation 2.0 2.0 0.0 rotation 0.0 0.0 0.0 1.0 scale 1.0 1.0 1.0
|
||||
camera
|
||||
@ -0,0 +1,4 @@
|
||||
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
|
||||
model "models/van.glb" "Scene"
|
||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,127 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Menu Plugin; empty struct for Plugin impl
|
||||
pub(crate) struct CameraPlugin;
|
||||
|
||||
impl Plugin for CameraPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(
|
||||
Update,
|
||||
move_editor_fly_camera.run_if(any_with_component::<FlyCamera>),
|
||||
);
|
||||
app.add_systems(
|
||||
Update,
|
||||
rotate_editor_fly_camera.run_if(any_with_component::<FlyCamera>),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct FlyCamera;
|
||||
|
||||
/// Fly camera system for moving around like a drone
|
||||
/// TODO: Only if key is pressed!
|
||||
fn move_editor_fly_camera(
|
||||
mut cameras: Query<(&Camera, &mut Transform), With<FlyCamera>>,
|
||||
windows: Query<&Window>,
|
||||
primary_window: Query<Entity, With<PrimaryWindow>>,
|
||||
keys: Res<ButtonInput<KeyCode>>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
(keys.any_pressed([
|
||||
KeyCode::KeyW,
|
||||
KeyCode::KeyS,
|
||||
KeyCode::KeyA,
|
||||
KeyCode::KeyD,
|
||||
KeyCode::KeyQ,
|
||||
KeyCode::KeyE,
|
||||
]))
|
||||
.then(|| {
|
||||
// Iterate over all cameras
|
||||
cameras.iter_mut().for_each(|(c, mut t)| {
|
||||
// Determine which window this camera is attached to
|
||||
let target_window = match c.target {
|
||||
RenderTarget::Window(wr) => match wr {
|
||||
WindowRef::Entity(e) => Some(e),
|
||||
WindowRef::Primary => Some(primary_window.get_single().unwrap()),
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
let window = windows.get(target_window.unwrap()).unwrap();
|
||||
|
||||
// If the target window is focused
|
||||
window.focused.then(|| {
|
||||
let move_speed = if keys.pressed(KeyCode::ShiftLeft) {
|
||||
16.0
|
||||
} else {
|
||||
4.0
|
||||
};
|
||||
let mut delta = Vec3::ZERO;
|
||||
if keys.pressed(KeyCode::KeyW) {
|
||||
delta += t.forward() * move_speed * time.delta_seconds()
|
||||
}
|
||||
if keys.pressed(KeyCode::KeyS) {
|
||||
delta += t.back() * move_speed * time.delta_seconds()
|
||||
}
|
||||
if keys.pressed(KeyCode::KeyA) {
|
||||
delta += t.left() * move_speed * time.delta_seconds()
|
||||
}
|
||||
if keys.pressed(KeyCode::KeyD) {
|
||||
delta += t.right() * move_speed * time.delta_seconds()
|
||||
}
|
||||
if keys.pressed(KeyCode::KeyE) {
|
||||
delta += Vec3::Y * move_speed * time.delta_seconds()
|
||||
}
|
||||
if keys.pressed(KeyCode::KeyQ) {
|
||||
delta += Vec3::NEG_Y * move_speed * time.delta_seconds()
|
||||
}
|
||||
t.translation += delta;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn rotate_editor_fly_camera(
|
||||
mut cameras: Query<(&Camera, &mut Transform), With<FlyCamera>>,
|
||||
windows: Query<&Window>,
|
||||
primary_window: Query<Entity, With<PrimaryWindow>>,
|
||||
mouse: Res<ButtonInput<MouseButton>>,
|
||||
mut cursor_events: EventReader<CursorMoved>,
|
||||
) {
|
||||
(!cursor_events.is_empty()).then(|| {
|
||||
// Iterate over all cameras
|
||||
cameras.iter_mut().for_each(|(c, mut t)| {
|
||||
// Determine which window this camera is attached to
|
||||
let target_window = match c.target {
|
||||
RenderTarget::Window(wr) => match wr {
|
||||
WindowRef::Entity(e) => Some(e),
|
||||
WindowRef::Primary => Some(primary_window.get_single().unwrap()),
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
let window = windows.get(target_window.unwrap()).unwrap();
|
||||
if mouse.pressed(MouseButton::Middle) {
|
||||
cursor_events
|
||||
.read()
|
||||
.filter_map(|CursorMoved { delta, window, .. }| {
|
||||
(*window == target_window.unwrap()).then_some(delta)
|
||||
})
|
||||
.for_each(|delta| {
|
||||
if let Some(Vec2 { x, y }) = delta {
|
||||
// Cribbing from bevy_flycam
|
||||
// Link: https://github.com/sburris0/bevy_flycam/blob/baffe50e0961ad1491d467fa6ab5551f9f21db8f/src/lib.rs#L145-L151
|
||||
let (mut yaw, mut pitch, _) = t.rotation.to_euler(EulerRot::YXZ);
|
||||
let window_scale = window.height().min(window.width());
|
||||
let sensitivity = 0.00012;
|
||||
pitch -= (sensitivity * y * window_scale).to_radians();
|
||||
yaw -= (sensitivity * x * window_scale).to_radians();
|
||||
t.rotation = Quat::from_axis_angle(Vec3::Y, yaw)
|
||||
* Quat::from_axis_angle(Vec3::X, pitch);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cursor_events.clear();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1,5 +1,8 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub(crate) fn any_component_added<T: Component>(q: Query<Entity, Added<T>>) -> bool {
|
||||
///
|
||||
/// ECS scheduler active when an entity has a given component added
|
||||
///
|
||||
pub(crate) fn any_component_added<C: Component>(q: Query<Entity, Added<C>>) -> bool {
|
||||
!q.is_empty()
|
||||
}
|
||||
|
||||
@ -1,9 +1,26 @@
|
||||
pub(crate) use bevy::app::AppExit;
|
||||
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::window::PrimaryWindow;
|
||||
pub(crate) use bevy::window::WindowCloseRequested;
|
||||
pub(crate) use bevy::window::WindowRef;
|
||||
pub(crate) use bevy::{
|
||||
app::AppExit,
|
||||
asset::{io::Reader, AssetLoader, LoadContext, AsyncReadExt},
|
||||
color::palettes::css::{WHITE, GRAY, RED, DARK_GREEN, BLUE},
|
||||
gltf::Gltf,
|
||||
input::common_conditions::input_just_pressed,
|
||||
math::Vec3A,
|
||||
prelude::*,
|
||||
render::camera::RenderTarget,
|
||||
window::{WindowRef, WindowCloseRequested, PrimaryWindow},
|
||||
};
|
||||
pub(crate) use nom::{
|
||||
IResult,
|
||||
bytes::complete::{tag, take_until1},
|
||||
character::complete::space1,
|
||||
number::complete::float,
|
||||
sequence::tuple,
|
||||
};
|
||||
pub(crate) use uuid::Uuid;
|
||||
pub(crate) use thiserror::Error;
|
||||
|
||||
pub(crate) use crate::conditions::*;
|
||||
pub(crate) use crate::{
|
||||
camera::*,
|
||||
conditions::*,
|
||||
save::*,
|
||||
};
|
||||
|
||||
@ -0,0 +1,367 @@
|
||||
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>>();
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Reference in New Issue