Compare commits
3 Commits
3a528e447a
...
d08c235122
Author | SHA1 | Date |
---|---|---|
kaosat.dev | d08c235122 | |
kaosat.dev | 171ec7490a | |
kaosat.dev | ae9f07f549 |
|
@ -10,6 +10,9 @@ pub use registry::*;
|
||||||
pub mod blueprints;
|
pub mod blueprints;
|
||||||
pub use blueprints::*;
|
pub use blueprints::*;
|
||||||
|
|
||||||
|
pub mod save_load;
|
||||||
|
pub use save_load::*;
|
||||||
|
|
||||||
#[derive(Clone, Resource)]
|
#[derive(Clone, Resource)]
|
||||||
pub struct BlenvyConfig {
|
pub struct BlenvyConfig {
|
||||||
// registry
|
// registry
|
||||||
|
@ -26,6 +29,8 @@ pub struct BlenvyConfig {
|
||||||
// save & load
|
// save & load
|
||||||
pub(crate) save_component_filter: SceneFilter,
|
pub(crate) save_component_filter: SceneFilter,
|
||||||
pub(crate) save_resource_filter: SceneFilter,
|
pub(crate) save_resource_filter: SceneFilter,
|
||||||
|
//pub(crate) save_path: PathBuf,
|
||||||
|
// save_path: PathBuf::from("saves"),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -63,6 +68,7 @@ impl Plugin for BlenvyPlugin {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
ExportRegistryPlugin::default(),
|
ExportRegistryPlugin::default(),
|
||||||
BlueprintsPlugin::default(),
|
BlueprintsPlugin::default(),
|
||||||
|
SaveLoadPlugin::default()
|
||||||
))
|
))
|
||||||
.insert_resource(BlenvyConfig {
|
.insert_resource(BlenvyConfig {
|
||||||
export_registry: self.export_registry,
|
export_registry: self.export_registry,
|
||||||
|
|
|
@ -1,108 +1,59 @@
|
||||||
pub mod saveable;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
pub use saveable::*;
|
|
||||||
|
|
||||||
pub mod saving;
|
|
||||||
pub use saving::*;
|
|
||||||
|
|
||||||
pub mod loading;
|
|
||||||
pub use loading::*;
|
|
||||||
|
|
||||||
use bevy::core_pipeline::core_3d::{Camera3dDepthTextureUsage, ScreenSpaceTransmissionQuality};
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::prelude::{App, IntoSystemConfigs, Plugin};
|
|
||||||
use blenvy::GltfBlueprintsSet;
|
|
||||||
|
|
||||||
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
|
#[derive(Component, Reflect, Debug, Default)]
|
||||||
pub enum SavingSet {
|
#[reflect(Component)]
|
||||||
Save,
|
/// component used to mark any entity as Dynamic: aka add this to make sure your entity is going to be saved
|
||||||
}
|
pub struct Dynamic;
|
||||||
|
|
||||||
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
|
#[derive(Component, Reflect, Debug, Default)]
|
||||||
pub enum LoadingSet {
|
#[reflect(Component)]
|
||||||
Load,
|
/// marker component for entities that do not have parents, or whose parents should be ignored when serializing
|
||||||
}
|
pub(crate) struct RootEntity;
|
||||||
|
|
||||||
// Plugin configuration
|
#[derive(Component, Debug)]
|
||||||
|
/// internal helper component to store parents before resetting them
|
||||||
|
pub(crate) struct OriginalParent(pub(crate) Entity);
|
||||||
|
|
||||||
#[derive(Clone, Resource)]
|
|
||||||
pub struct SaveLoadConfig {
|
|
||||||
pub(crate) save_path: PathBuf,
|
|
||||||
pub(crate) component_filter: SceneFilter,
|
|
||||||
pub(crate) resource_filter: SceneFilter,
|
|
||||||
}
|
|
||||||
|
|
||||||
// define the plugin
|
|
||||||
|
|
||||||
pub struct SaveLoadPlugin {
|
|
||||||
pub component_filter: SceneFilter,
|
|
||||||
pub resource_filter: SceneFilter,
|
|
||||||
pub save_path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for SaveLoadPlugin {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
component_filter: SceneFilter::default(),
|
|
||||||
resource_filter: SceneFilter::default(),
|
|
||||||
save_path: PathBuf::from("scenes"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// Marker component to Flag the root entity of all static entities (immutables)
|
||||||
#[derive(Component, Reflect, Debug, Default)]
|
#[derive(Component, Reflect, Debug, Default)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct StaticEntitiesRoot;
|
pub struct StaticEntitiesRoot;
|
||||||
|
|
||||||
|
/// Marker component to Flag the root entity of all dynamic entities (mutables)
|
||||||
#[derive(Component, Reflect, Debug, Default)]
|
#[derive(Component, Reflect, Debug, Default)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct DynamicEntitiesRoot;
|
pub struct DynamicEntitiesRoot;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Resource, Clone, Debug, Default, Reflect)]
|
||||||
|
#[reflect(Resource)]
|
||||||
|
pub struct StaticEntitiesBlueprintInfo {
|
||||||
|
//pub blueprint_info: BlueprintInfo,
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub mod saving;
|
||||||
|
pub use saving::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
/// Plugin for saving & loading
|
||||||
|
pub struct SaveLoadPlugin {}
|
||||||
|
|
||||||
impl Plugin for SaveLoadPlugin {
|
impl Plugin for SaveLoadPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.register_type::<Dynamic>()
|
app.register_type::<Dynamic>()
|
||||||
.register_type::<StaticEntitiesRoot>()
|
.register_type::<StaticEntitiesRoot>()
|
||||||
// TODO: remove these in bevy 0.13, as these are now registered by default
|
|
||||||
.register_type::<Camera3dDepthTextureUsage>()
|
|
||||||
.register_type::<ScreenSpaceTransmissionQuality>()
|
|
||||||
.register_type::<StaticEntitiesStorage>()
|
|
||||||
.add_event::<SaveRequest>()
|
|
||||||
.add_event::<LoadRequest>()
|
|
||||||
.add_event::<LoadingFinished>()
|
|
||||||
.add_event::<SavingFinished>()
|
|
||||||
.insert_resource(SaveLoadConfig {
|
|
||||||
save_path: self.save_path.clone(),
|
|
||||||
|
|
||||||
component_filter: self.component_filter.clone(),
|
.add_event::<SaveRequest>()
|
||||||
resource_filter: self.resource_filter.clone(),
|
.add_event::<SaveFinished>()
|
||||||
})
|
|
||||||
.configure_sets(
|
|
||||||
Update,
|
|
||||||
(LoadingSet::Load).chain().before(GltfBlueprintsSet::Spawn), //.before(GltfComponentsSet::Injection)
|
|
||||||
)
|
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PreUpdate,
|
Update,
|
||||||
(prepare_save_game, apply_deferred, save_game, cleanup_save)
|
(prepare_save_game, apply_deferred, save_game, cleanup_save)
|
||||||
.chain()
|
.chain()
|
||||||
.run_if(should_save),
|
.run_if(should_save),
|
||||||
)
|
)
|
||||||
.add_systems(Update, mark_load_requested)
|
;
|
||||||
.add_systems(
|
|
||||||
Update,
|
|
||||||
(unload_world, apply_deferred, load_game)
|
|
||||||
.chain()
|
|
||||||
.run_if(resource_exists::<LoadRequested>)
|
|
||||||
.run_if(not(resource_exists::<LoadFirstStageDone>))
|
|
||||||
.in_set(LoadingSet::Load),
|
|
||||||
)
|
|
||||||
.add_systems(
|
|
||||||
Update,
|
|
||||||
(load_static, apply_deferred, cleanup_loaded_scene)
|
|
||||||
.chain()
|
|
||||||
.run_if(resource_exists::<LoadFirstStageDone>)
|
|
||||||
// .run_if(in_state(AppState::LoadingGame))
|
|
||||||
.in_set(LoadingSet::Load),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
pub mod saveable;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub use saveable::*;
|
||||||
|
|
||||||
|
pub mod saving;
|
||||||
|
pub use saving::*;
|
||||||
|
|
||||||
|
pub mod loading;
|
||||||
|
pub use loading::*;
|
||||||
|
|
||||||
|
use bevy::core_pipeline::core_3d::{Camera3dDepthTextureUsage, ScreenSpaceTransmissionQuality};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::prelude::{App, IntoSystemConfigs, Plugin};
|
||||||
|
use blenvy::GltfBlueprintsSet;
|
||||||
|
|
||||||
|
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
|
pub enum SavingSet {
|
||||||
|
Save,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
|
pub enum LoadingSet {
|
||||||
|
Load,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Debug, Default)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct StaticEntitiesRoot;
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Debug, Default)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct DynamicEntitiesRoot;
|
||||||
|
|
||||||
|
impl Plugin for SaveLoadPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.register_type::<Dynamic>()
|
||||||
|
.register_type::<StaticEntitiesRoot>()
|
||||||
|
// TODO: remove these in bevy 0.13, as these are now registered by default
|
||||||
|
.register_type::<Camera3dDepthTextureUsage>()
|
||||||
|
.register_type::<ScreenSpaceTransmissionQuality>()
|
||||||
|
.register_type::<StaticEntitiesStorage>()
|
||||||
|
.add_event::<SaveRequest>()
|
||||||
|
.add_event::<LoadRequest>()
|
||||||
|
.add_event::<LoadingFinished>()
|
||||||
|
.add_event::<SavingFinished>()
|
||||||
|
.configure_sets(
|
||||||
|
Update,
|
||||||
|
(LoadingSet::Load).chain().before(GltfBlueprintsSet::Spawn), //.before(GltfComponentsSet::Injection)
|
||||||
|
)
|
||||||
|
.add_systems(
|
||||||
|
PreUpdate,
|
||||||
|
(prepare_save_game, apply_deferred, save_game, cleanup_save)
|
||||||
|
.chain()
|
||||||
|
.run_if(should_save),
|
||||||
|
)
|
||||||
|
.add_systems(Update, mark_load_requested)
|
||||||
|
.add_systems(
|
||||||
|
Update,
|
||||||
|
(unload_world, apply_deferred, load_game)
|
||||||
|
.chain()
|
||||||
|
.run_if(resource_exists::<LoadRequested>)
|
||||||
|
.run_if(not(resource_exists::<LoadFirstStageDone>))
|
||||||
|
.in_set(LoadingSet::Load),
|
||||||
|
)
|
||||||
|
.add_systems(
|
||||||
|
Update,
|
||||||
|
(load_static, apply_deferred, cleanup_loaded_scene)
|
||||||
|
.chain()
|
||||||
|
.run_if(resource_exists::<LoadFirstStageDone>)
|
||||||
|
// .run_if(in_state(AppState::LoadingGame))
|
||||||
|
.in_set(LoadingSet::Load),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::tasks::IoTaskPool;
|
||||||
|
use blenvy::{BlueprintName, InBlueprint, Library, SpawnHere};
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::{DynamicEntitiesRoot, SaveLoadConfig, StaticEntitiesRoot};
|
||||||
|
|
||||||
|
#[derive(Event, Debug)]
|
||||||
|
pub struct SaveRequest {
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Event)]
|
||||||
|
pub struct SavingFinished;
|
||||||
|
|
||||||
|
pub fn should_save(save_requests: EventReader<SaveRequest>) -> bool {
|
||||||
|
!save_requests.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Clone, Debug, Default, Reflect)]
|
||||||
|
#[reflect(Resource)]
|
||||||
|
pub struct StaticEntitiesStorage {
|
||||||
|
pub name: String,
|
||||||
|
pub library_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Debug, Default)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
/// marker component for entities that do not have parents, or whose parents should be ignored when serializing
|
||||||
|
pub(crate) struct RootEntity;
|
||||||
|
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
/// internal helper component to store parents before resetting them
|
||||||
|
pub(crate) struct OriginalParent(pub(crate) Entity);
|
||||||
|
|
||||||
|
// any child of dynamic/ saveable entities that is not saveable itself should be removed from the list of children
|
||||||
|
pub(crate) fn prepare_save_game(
|
||||||
|
saveables: Query<Entity, (With<Dynamic>, With<BlueprintName>)>,
|
||||||
|
root_entities: Query<Entity, Or<(With<DynamicEntitiesRoot>, Without<Parent>)>>, // With<DynamicEntitiesRoot>
|
||||||
|
dynamic_entities: Query<(Entity, &Parent, Option<&Children>), With<Dynamic>>,
|
||||||
|
static_entities: Query<(Entity, &BlueprintName, Option<&Library>), With<StaticEntitiesRoot>>,
|
||||||
|
|
||||||
|
mut commands: Commands,
|
||||||
|
) {
|
||||||
|
for entity in saveables.iter() {
|
||||||
|
commands.entity(entity).insert(SpawnHere);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (entity, parent, children) in dynamic_entities.iter() {
|
||||||
|
let parent = parent.get();
|
||||||
|
if root_entities.contains(parent) {
|
||||||
|
commands.entity(entity).insert(RootEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(children) = children {
|
||||||
|
for sub_child in children.iter() {
|
||||||
|
if !dynamic_entities.contains(*sub_child) {
|
||||||
|
commands.entity(*sub_child).insert(OriginalParent(entity));
|
||||||
|
commands.entity(entity).remove_children(&[*sub_child]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (_, blueprint_name, library) in static_entities.iter() {
|
||||||
|
let library_path: String = library.map_or_else(|| "", |l| l.0.to_str().unwrap()).into();
|
||||||
|
commands.insert_resource(StaticEntitiesStorage {
|
||||||
|
name: blueprint_name.0.clone(),
|
||||||
|
library_path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn save_game(world: &mut World) {
|
||||||
|
info!("saving");
|
||||||
|
|
||||||
|
let mut save_path: String = "".into();
|
||||||
|
let mut events = world.resource_mut::<Events<SaveRequest>>();
|
||||||
|
|
||||||
|
for event in events.get_reader().read(&events) {
|
||||||
|
info!("SAVE EVENT !! {:?}", event);
|
||||||
|
save_path.clone_from(&event.path);
|
||||||
|
}
|
||||||
|
events.clear();
|
||||||
|
|
||||||
|
let saveable_entities: Vec<Entity> = world
|
||||||
|
.query_filtered::<Entity, (With<Dynamic>, Without<InBlueprint>, Without<RootEntity>)>()
|
||||||
|
.iter(world)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let saveable_root_entities: Vec<Entity> = world
|
||||||
|
.query_filtered::<Entity, (With<Dynamic>, Without<InBlueprint>, With<RootEntity>)>()
|
||||||
|
.iter(world)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
info!("saveable entities {}", saveable_entities.len());
|
||||||
|
info!("saveable root entities {}", saveable_root_entities.len());
|
||||||
|
|
||||||
|
let save_load_config = world
|
||||||
|
.get_resource::<SaveLoadConfig>()
|
||||||
|
.expect("SaveLoadConfig should exist at this stage");
|
||||||
|
|
||||||
|
// we hardcode some of the always allowed types
|
||||||
|
let filter = save_load_config
|
||||||
|
.component_filter
|
||||||
|
.clone()
|
||||||
|
.allow::<Parent>()
|
||||||
|
.allow::<Children>()
|
||||||
|
.allow::<BlueprintName>()
|
||||||
|
.allow::<SpawnHere>()
|
||||||
|
.allow::<Dynamic>()
|
||||||
|
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
// for root entities, it is the same EXCEPT we make sure parents are not included
|
||||||
|
let filter_root = filter.clone().deny::<Parent>();
|
||||||
|
|
||||||
|
let filter_resources = save_load_config
|
||||||
|
.resource_filter
|
||||||
|
.clone()
|
||||||
|
.allow::<StaticEntitiesStorage>()
|
||||||
|
;
|
||||||
|
|
||||||
|
// for default stuff
|
||||||
|
let scene_builder = DynamicSceneBuilder::from_world(world)
|
||||||
|
.with_filter(filter.clone())
|
||||||
|
.with_resource_filter(filter_resources.clone());
|
||||||
|
|
||||||
|
let mut dyn_scene = scene_builder
|
||||||
|
.extract_resources()
|
||||||
|
.extract_entities(saveable_entities.clone().into_iter())
|
||||||
|
.remove_empty_entities()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// for root entities
|
||||||
|
let scene_builder_root = DynamicSceneBuilder::from_world(world)
|
||||||
|
.with_filter(filter_root.clone())
|
||||||
|
.with_resource_filter(filter_resources.clone());
|
||||||
|
|
||||||
|
// FIXME : add back
|
||||||
|
let mut dyn_scene_root = scene_builder_root
|
||||||
|
.extract_resources()
|
||||||
|
.extract_entities(
|
||||||
|
saveable_root_entities.clone().into_iter(), // .chain(static_world_markers.into_iter()),
|
||||||
|
)
|
||||||
|
.remove_empty_entities()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
dyn_scene.entities.append(&mut dyn_scene_root.entities);
|
||||||
|
// dyn_scene.resources.append(&mut dyn_scene_root.resources);
|
||||||
|
|
||||||
|
let serialized_scene = dyn_scene
|
||||||
|
.serialize(&world.resource::<AppTypeRegistry>().read())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let save_path = Path::new("assets")
|
||||||
|
.join(&save_load_config.save_path)
|
||||||
|
.join(Path::new(save_path.as_str())); // Path::new(&save_load_config.save_path).join(Path::new(save_path.as_str()));
|
||||||
|
info!("saving game to {:?}", save_path);
|
||||||
|
|
||||||
|
// world.send_event(SavingFinished);
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
IoTaskPool::get()
|
||||||
|
.spawn(async move {
|
||||||
|
// Write the scene RON data to file
|
||||||
|
File::create(save_path)
|
||||||
|
.and_then(|mut file| file.write(serialized_scene.as_bytes()))
|
||||||
|
.expect("Error while writing save to file");
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn cleanup_save(
|
||||||
|
needs_parent_reset: Query<(Entity, &OriginalParent)>,
|
||||||
|
mut saving_finished: EventWriter<SavingFinished>,
|
||||||
|
mut commands: Commands,
|
||||||
|
) {
|
||||||
|
for (entity, original_parent) in needs_parent_reset.iter() {
|
||||||
|
commands.entity(original_parent.0).add_child(entity);
|
||||||
|
}
|
||||||
|
commands.remove_resource::<StaticEntitiesStorage>();
|
||||||
|
saving_finished.send(SavingFinished);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
pub(crate) fn cleanup_save(mut world: &mut World) {
|
||||||
|
|
||||||
|
let mut query = world.query::<(Entity, &OriginalParent)>();
|
||||||
|
for (mut entity, original_parent) in query.iter_mut(&mut world) {
|
||||||
|
let e = world.entity_mut(original_parent.0);
|
||||||
|
// .add_child(entity);
|
||||||
|
}
|
||||||
|
}*/
|
|
@ -1,7 +0,0 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Debug, Default)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
/// component used to mark any entity as Dynamic: aka add this to make sure your entity is going to be saved
|
|
||||||
pub struct Dynamic(pub bool);
|
|
||||||
|
|
|
@ -1,55 +1,42 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy::tasks::IoTaskPool;
|
|
||||||
use blenvy::{BlueprintName, InBlueprint, Library, SpawnHere};
|
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::{Dynamic, DynamicEntitiesRoot, SaveLoadConfig, StaticEntitiesRoot};
|
use bevy::render::camera::{CameraMainTextureUsages, CameraRenderGraph};
|
||||||
|
use bevy::{prelude::*, tasks::IoTaskPool};
|
||||||
|
use bevy::prelude::World;
|
||||||
|
|
||||||
|
use crate::{BlenvyConfig, BlueprintInfo, Dynamic, FromBlueprint, RootEntity, SpawnBlueprint};
|
||||||
|
|
||||||
|
use super::{DynamicEntitiesRoot, OriginalParent, StaticEntitiesRoot};
|
||||||
|
|
||||||
#[derive(Event, Debug)]
|
#[derive(Event, Debug)]
|
||||||
pub struct SaveRequest {
|
pub struct SaveRequest {
|
||||||
pub path: String,
|
pub path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Event)]
|
#[derive(Event)]
|
||||||
pub struct SavingFinished;
|
pub struct SaveFinished; // TODO: merge the the events above
|
||||||
|
|
||||||
pub fn should_save(save_requests: EventReader<SaveRequest>) -> bool {
|
pub fn should_save(save_requests: EventReader<SaveRequest>) -> bool {
|
||||||
!save_requests.is_empty()
|
!save_requests.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Clone, Debug, Default, Reflect)]
|
|
||||||
#[reflect(Resource)]
|
|
||||||
pub struct StaticEntitiesStorage {
|
|
||||||
pub name: String,
|
|
||||||
pub library_path: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Debug, Default)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
/// marker component for entities that do not have parents, or whose parents should be ignored when serializing
|
|
||||||
pub(crate) struct RootEntity;
|
|
||||||
|
|
||||||
#[derive(Component, Debug)]
|
|
||||||
/// internal helper component to store parents before resetting them
|
|
||||||
pub(crate) struct OriginalParent(pub(crate) Entity);
|
|
||||||
|
|
||||||
// any child of dynamic/ saveable entities that is not saveable itself should be removed from the list of children
|
// any child of dynamic/ saveable entities that is not saveable itself should be removed from the list of children
|
||||||
pub(crate) fn prepare_save_game(
|
pub(crate) fn prepare_save_game(
|
||||||
saveables: Query<Entity, (With<Dynamic>, With<BlueprintName>)>,
|
saveables: Query<Entity, (With<Dynamic>, With<BlueprintInfo>)>,
|
||||||
root_entities: Query<Entity, Or<(With<DynamicEntitiesRoot>, Without<Parent>)>>, // With<DynamicEntitiesRoot>
|
root_entities: Query<Entity, Or<(With<DynamicEntitiesRoot>, Without<Parent>)>>, // With<DynamicEntitiesRoot>
|
||||||
dynamic_entities: Query<(Entity, &Parent, Option<&Children>), With<Dynamic>>,
|
dynamic_entities: Query<(Entity, &Parent, Option<&Children>), With<Dynamic>>,
|
||||||
static_entities: Query<(Entity, &BlueprintName, Option<&Library>), With<StaticEntitiesRoot>>,
|
static_entities: Query<(Entity, &BlueprintInfo), With<StaticEntitiesRoot>>,
|
||||||
|
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for entity in saveables.iter() {
|
for entity in saveables.iter() {
|
||||||
commands.entity(entity).insert(SpawnHere);
|
commands.entity(entity).insert(SpawnBlueprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (entity, parent, children) in dynamic_entities.iter() {
|
for (entity, parent, children) in dynamic_entities.iter() {
|
||||||
|
println!("prepare save game");
|
||||||
let parent = parent.get();
|
let parent = parent.get();
|
||||||
if root_entities.contains(parent) {
|
if root_entities.contains(parent) {
|
||||||
commands.entity(entity).insert(RootEntity);
|
commands.entity(entity).insert(RootEntity);
|
||||||
|
@ -64,14 +51,17 @@ pub(crate) fn prepare_save_game(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (_, blueprint_name, library) in static_entities.iter() {
|
/*for (_, blueprint_name) in static_entities.iter() {
|
||||||
let library_path: String = library.map_or_else(|| "", |l| l.0.to_str().unwrap()).into();
|
let library_path: String = library.map_or_else(|| "", |l| l.0.to_str().unwrap()).into();
|
||||||
commands.insert_resource(StaticEntitiesStorage {
|
commands.insert_resource(StaticEntitiesStorage {
|
||||||
name: blueprint_name.0.clone(),
|
name: blueprint_name.0.clone(),
|
||||||
library_path,
|
library_path,
|
||||||
});
|
});
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub(crate) fn save_game(world: &mut World) {
|
pub(crate) fn save_game(world: &mut World) {
|
||||||
info!("saving");
|
info!("saving");
|
||||||
|
|
||||||
|
@ -85,39 +75,49 @@ pub(crate) fn save_game(world: &mut World) {
|
||||||
events.clear();
|
events.clear();
|
||||||
|
|
||||||
let saveable_entities: Vec<Entity> = world
|
let saveable_entities: Vec<Entity> = world
|
||||||
.query_filtered::<Entity, (With<Dynamic>, Without<InBlueprint>, Without<RootEntity>)>()
|
// .query_filtered::<Entity, (With<Dynamic>, Without<FromBlueprint>, Without<RootEntity>)>()
|
||||||
|
.query_filtered::<Entity, (With<Dynamic>, Without<RootEntity>)>()
|
||||||
.iter(world)
|
.iter(world)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let saveable_root_entities: Vec<Entity> = world
|
let saveable_root_entities: Vec<Entity> = world
|
||||||
.query_filtered::<Entity, (With<Dynamic>, Without<InBlueprint>, With<RootEntity>)>()
|
.query_filtered::<Entity, (With<Dynamic>, With<RootEntity>)>()
|
||||||
|
//.query_filtered::<Entity, (With<Dynamic>, Without<FromBlueprint>, With<RootEntity>)>()
|
||||||
.iter(world)
|
.iter(world)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
info!("saveable entities {}", saveable_entities.len());
|
info!("saveable entities {}", saveable_entities.len());
|
||||||
info!("saveable root entities {}", saveable_root_entities.len());
|
info!("saveable root entities {}", saveable_root_entities.len());
|
||||||
|
|
||||||
let save_load_config = world
|
let config = world
|
||||||
.get_resource::<SaveLoadConfig>()
|
.get_resource::<BlenvyConfig>()
|
||||||
.expect("SaveLoadConfig should exist at this stage");
|
.expect("Blenvy configuration should exist at this stage");
|
||||||
|
|
||||||
// we hardcode some of the always allowed types
|
// we hardcode some of the always allowed types
|
||||||
let filter = save_load_config
|
let filter = config
|
||||||
.component_filter
|
.save_component_filter
|
||||||
.clone()
|
.clone()
|
||||||
.allow::<Parent>()
|
.allow::<Parent>()
|
||||||
.allow::<Children>()
|
.allow::<Children>()
|
||||||
.allow::<BlueprintName>()
|
.allow::<BlueprintInfo>()
|
||||||
.allow::<SpawnHere>()
|
.allow::<SpawnBlueprint>()
|
||||||
.allow::<Dynamic>();
|
.allow::<Dynamic>()
|
||||||
|
|
||||||
|
/*.deny::<CameraRenderGraph>()
|
||||||
|
.deny::<CameraMainTextureUsages>()
|
||||||
|
.deny::<Handle<Mesh>>()
|
||||||
|
.deny::<Handle<StandardMaterial>>() */
|
||||||
|
;
|
||||||
|
|
||||||
// for root entities, it is the same EXCEPT we make sure parents are not included
|
// for root entities, it is the same EXCEPT we make sure parents are not included
|
||||||
let filter_root = filter.clone().deny::<Parent>();
|
let filter_root = filter.clone().deny::<Parent>();
|
||||||
|
|
||||||
let filter_resources = save_load_config
|
let filter_resources = config.clone()
|
||||||
.resource_filter
|
.save_resource_filter
|
||||||
.clone()
|
.deny::<Time<Real>>()
|
||||||
.allow::<StaticEntitiesStorage>();
|
|
||||||
|
.clone();
|
||||||
|
//.allow::<StaticEntitiesStorage>();
|
||||||
|
|
||||||
// for default stuff
|
// for default stuff
|
||||||
let scene_builder = DynamicSceneBuilder::from_world(world)
|
let scene_builder = DynamicSceneBuilder::from_world(world)
|
||||||
|
@ -143,15 +143,15 @@ pub(crate) fn save_game(world: &mut World) {
|
||||||
.remove_empty_entities()
|
.remove_empty_entities()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
dyn_scene.entities.append(&mut dyn_scene_root.entities);
|
// dyn_scene.entities.append(&mut dyn_scene_root.entities);
|
||||||
// dyn_scene.resources.append(&mut dyn_scene_root.resources);
|
// dyn_scene.resources.append(&mut dyn_scene_root.resources);
|
||||||
|
|
||||||
let serialized_scene = dyn_scene
|
let serialized_scene = dyn_scene
|
||||||
.serialize(&world.resource::<AppTypeRegistry>().read())
|
.serialize(&world.resource::<AppTypeRegistry>().read())
|
||||||
.unwrap();
|
.expect("filtered scene should serialize correctly");
|
||||||
|
|
||||||
let save_path = Path::new("assets")
|
let save_path = Path::new("assets")
|
||||||
.join(&save_load_config.save_path)
|
//.join(&config.save_path)
|
||||||
.join(Path::new(save_path.as_str())); // Path::new(&save_load_config.save_path).join(Path::new(save_path.as_str()));
|
.join(Path::new(save_path.as_str())); // Path::new(&save_load_config.save_path).join(Path::new(save_path.as_str()));
|
||||||
info!("saving game to {:?}", save_path);
|
info!("saving game to {:?}", save_path);
|
||||||
|
|
||||||
|
@ -170,21 +170,12 @@ pub(crate) fn save_game(world: &mut World) {
|
||||||
|
|
||||||
pub(crate) fn cleanup_save(
|
pub(crate) fn cleanup_save(
|
||||||
needs_parent_reset: Query<(Entity, &OriginalParent)>,
|
needs_parent_reset: Query<(Entity, &OriginalParent)>,
|
||||||
mut saving_finished: EventWriter<SavingFinished>,
|
mut saving_finished: EventWriter<SaveFinished>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, original_parent) in needs_parent_reset.iter() {
|
for (entity, original_parent) in needs_parent_reset.iter() {
|
||||||
commands.entity(original_parent.0).add_child(entity);
|
commands.entity(original_parent.0).add_child(entity);
|
||||||
}
|
}
|
||||||
commands.remove_resource::<StaticEntitiesStorage>();
|
// commands.remove_resource::<StaticEntitiesStorage>();
|
||||||
saving_finished.send(SavingFinished);
|
saving_finished.send(SaveFinished);
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
pub(crate) fn cleanup_save(mut world: &mut World) {
|
|
||||||
|
|
||||||
let mut query = world.query::<(Entity, &OriginalParent)>();
|
|
||||||
for (mut entity, original_parent) in query.iter_mut(&mut world) {
|
|
||||||
let e = world.entity_mut(original_parent.0);
|
|
||||||
// .add_child(entity);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
Binary file not shown.
|
@ -1 +0,0 @@
|
||||||
({})
|
|
|
@ -1,8 +0,0 @@
|
||||||
({
|
|
||||||
"world":File (path: "models/World.glb"),
|
|
||||||
"world_dynamic":File (path: "models/World_dynamic.glb"),
|
|
||||||
|
|
||||||
"models": Folder (
|
|
||||||
path: "models/library",
|
|
||||||
),
|
|
||||||
})
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,5 @@
|
||||||
|
(
|
||||||
|
assets:
|
||||||
|
[
|
||||||
|
]
|
||||||
|
)
|
Binary file not shown.
|
@ -0,0 +1,5 @@
|
||||||
|
(
|
||||||
|
assets:
|
||||||
|
[
|
||||||
|
]
|
||||||
|
)
|
Binary file not shown.
|
@ -0,0 +1,9 @@
|
||||||
|
(
|
||||||
|
assets:
|
||||||
|
[
|
||||||
|
("Pillar", File ( path: "blueprints/Pillar.glb" )),
|
||||||
|
("Stone", File ( path: "materials/Stone.glb" )),
|
||||||
|
("Mover", File ( path: "blueprints/Mover.glb" )),
|
||||||
|
("Material.001", File ( path: "materials/Material.001.glb" )),
|
||||||
|
]
|
||||||
|
)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,14 +1,11 @@
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
|
||||||
use bevy::{prelude::*, utils::hashbrown::HashSet};
|
use bevy::{prelude::*, utils::hashbrown::HashSet};
|
||||||
use blenvy::{AddToGameWorld, BlenvyPlugin, BluePrintBundle, BlueprintInfo, DynamicBlueprintInstance, GameWorldTag, HideUntilReady, SpawnBlueprint};
|
use blenvy::{AddToGameWorld, BlenvyPlugin, BluePrintBundle, BlueprintInfo, Dynamic, DynamicBlueprintInstance, GameWorldTag, HideUntilReady, SaveRequest, SpawnBlueprint};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
mod core;
|
// mod game;
|
||||||
use crate::core::*;
|
// use game::*;
|
||||||
|
|
||||||
mod game;
|
|
||||||
use game::*;
|
|
||||||
|
|
||||||
mod component_examples;
|
mod component_examples;
|
||||||
use component_examples::*;
|
use component_examples::*;
|
||||||
|
@ -37,13 +34,12 @@ fn main() {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
// our custom plugins
|
// our custom plugins
|
||||||
CorePlugin, // reusable plugins
|
// GamePlugin, // specific to our game
|
||||||
GamePlugin, // specific to our game
|
|
||||||
ComponentsExamplesPlugin, // Showcases different type of components /structs
|
ComponentsExamplesPlugin, // Showcases different type of components /structs
|
||||||
))
|
))
|
||||||
|
|
||||||
.add_systems(Startup, setup_game)
|
.add_systems(Startup, setup_game)
|
||||||
.add_systems(Update, (spawn_blueprint_instance, save_game, load_game))
|
.add_systems(Update, (spawn_blueprint_instance, move_movers, save_game, load_game))
|
||||||
|
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
@ -60,6 +56,13 @@ fn setup_game(
|
||||||
HideUntilReady, // only reveal the level once it is ready
|
HideUntilReady, // only reveal the level once it is ready
|
||||||
GameWorldTag,
|
GameWorldTag,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// here we spawn our game world/level, which is also a blueprint !
|
||||||
|
commands.spawn((
|
||||||
|
BlueprintInfo::from_path("levels/World_dynamic.glb"), // all we need is a Blueprint info...
|
||||||
|
SpawnBlueprint, // and spawnblueprint to tell blenvy to spawn the blueprint now
|
||||||
|
HideUntilReady, // only reveal the level once it is ready
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// you can also spawn blueprint instances at runtime
|
// you can also spawn blueprint instances at runtime
|
||||||
|
@ -90,14 +93,46 @@ fn spawn_blueprint_instance(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn move_movers(
|
||||||
|
mut movers: Query<(&mut Transform), With<Dynamic>>
|
||||||
|
) {
|
||||||
|
for mut transform in movers.iter_mut(){
|
||||||
|
println!("moving dynamic entity");
|
||||||
|
transform.translation.x += 0.01;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn save_game(
|
fn save_game(
|
||||||
keycode: Res<ButtonInput<KeyCode>>,
|
keycode: Res<ButtonInput<KeyCode>>,
|
||||||
|
mut save_requests: EventWriter<SaveRequest>,
|
||||||
) {
|
) {
|
||||||
if keycode.just_pressed(KeyCode::KeyS) {
|
if keycode.just_pressed(KeyCode::KeyS) {
|
||||||
|
save_requests.send(SaveRequest {
|
||||||
|
path: "scenes/save.scn.ron".into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub fn request_save(
|
||||||
|
mut save_requests: EventWriter<SaveRequest>,
|
||||||
|
keycode: Res<ButtonInput<KeyCode>>,
|
||||||
|
|
||||||
|
current_state: Res<State<GameState>>,
|
||||||
|
mut next_game_state: ResMut<NextState<GameState>>,
|
||||||
|
) {
|
||||||
|
if keycode.just_pressed(KeyCode::KeyS)
|
||||||
|
&& (current_state.get() != &GameState::InLoading)
|
||||||
|
&& (current_state.get() != &GameState::InSaving)
|
||||||
|
{
|
||||||
|
next_game_state.set(GameState::InSaving);
|
||||||
|
save_requests.send(SaveRequest {
|
||||||
|
path: "save.scn.ron".into(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn load_game(
|
fn load_game(
|
||||||
keycode: Res<ButtonInput<KeyCode>>,
|
keycode: Res<ButtonInput<KeyCode>>,
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
|
from ...bevy_components.components.metadata import get_bevy_component_value_by_long_name
|
||||||
|
|
||||||
# checks if an object is dynamic
|
# checks if an object is dynamic
|
||||||
# TODO: for efficiency, it might make sense to write this flag semi automatically at the root level of the object so we can skip the inner loop
|
# TODO: for efficiency, it might make sense to write this flag semi automatically at the root level of the object so we can skip the inner loop
|
||||||
# TODO: we need to recompute these on blueprint changes too
|
# TODO: we need to recompute these on blueprint changes too
|
||||||
# even better, keep a list of dynamic objects per scene , updated only when needed ?
|
# even better, keep a list of dynamic objects per scene , updated only when needed ?
|
||||||
def is_object_dynamic(object):
|
def is_object_dynamic(object):
|
||||||
is_dynamic = object['Dynamic'] if 'Dynamic' in object else False
|
is_dynamic = get_bevy_component_value_by_long_name(object, 'blenvy::save_load::Dynamic') is not None
|
||||||
|
#is_dynamic = object['Dynamic'] if 'Dynamic' in object else False
|
||||||
# only look for data in the original collection if it is not alread marked as dynamic at instance level
|
# only look for data in the original collection if it is not alread marked as dynamic at instance level
|
||||||
if not is_dynamic and object.type == 'EMPTY' and hasattr(object, 'instance_collection') and object.instance_collection is not None :
|
if not is_dynamic and object.type == 'EMPTY' and hasattr(object, 'instance_collection') and object.instance_collection is not None :
|
||||||
#print("collection", object.instance_collection, "object", object.name)
|
#print("collection", object.instance_collection, "object", object.name)
|
||||||
|
@ -14,15 +16,18 @@ def is_object_dynamic(object):
|
||||||
collection_name = object.instance_collection.name
|
collection_name = object.instance_collection.name
|
||||||
original_collection = bpy.data.collections[collection_name]
|
original_collection = bpy.data.collections[collection_name]
|
||||||
|
|
||||||
|
is_dynamic = get_bevy_component_value_by_long_name(original_collection, 'blenvy::save_load::Dynamic') is not None
|
||||||
# scan original collection, look for a 'Dynamic' flag
|
# scan original collection, look for a 'Dynamic' flag
|
||||||
for object in original_collection.objects:
|
"""for object in original_collection.objects:
|
||||||
#print(" inner", object)
|
#print(" inner", object)
|
||||||
if object.type == 'EMPTY' and object.name.endswith("components"):
|
if object.type == 'EMPTY': #and object.name.endswith("components"):
|
||||||
for component_name in object.keys():
|
for component_name in object.keys():
|
||||||
#print(" compo", component_name)
|
#print(" compo", component_name)
|
||||||
if component_name == 'Dynamic':
|
if component_name == 'Dynamic':
|
||||||
is_dynamic = True
|
is_dynamic = True
|
||||||
break
|
break"""
|
||||||
|
print("IS OBJECT DYNAMIC", object, is_dynamic)
|
||||||
|
|
||||||
return is_dynamic
|
return is_dynamic
|
||||||
|
|
||||||
def is_object_static(object):
|
def is_object_static(object):
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import os
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from blenvy.core.helpers_collections import (traverse_tree)
|
from blenvy.core.helpers_collections import (traverse_tree)
|
||||||
from blenvy.core.object_makers import make_cube
|
from blenvy.core.object_makers import make_cube
|
||||||
from blenvy.materials.materials_helpers import add_material_info_to_objects, get_all_materials
|
|
||||||
from ..common.generate_temporary_scene_and_export import generate_temporary_scene_and_export
|
from ..common.generate_temporary_scene_and_export import generate_temporary_scene_and_export
|
||||||
from ..common.export_gltf import (generate_gltf_export_settings)
|
from ..common.export_gltf import (generate_gltf_export_settings)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue