chore(): cleanup & simplification pass (#122)

* closes #118 
* closes #110 
* closes #99
This commit is contained in:
Mark Moissette 2024-02-07 14:07:17 +01:00 committed by GitHub
parent 537f08b959
commit 0d708b800e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
387 changed files with 1611 additions and 21236 deletions

View File

@ -4,20 +4,20 @@ members = [
"crates/bevy_gltf_blueprints", "crates/bevy_gltf_blueprints",
"crates/bevy_gltf_save_load", "crates/bevy_gltf_save_load",
"crates/bevy_registry_export", "crates/bevy_registry_export",
"examples/common/",
"examples/bevy_gltf_components/basic/", "examples/bevy_gltf_components/basic/",
"examples/bevy_gltf_components/basic_wasm/",
"examples/bevy_gltf_blueprints/basic/", "examples/bevy_gltf_blueprints/basic/",
"examples/bevy_gltf_blueprints/basic_wasm/",
"examples/bevy_gltf_blueprints/basic_scene_components/",
"examples/bevy_gltf_blueprints/basic_xpbd_physics/", "examples/bevy_gltf_blueprints/basic_xpbd_physics/",
"examples/bevy_gltf_blueprints/nested_blueprints/",
"examples/bevy_gltf_blueprints/animation/", "examples/bevy_gltf_blueprints/animation/",
"examples/bevy_gltf_blueprints/multiple_levels/",
"examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles", "examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles",
"examples/bevy_gltf_blueprints/materials/", "examples/bevy_gltf_blueprints/materials/",
"examples/bevy_gltf_save_load/basic/",
"examples/bevy_registry_export/basic"
"examples/bevy_gltf_save_load/basic/",
"examples/bevy_registry_export/basic"
] ]
resolver = "2" resolver = "2"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bevy_gltf_blueprints" name = "bevy_gltf_blueprints"
version = "0.7.0" version = "0.7.1"
authors = ["Mark 'kaosat-dev' Moissette"] authors = ["Mark 'kaosat-dev' Moissette"]
description = "Adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy." description = "Adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy."
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow" homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"

View File

@ -332,16 +332,10 @@ https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/example
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic_xpbd_physics https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic_xpbd_physics
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic_scene_components
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/multiple_levels
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/materials https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/materials
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/nested_blueprints
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles

View File

@ -41,7 +41,7 @@ pub fn compute_descendant_aabb(
if let Ok(aabb) = existing_aabbs.get(*child) { if let Ok(aabb) = existing_aabbs.get(*child) {
chilren_aabbs.push(*aabb); chilren_aabbs.push(*aabb);
} else { } else {
let aabb = compute_descendant_aabb(*child, children, &existing_aabbs); let aabb = compute_descendant_aabb(*child, children, existing_aabbs);
chilren_aabbs.push(aabb); chilren_aabbs.push(aabb);
} }
} }
@ -57,5 +57,5 @@ pub fn compute_descendant_aabb(
return aabb; return aabb;
} }
return Aabb::default(); Aabb::default()
} }

View File

@ -3,14 +3,14 @@ use bevy::utils::HashMap;
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
/// storage for animations for a given entity (hierarchy), essentially a clone of gltf's named_animations /// storage for animations for a given entity (hierarchy), essentially a clone of gltf's `named_animations`
pub struct Animations { pub struct Animations {
pub named_animations: HashMap<String, Handle<AnimationClip>>, pub named_animations: HashMap<String, Handle<AnimationClip>>,
} }
#[derive(Component, Debug)] #[derive(Component, Debug)]
/// Stop gap helper component : this is inserted into a "root" entity (an entity representing a whole gltf file) /// Stop gap helper component : this is inserted into a "root" entity (an entity representing a whole gltf file)
/// so that the root entity knows which of its children contains an actualy AnimationPlayer component /// so that the root entity knows which of its children contains an actualy `AnimationPlayer` component
/// this is for convenience, because currently , Bevy's gltf parsing inserts AnimationPlayers "one level down" /// this is for convenience, because currently , Bevy's gltf parsing inserts `AnimationPlayers` "one level down"
/// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid /// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid
pub struct AnimationPlayerLink(pub Entity); pub struct AnimationPlayerLink(pub Entity);

View File

@ -42,7 +42,7 @@ impl CopyComponents {
let type_id = component_info.type_id().unwrap(); let type_id = component_info.type_id().unwrap();
if self.exclude.contains(&type_id) { if self.exclude.contains(&type_id) {
debug!("excluding component: {:?}", component_info.name()); debug!("excluding component: {:?}", component_info.name());
return None; None
} else { } else {
debug!( debug!(
"cloning: component: {:?} {:?}", "cloning: component: {:?} {:?}",
@ -51,32 +51,27 @@ impl CopyComponents {
); );
if let Some(type_registration) = registry.get(type_id) { if let Some(type_registration) = registry.get(type_id) {
return Some(type_registration); Some(type_registration)
} else { } else if self.stringent {
if self.stringent { return Some(registry.get(type_id).unwrap_or_else(|| {
return Some( panic!(
registry.get(type_id).expect( "cannot clone entity: component: {:?} is not registered",
format!(
"cannot clone entity: component: {:?} is not registered",
component_info.name()
)
.as_str(),
),
);
} else {
warn!(
"cannot clone component: component: {:?} is not registered",
component_info.name() component_info.name()
); )
return None; }));
} } else {
warn!(
"cannot clone component: component: {:?} is not registered",
component_info.name()
);
None
} }
} }
}) })
.map(|type_id| { .map(|type_id| {
return ( return (
type_id.data::<ReflectComponent>().unwrap().clone(), type_id.data::<ReflectComponent>().unwrap().clone(),
type_id.type_info().type_id().clone(), // we need the original type_id down the line type_id.type_info().type_id(), // we need the original type_id down the line
); );
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
@ -104,6 +99,6 @@ impl CopyComponents {
// This allows the command to be used in systems // This allows the command to be used in systems
impl Command for CopyComponents { impl Command for CopyComponents {
fn apply(self, world: &mut World) { fn apply(self, world: &mut World) {
self.transfer_components(world) self.transfer_components(world);
} }
} }

View File

@ -113,47 +113,47 @@ fn materials_library_enabled(blueprints_config: Res<BluePrintsConfig>) -> bool {
impl Plugin for BlueprintsPlugin { impl Plugin for BlueprintsPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(ComponentsFromGltfPlugin{ app.add_plugins(ComponentsFromGltfPlugin {
legacy_mode: self.legacy_mode legacy_mode: self.legacy_mode,
}) })
.register_type::<BlueprintName>() .register_type::<BlueprintName>()
.register_type::<MaterialInfo>() .register_type::<MaterialInfo>()
.register_type::<SpawnHere>() .register_type::<SpawnHere>()
.register_type::<Animations>() .register_type::<Animations>()
.insert_resource(BluePrintsConfig { .insert_resource(BluePrintsConfig {
format: self.format.clone(), format: self.format,
library_folder: self.library_folder.clone(), library_folder: self.library_folder.clone(),
aabbs: self.aabbs, aabbs: self.aabbs,
aabb_cache: HashMap::new(), aabb_cache: HashMap::new(),
material_library: self.material_library, material_library: self.material_library,
material_library_folder: self.material_library_folder.clone(), material_library_folder: self.material_library_folder.clone(),
material_library_cache: HashMap::new(), material_library_cache: HashMap::new(),
}) })
.configure_sets( .configure_sets(
Update, Update,
(GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn) (GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)
.chain() .chain()
.after(GltfComponentsSet::Injection), .after(GltfComponentsSet::Injection),
)
.add_systems(
Update,
(
spawn_from_blueprints,
compute_scene_aabbs.run_if(aabbs_enabled),
apply_deferred.run_if(aabbs_enabled),
apply_deferred,
materials_inject.run_if(materials_library_enabled),
) )
.add_systems( .chain()
Update, .in_set(GltfBlueprintsSet::Spawn),
( )
spawn_from_blueprints, .add_systems(
compute_scene_aabbs.run_if(aabbs_enabled), Update,
apply_deferred.run_if(aabbs_enabled), (spawned_blueprint_post_process, apply_deferred)
apply_deferred, .chain()
materials_inject.run_if(materials_library_enabled), .in_set(GltfBlueprintsSet::AfterSpawn),
) );
.chain()
.in_set(GltfBlueprintsSet::Spawn),
)
.add_systems(
Update,
(spawned_blueprint_post_process, apply_deferred)
.chain()
.in_set(GltfBlueprintsSet::AfterSpawn),
);
} }
} }

View File

@ -19,17 +19,17 @@ pub struct BlueprintName(pub String);
pub struct SpawnHere; pub struct SpawnHere;
#[derive(Component)] #[derive(Component)]
/// FlagComponent for dynamically spawned scenes /// flag component for dynamically spawned scenes
pub struct Spawned; pub struct Spawned;
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
/// flag component marking any spwaned child of blueprints ..unless the original entity was marked with the 'NoInBlueprint' marker component /// flag component marking any spwaned child of blueprints ..unless the original entity was marked with the `NoInBlueprint` marker component
pub struct InBlueprint; pub struct InBlueprint;
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
/// flag component preventing any spwaned child of blueprints to be marked with the InBlueprint component /// flag component preventing any spawned child of blueprints to be marked with the `InBlueprint` component
pub struct NoInBlueprint; pub struct NoInBlueprint;
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
@ -112,14 +112,15 @@ pub(crate) fn spawn_from_blueprints(
let main_scene_name = gltf let main_scene_name = gltf
.named_scenes .named_scenes
.keys() .keys()
.nth(0) .next()
.expect("there should be at least one named scene in the gltf file to spawn"); .expect("there should be at least one named scene in the gltf file to spawn");
let scene = &gltf.named_scenes[main_scene_name]; let scene = &gltf.named_scenes[main_scene_name];
// transforms are optional, but still deal with them correctly // transforms are optional, but still deal with them correctly
let mut transforms: Transform = Transform::default(); let mut transforms: Transform = Transform::default();
if transform.is_some() { if transform.is_some() {
transforms = transform.unwrap().clone(); transforms = *transform.unwrap();
} }
commands.entity(entity).insert(( commands.entity(entity).insert((

View File

@ -12,7 +12,7 @@ use crate::{CopyComponents, InBlueprint, NoInBlueprint, OriginalChildren};
/// - it removes one level of useless nesting /// - it removes one level of useless nesting
/// - it copies the blueprint's root components to the entity it was spawned on (original entity) /// - it copies the blueprint's root components to the entity it was spawned on (original entity)
/// - it copies the children of the blueprint scene into the original entity /// - it copies the children of the blueprint scene into the original entity
/// - it add AnimationLink components so that animations can be controlled from the original entity /// - it add `AnimationLink` components so that animations can be controlled from the original entity
/// - it cleans up/ removes a few , by then uneeded components /// - it cleans up/ removes a few , by then uneeded components
pub(crate) fn spawned_blueprint_post_process( pub(crate) fn spawned_blueprint_post_process(
unprocessed_entities: Query< unprocessed_entities: Query<

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bevy_gltf_components" name = "bevy_gltf_components"
version = "0.3.0" version = "0.3.1"
authors = ["Mark 'kaosat-dev' Moissette"] authors = ["Mark 'kaosat-dev' Moissette"]
description = "Allows you to define [Bevy](https://bevyengine.org/) components direclty inside gltf files and instanciate the components on the Bevy side." description = "Allows you to define [Bevy](https://bevyengine.org/) components direclty inside gltf files and instanciate the components on the Bevy side."
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow" homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"

View File

@ -26,11 +26,11 @@ struct TuppleTestColor(Color);
pub struct VecOfColors(Vec<Color>); pub struct VecOfColors(Vec<Color>);
pub fn ronstring_to_reflect_component( pub fn ronstring_to_reflect_component(
ron_string: &String, ron_string: &str,
type_registry: &TypeRegistry, type_registry: &TypeRegistry,
simplified_types: bool, simplified_types: bool,
) -> Vec<Box<dyn Reflect>> { ) -> Vec<Box<dyn Reflect>> {
let lookup: HashMap<String, Value> = ron::from_str(ron_string.as_str()).unwrap(); let lookup: HashMap<String, Value> = ron::from_str(ron_string).unwrap();
let mut components: Vec<Box<dyn Reflect>> = Vec::new(); let mut components: Vec<Box<dyn Reflect>> = Vec::new();
for (key, value) in lookup.into_iter() { for (key, value) in lookup.into_iter() {
let type_string = key.replace("component: ", "").trim().to_string(); let type_string = key.replace("component: ", "").trim().to_string();
@ -49,70 +49,65 @@ pub fn ronstring_to_reflect_component(
{ {
debug!("TYPE INFO {:?}", type_registration.type_info()); debug!("TYPE INFO {:?}", type_registration.type_info());
if simplified_types { if simplified_types {
match type_registration.type_info() { if let TypeInfo::TupleStruct(info) = type_registration.type_info() {
TypeInfo::TupleStruct(info) => { // we handle tupple strucs with only one field differently, as Blender's custom properties with custom ui (float, int, bool, etc) always give us a tupple struct
// we handle tupple strucs with only one field differently, as Blender's custom properties with custom ui (float, int, bool, etc) always give us a tupple struct if info.field_len() == 1 {
if info.field_len() == 1 { let field = info
let field = info .field_at(0)
.field_at(0) .expect("we should always have at least one field here");
.expect("we should always have at least one field here"); let field_name = field.type_path();
let field_name = field.type_path(); let mut formated = parsed_value.clone();
let mut formated = parsed_value.clone(); match field_name {
match field_name { "f32" => {
"f32" => { formated = parsed_value.parse::<f32>().unwrap().to_string();
formated = parsed_value.parse::<f32>().unwrap().to_string(); }
} "f64" => {
"f64" => { formated = parsed_value.parse::<f64>().unwrap().to_string();
formated = parsed_value.parse::<f64>().unwrap().to_string(); }
} "u8" => {
"u8" => { formated = parsed_value.parse::<u8>().unwrap().to_string();
formated = parsed_value.parse::<u8>().unwrap().to_string(); }
} "u16" => {
"u16" => { formated = parsed_value.parse::<u16>().unwrap().to_string();
formated = parsed_value.parse::<u16>().unwrap().to_string(); }
} "u32" => {
"u32" => { formated = parsed_value.parse::<u32>().unwrap().to_string();
formated = parsed_value.parse::<u32>().unwrap().to_string(); }
} "u64" => {
"u64" => { formated = parsed_value.parse::<u64>().unwrap().to_string();
formated = parsed_value.parse::<u64>().unwrap().to_string(); }
} "u128" => {
"u128" => { formated = parsed_value.parse::<u128>().unwrap().to_string();
formated = parsed_value.parse::<u128>().unwrap().to_string(); }
} "glam::Vec2" => {
"glam::Vec2" => { let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap(); formated = format!("(x:{},y:{})", parsed[0], parsed[1]);
formated = format!("(x:{},y:{})", parsed[0], parsed[1]); }
} "glam::Vec3" => {
"glam::Vec3" => { let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap(); formated =
format!("(x:{},y:{},z:{})", parsed[0], parsed[1], parsed[2]);
}
"bevy_render::color::Color" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
if parsed.len() == 3 {
formated = format!( formated = format!(
"(x:{},y:{},z:{})", "Rgba(red:{},green:{},blue:{}, alpha: 1.0)",
parsed[0], parsed[1], parsed[2] parsed[0], parsed[1], parsed[2]
); );
} }
"bevy_render::color::Color" => { if parsed.len() == 4 {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap(); formated = format!(
if parsed.len() == 3 { "Rgba(red:{},green:{},blue:{}, alpha:{})",
formated = format!( parsed[0], parsed[1], parsed[2], parsed[3]
"Rgba(red:{},green:{},blue:{}, alpha: 1.0)", );
parsed[0], parsed[1], parsed[2]
);
}
if parsed.len() == 4 {
formated = format!(
"Rgba(red:{},green:{},blue:{}, alpha:{})",
parsed[0], parsed[1], parsed[2], parsed[3]
);
}
} }
_ => {}
} }
_ => {}
parsed_value = format!("({formated})");
} }
parsed_value = format!("({formated})");
} }
_ => {}
} }
} }
@ -137,13 +132,14 @@ pub fn ronstring_to_reflect_component(
debug!("component data ron string {}", ron_string); debug!("component data ron string {}", ron_string);
let mut deserializer = ron::Deserializer::from_str(ron_string.as_str()).unwrap(); let mut deserializer = ron::Deserializer::from_str(ron_string.as_str()).unwrap();
let reflect_deserializer = UntypedReflectDeserializer::new(type_registry); let reflect_deserializer = UntypedReflectDeserializer::new(type_registry);
let component = reflect_deserializer.deserialize(&mut deserializer).expect( let component = reflect_deserializer
format!( .deserialize(&mut deserializer)
"failed to deserialize component {} with value: {:?}", .unwrap_or_else(|_| {
key, value panic!(
) "failed to deserialize component {} with value: {:?}",
.as_str(), key, value
); )
});
debug!("component {:?}", component); debug!("component {:?}", component);
debug!("real type {:?}", component.get_represented_type_info()); debug!("real type {:?}", component.get_represented_type_info());
@ -157,12 +153,12 @@ pub fn ronstring_to_reflect_component(
components components
} }
/// main function: injects components into each entity in gltf files that have gltf_extras, using reflection /// main function: injects components into each entity in gltf files that have `gltf_extras`, using reflection
pub fn gltf_extras_to_components( pub fn gltf_extras_to_components(
gltf: &mut Gltf, gltf: &mut Gltf,
scenes: &mut ResMut<Assets<Scene>>, scenes: &mut ResMut<Assets<Scene>>,
type_registry: impl Deref<Target = TypeRegistry>, type_registry: impl Deref<Target = TypeRegistry>,
legacy_mode: bool legacy_mode: bool,
) { ) {
let mut added_components = 0; let mut added_components = 0;
let simplified_types = legacy_mode; let simplified_types = legacy_mode;

View File

@ -7,7 +7,10 @@ pub use gltf_to_components::*;
pub mod process_gltfs; pub mod process_gltfs;
pub use process_gltfs::*; pub use process_gltfs::*;
use bevy::{ecs::system::Resource, prelude::{App, IntoSystemConfigs, Plugin, SystemSet, Update}}; use bevy::{
ecs::system::Resource,
prelude::{App, IntoSystemConfigs, Plugin, SystemSet, Update},
};
/// A Bevy plugin for extracting components from gltf files and automatically adding them to the relevant entities /// A Bevy plugin for extracting components from gltf files and automatically adding them to the relevant entities
/// It will automatically run every time you load a gltf file /// It will automatically run every time you load a gltf file
@ -54,14 +57,12 @@ pub struct GltfComponentsConfig {
} }
pub struct ComponentsFromGltfPlugin { pub struct ComponentsFromGltfPlugin {
pub legacy_mode: bool pub legacy_mode: bool,
} }
impl Default for ComponentsFromGltfPlugin { impl Default for ComponentsFromGltfPlugin {
fn default() -> Self { fn default() -> Self {
Self { Self { legacy_mode: true }
legacy_mode: true
}
} }
} }
@ -69,7 +70,7 @@ impl Plugin for ComponentsFromGltfPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.insert_resource(GltfLoadingTracker::new()) app.insert_resource(GltfLoadingTracker::new())
.insert_resource(GltfComponentsConfig { .insert_resource(GltfComponentsConfig {
legacy_mode: self.legacy_mode legacy_mode: self.legacy_mode,
}) })
.add_systems(Update, (track_new_gltf, process_loaded_scenes)) .add_systems(Update, (track_new_gltf, process_loaded_scenes))
.add_systems( .add_systems(

View File

@ -1,8 +1,8 @@
use std::path::Path;
use bevy::asset::AssetPath; use bevy::asset::AssetPath;
use bevy::gltf::Gltf; use bevy::gltf::Gltf;
use bevy::utils::HashSet; use bevy::utils::HashSet;
use bevy::{asset::LoadState, prelude::*}; use bevy::{asset::LoadState, prelude::*};
use std::path::Path;
use crate::{gltf_extras_to_components, GltfComponentsConfig}; use crate::{gltf_extras_to_components, GltfComponentsConfig};
@ -12,6 +12,11 @@ pub struct GltfLoadingTracker {
pub loading_gltfs: HashSet<Handle<Gltf>>, pub loading_gltfs: HashSet<Handle<Gltf>>,
pub processed_gltfs: HashSet<String>, pub processed_gltfs: HashSet<String>,
} }
impl Default for GltfLoadingTracker {
fn default() -> Self {
Self::new()
}
}
impl GltfLoadingTracker { impl GltfLoadingTracker {
pub fn new() -> GltfLoadingTracker { pub fn new() -> GltfLoadingTracker {
@ -32,17 +37,18 @@ pub fn track_new_gltf(
) { ) {
for event in events.read() { for event in events.read() {
if let AssetEvent::Added { id } = event { if let AssetEvent::Added { id } = event {
let handle = asset_server.get_id_handle(*id); let handle = asset_server.get_id_handle(*id);
if let Some(handle) = handle { if let Some(handle) = handle {
tracker.add_gltf(handle.clone()); tracker.add_gltf(handle.clone());
debug!("gltf created {:?}", handle); debug!("gltf created {:?}", handle);
} else { } else {
let asset_path = asset_server.get_path(*id).unwrap_or(AssetPath::from_path(Path::new("n/a".into()))); // will unfortunatly not work, will do a PR/ discussion at the Bevy level, leaving for reference, would be very practical let asset_path = asset_server
.get_path(*id)
.unwrap_or(AssetPath::from_path(Path::new("n/a"))); // will unfortunatly not work, will do a PR/ discussion at the Bevy level, leaving for reference, would be very practical
warn!( warn!(
"a gltf file ({:?}) has no handle available, cannot inject components", "a gltf file ({:?}) has no handle available, cannot inject components",
asset_path asset_path
) );
} }
} }
} }
@ -76,7 +82,12 @@ pub fn process_loaded_scenes(
for gltf_handle in &loaded_gltfs { for gltf_handle in &loaded_gltfs {
if let Some(gltf) = gltfs.get_mut(gltf_handle) { if let Some(gltf) = gltfs.get_mut(gltf_handle) {
gltf_extras_to_components(gltf, &mut scenes, &*type_registry, gltf_components_config.legacy_mode); gltf_extras_to_components(
gltf,
&mut scenes,
&*type_registry,
gltf_components_config.legacy_mode,
);
if let Some(path) = gltf_handle.path() { if let Some(path) = gltf_handle.path() {
tracker.processed_gltfs.insert(path.to_string()); tracker.processed_gltfs.insert(path.to_string());

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bevy_gltf_save_load" name = "bevy_gltf_save_load"
version = "0.2.0" version = "0.2.1"
authors = ["Mark 'kaosat-dev' Moissette"] authors = ["Mark 'kaosat-dev' Moissette"]
description = "Save & load your bevy games" description = "Save & load your bevy games"
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow" homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"

View File

@ -24,18 +24,18 @@ pub(crate) struct LoadFirstStageDone;
#[reflect(Component)] #[reflect(Component)]
pub(crate) struct CleanupScene; pub(crate) struct CleanupScene;
/// helper system that "converts" loadRequest events to LoadRequested resources /// helper system that "converts" loadRequest events to `LoadRequested` resources
pub(crate) fn mark_load_requested( pub(crate) fn mark_load_requested(
mut load_requests: EventReader<LoadRequest>, mut load_requests: EventReader<LoadRequest>,
mut commands: Commands, mut commands: Commands,
) { ) {
let mut save_path: String = "".into(); let mut save_path: String = "".into();
for load_request in load_requests.read() { for load_request in load_requests.read() {
if load_request.path != "" { if !load_request.path.is_empty() {
save_path = load_request.path.clone(); save_path = load_request.path.clone();
} }
} }
if save_path != "" { if !save_path.is_empty() {
commands.insert_resource(LoadRequested { path: save_path }); commands.insert_resource(LoadRequested { path: save_path });
} }
} }
@ -111,7 +111,7 @@ pub(crate) fn load_static(
)) ))
.id(); .id();
if info.library_path != "" { if !info.library_path.is_empty() {
commands commands
.entity(static_data) .entity(static_data)
.insert(Library(info.library_path.clone().into())); .insert(Library(info.library_path.clone().into()));

View File

@ -17,7 +17,7 @@ pub struct SaveRequest {
pub struct SavingFinished; pub struct SavingFinished;
pub fn should_save(save_requests: EventReader<SaveRequest>) -> bool { pub fn should_save(save_requests: EventReader<SaveRequest>) -> bool {
return save_requests.len() > 0; !save_requests.is_empty()
} }
#[derive(Resource, Clone, Debug, Default, Reflect)] #[derive(Resource, Clone, Debug, Default, Reflect)]
@ -65,9 +65,7 @@ pub(crate) fn prepare_save_game(
} }
} }
for (_, blueprint_name, library) in static_entities.iter() { for (_, blueprint_name, library) in static_entities.iter() {
let library_path: String = library let library_path: String = library.map_or_else(|| "", |l| l.0.to_str().unwrap()).into();
.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,

View File

@ -1,7 +1,7 @@
[package] [package]
name = "bevy_registry_export" name = "bevy_registry_export"
version = "0.1.0" version = "0.1.1"
authors = ["Mark 'kaosat-dev' Moissette"] authors = ["Mark 'kaosat-dev' Moissette", "Pascal 'Killercup' Hertleif"]
description = "Allows you to define [Bevy](https://bevyengine.org/) components direclty inside gltf files and instanciate the components on the Bevy side." description = "Allows you to define [Bevy](https://bevyengine.org/) components direclty inside gltf files and instanciate the components on the Bevy side."
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow" homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
repository = "https://github.com/kaosat-dev/Blender_bevy_components_workflow" repository = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"

View File

@ -15,8 +15,7 @@ pub fn export_types(world: &mut World) {
.get_resource::<ExportComponentsConfig>() .get_resource::<ExportComponentsConfig>()
.expect("ExportComponentsConfig should exist at this stage"); .expect("ExportComponentsConfig should exist at this stage");
let writer = let writer = File::create(&config.save_path).expect("should have created schema file");
File::create(config.save_path.to_path_buf()).expect("should have created schema file");
let types = world.resource_mut::<AppTypeRegistry>(); let types = world.resource_mut::<AppTypeRegistry>();
let types = types.read(); let types = types.read();

View File

@ -7,6 +7,8 @@ license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
bevy="0.12" bevy="0.12"
bevy_gltf_blueprints = { path = "../../../crates/bevy_gltf_blueprints" } bevy_gltf_blueprints = { path = "../../../crates/bevy_gltf_blueprints" }
bevy_gltf_worlflow_examples_common = { path = "../../common" }
bevy_rapier3d = { version = "0.23.0", features = [ "serde-serialize", "debug-render-3d", "enhanced-determinism"] } bevy_rapier3d = { version = "0.23.0", features = [ "serde-serialize", "debug-render-3d", "enhanced-determinism"] }
bevy_asset_loader = { version = "0.18", features = ["standard_dynamic_assets" ]} bevy_asset_loader = { version = "0.18", features = ["standard_dynamic_assets" ]}
bevy_editor_pls = { version = "0.6" } bevy_editor_pls = { version = "0.6" }

View File

@ -1,13 +0,0 @@
use bevy::gltf::Gltf;
use bevy::prelude::*;
use bevy::utils::HashMap;
use bevy_asset_loader::prelude::*;
#[derive(AssetCollection, Resource)]
pub struct GameAssets {
#[asset(key = "world")]
pub world: Handle<Gltf>,
#[asset(key = "models", collection(typed, mapped))]
pub models: HashMap<String, Handle<Gltf>>,
}

View File

@ -1,24 +0,0 @@
use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings};
use bevy::core_pipeline::tonemapping::{DebandDither, Tonemapping};
use bevy::prelude::*;
use super::CameraTrackingOffset;
pub fn camera_replace_proxies(
mut commands: Commands,
mut added_cameras: Query<(Entity, &mut Camera), (Added<Camera>, With<CameraTrackingOffset>)>,
) {
for (entity, mut camera) in added_cameras.iter_mut() {
info!("detected added camera, updating proxy");
camera.hdr = true;
commands
.entity(entity)
.insert(DebandDither::Enabled)
.insert(Tonemapping::BlenderFilmic)
.insert(BloomSettings {
intensity: 0.01,
composite_mode: BloomCompositeMode::Additive,
..default()
});
}
}

View File

@ -1,58 +0,0 @@
use bevy::prelude::*;
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
/// Component for cameras, with an offset from the Trackable target
///
pub struct CameraTracking {
pub offset: Vec3,
}
impl Default for CameraTracking {
fn default() -> Self {
CameraTracking {
offset: Vec3::new(0.0, 6.0, 8.0),
}
}
}
#[derive(Component, Reflect, Debug, Deref, DerefMut)]
#[reflect(Component)]
/// Component for cameras, with an offset from the Trackable target
pub struct CameraTrackingOffset(Vec3);
impl Default for CameraTrackingOffset {
fn default() -> Self {
CameraTrackingOffset(Vec3::new(0.0, 6.0, 8.0))
}
}
impl CameraTrackingOffset {
fn new(input: Vec3) -> Self {
CameraTrackingOffset(input)
}
}
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Add this component to an entity if you want it to be tracked by a Camera
pub struct CameraTrackable;
pub fn camera_track(
mut tracking_cameras: Query<
(&mut Transform, &CameraTrackingOffset),
(
With<Camera>,
With<CameraTrackingOffset>,
Without<CameraTrackable>,
),
>,
camera_tracked: Query<&Transform, With<CameraTrackable>>,
) {
for (mut camera_transform, tracking_offset) in tracking_cameras.iter_mut() {
for tracked_transform in camera_tracked.iter() {
let target_position = tracked_transform.translation + tracking_offset.0;
let eased_position = camera_transform.translation.lerp(target_position, 0.1);
camera_transform.translation = eased_position; // + tracking.offset;// tracked_transform.translation + tracking.offset;
*camera_transform = camera_transform.looking_at(tracked_transform.translation, Vec3::Y);
}
}
}

View File

@ -1,24 +0,0 @@
pub mod camera_tracking;
pub use camera_tracking::*;
pub mod camera_replace_proxies;
pub use camera_replace_proxies::*;
use bevy::prelude::*;
use bevy_gltf_blueprints::GltfBlueprintsSet;
pub struct CameraPlugin;
impl Plugin for CameraPlugin {
fn build(&self, app: &mut App) {
app.register_type::<CameraTrackable>()
.register_type::<CameraTracking>()
.register_type::<CameraTrackingOffset>()
.add_systems(
Update,
(
camera_replace_proxies.after(GltfBlueprintsSet::AfterSpawn),
camera_track,
),
);
}
}

View File

@ -1,25 +0,0 @@
use bevy::prelude::*;
use bevy::pbr::{CascadeShadowConfig, CascadeShadowConfigBuilder};
// fixme might be too specific to might needs, should it be moved out ? also these are all for lights, not models
pub fn lighting_replace_proxies(
mut added_dirights: Query<(Entity, &mut DirectionalLight), Added<DirectionalLight>>,
mut added_spotlights: Query<&mut SpotLight, Added<SpotLight>>,
mut commands: Commands,
) {
for (entity, mut light) in added_dirights.iter_mut() {
light.illuminance *= 5.0;
light.shadows_enabled = true;
let shadow_config: CascadeShadowConfig = CascadeShadowConfigBuilder {
first_cascade_far_bound: 15.0,
maximum_distance: 135.0,
..default()
}
.into();
commands.entity(entity).insert(shadow_config);
}
for mut light in added_spotlights.iter_mut() {
light.shadows_enabled = true;
}
}

View File

@ -1,18 +0,0 @@
mod lighting_replace_proxies;
use lighting_replace_proxies::*;
use bevy::pbr::{DirectionalLightShadowMap, NotShadowCaster};
use bevy::prelude::*;
pub struct LightingPlugin;
impl Plugin for LightingPlugin {
fn build(&self, app: &mut App) {
app
.insert_resource(DirectionalLightShadowMap { size: 4096 })
// FIXME: adding these since they are missing
.register_type::<NotShadowCaster>()
.add_systems(PreUpdate, lighting_replace_proxies) // FIXME: you should actually run this in a specific state most likely
;
}
}

View File

@ -1,29 +1,14 @@
pub mod camera;
pub use camera::*;
pub mod lighting;
pub use lighting::*;
pub mod relationships;
pub use relationships::*;
pub mod physics;
pub use physics::*;
use bevy::prelude::*; use bevy::prelude::*;
use bevy_gltf_blueprints::*; use bevy_gltf_blueprints::*;
pub struct CorePlugin; pub struct CorePlugin;
impl Plugin for CorePlugin { impl Plugin for CorePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(( app.add_plugins((BlueprintsPlugin {
LightingPlugin, library_folder: "models/library".into(),
CameraPlugin, format: GltfFormat::GLB,
PhysicsPlugin, aabbs: true,
BlueprintsPlugin { ..Default::default()
library_folder: "models/library".into(), },));
..Default::default()
},
));
} }
} }

View File

@ -1,12 +0,0 @@
use bevy::prelude::{info, ResMut};
use bevy_rapier3d::prelude::RapierConfiguration;
pub fn pause_physics(mut physics_config: ResMut<RapierConfiguration>) {
info!("pausing physics");
physics_config.physics_pipeline_active = false;
}
pub fn resume_physics(mut physics_config: ResMut<RapierConfiguration>) {
info!("unpausing physics");
physics_config.physics_pipeline_active = true;
}

View File

@ -1,37 +0,0 @@
pub mod physics_replace_proxies;
use bevy_rapier3d::{
prelude::{NoUserData, RapierPhysicsPlugin},
render::RapierDebugRenderPlugin,
};
pub use physics_replace_proxies::*;
pub mod utils;
pub mod controls;
pub use controls::*;
use crate::state::GameState;
use bevy::prelude::*;
// use super::blueprints::GltfBlueprintsSet;
use bevy_gltf_blueprints::GltfBlueprintsSet;
// use crate::Collider;
pub struct PhysicsPlugin;
impl Plugin for PhysicsPlugin {
fn build(&self, app: &mut App) {
app.add_plugins((
RapierPhysicsPlugin::<NoUserData>::default(),
RapierDebugRenderPlugin::default(),
))
.register_type::<AutoAABBCollider>()
.register_type::<physics_replace_proxies::Collider>()
// find a way to make serde's stuff serializable
// .register_type::<bevy_rapier3d::dynamics::CoefficientCombineRule>()
//bevy_rapier3d::dynamics::CoefficientCombineRule
.add_systems(
Update,
physics_replace_proxies.after(GltfBlueprintsSet::AfterSpawn),
)
.add_systems(OnEnter(GameState::InGame), resume_physics)
.add_systems(OnExit(GameState::InGame), pause_physics);
}
}

View File

@ -1,75 +0,0 @@
use bevy::prelude::*;
use bevy::render::mesh::{MeshVertexAttributeId, PrimitiveTopology, VertexAttributeValues};
// TAKEN VERBATIB FROM https://github.com/janhohenheim/foxtrot/blob/6e31fc02652fc9d085a4adde0a73ab007dbbb0dc/src/util/trait_extension.rs
pub trait Vec3Ext {
#[allow(clippy::wrong_self_convention)] // Because [`Vec3`] is [`Copy`]
fn is_approx_zero(self) -> bool;
fn x0z(self) -> Vec3;
}
impl Vec3Ext for Vec3 {
fn is_approx_zero(self) -> bool {
[self.x, self.y, self.z].iter().all(|&x| x.abs() < 1e-5)
}
fn x0z(self) -> Vec3 {
Vec3::new(self.x, 0., self.z)
}
}
pub trait MeshExt {
fn transform(&mut self, transform: Transform);
fn transformed(&self, transform: Transform) -> Mesh;
fn read_coords_mut(&mut self, id: impl Into<MeshVertexAttributeId>) -> &mut Vec<[f32; 3]>;
fn search_in_children<'a>(
children: &'a Children,
meshes: &'a Assets<Mesh>,
mesh_handles: &'a Query<&Handle<Mesh>>,
) -> (Entity, &'a Mesh);
}
impl MeshExt for Mesh {
fn transform(&mut self, transform: Transform) {
for attribute in [Mesh::ATTRIBUTE_POSITION, Mesh::ATTRIBUTE_NORMAL] {
for coords in self.read_coords_mut(attribute.clone()) {
let vec3 = (*coords).into();
let transformed = transform.transform_point(vec3);
*coords = transformed.into();
}
}
}
fn transformed(&self, transform: Transform) -> Mesh {
let mut mesh = self.clone();
mesh.transform(transform);
mesh
}
fn read_coords_mut(&mut self, id: impl Into<MeshVertexAttributeId>) -> &mut Vec<[f32; 3]> {
match self.attribute_mut(id).unwrap() {
VertexAttributeValues::Float32x3(values) => values,
// Guaranteed by Bevy
_ => unreachable!(),
}
}
fn search_in_children<'a>(
children: &'a Children,
meshes: &'a Assets<Mesh>,
mesh_handles: &'a Query<&Handle<Mesh>>,
) -> (Entity, &'a Mesh) {
let entity_handles: Vec<_> = children
.iter()
.filter_map(|entity| mesh_handles.get(*entity).ok().map(|mesh| (*entity, mesh)))
.collect();
assert_eq!(
entity_handles.len(),
1,
"Collider must contain exactly one mesh, but found {}",
entity_handles.len()
);
let (entity, mesh_handle) = entity_handles.first().unwrap();
let mesh = meshes.get(mesh_handle).unwrap();
assert_eq!(mesh.primitive_topology(), PrimitiveTopology::TriangleList);
(*entity, mesh)
}
}

View File

@ -1,18 +1,15 @@
use bevy_gltf_worlflow_examples_common::{assets::GameAssets, GameState, InAppRunning, Player};
use bevy_rapier3d::prelude::Velocity; use bevy_rapier3d::prelude::Velocity;
use rand::Rng; use rand::Rng;
use std::time::Duration; use std::time::Duration;
use bevy::prelude::*; use bevy::prelude::*;
use crate::{
assets::GameAssets,
state::{GameState, InAppRunning},
};
use bevy_gltf_blueprints::{ use bevy_gltf_blueprints::{
AnimationPlayerLink, Animations, BluePrintBundle, BlueprintName, GameWorldTag, AnimationPlayerLink, Animations, BluePrintBundle, BlueprintName, GameWorldTag,
}; };
use super::{Fox, Player, Robot}; use super::{Fox, Robot};
pub fn setup_game( pub fn setup_game(
mut commands: Commands, mut commands: Commands,
@ -103,9 +100,9 @@ pub fn animation_change_on_proximity_foxes(
let mut anim_name = "Walk"; let mut anim_name = "Walk";
if distance < 8.5 { if distance < 8.5 {
anim_name = "Run"; anim_name = "Run";
} else if distance >= 8.5 && distance < 10.0 { } else if (8.5..10.0).contains(&distance) {
anim_name = "Walk"; anim_name = "Walk";
} else if distance >= 10.0 && distance < 15.0 { } else if (10.0..15.0).contains(&distance) {
anim_name = "Survey"; anim_name = "Survey";
} }
// now play the animation based on the chosen animation name // now play the animation based on the chosen animation name
@ -140,9 +137,9 @@ pub fn animation_change_on_proximity_robots(
let mut anim_name = "Idle"; let mut anim_name = "Idle";
if distance < 8.5 { if distance < 8.5 {
anim_name = "Jump"; anim_name = "Jump";
} else if distance >= 8.5 && distance < 10.0 { } else if (8.5..10.0).contains(&distance) {
anim_name = "Scan"; anim_name = "Scan";
} else if distance >= 10.0 && distance < 15.0 { } else if (10.0..15.0).contains(&distance) {
anim_name = "Idle"; anim_name = "Idle";
} }

View File

@ -1,6 +1,5 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_gltf_worlflow_examples_common::{AppState, InMainMenu};
use crate::state::{AppState, GameState, InMainMenu};
pub fn setup_main_menu(mut commands: Commands) { pub fn setup_main_menu(mut commands: Commands) {
commands.spawn((Camera2dBundle::default(), InMainMenu)); commands.spawn((Camera2dBundle::default(), InMainMenu));

View File

@ -4,44 +4,8 @@ pub use in_game::*;
pub mod in_main_menu; pub mod in_main_menu;
pub use in_main_menu::*; pub use in_main_menu::*;
pub mod picking;
pub use picking::*;
use crate::{
insert_dependant_component,
state::{AppState, GameState},
};
use bevy::prelude::*; use bevy::prelude::*;
use bevy_rapier3d::prelude::*; use bevy_gltf_worlflow_examples_common::{AppState, GameState};
// this file is just for demo purposes, contains various types of components, systems etc
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum SoundMaterial {
Metal,
Wood,
Rock,
Cloth,
Squishy,
#[default]
None,
}
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo marker component
pub struct Player;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo component showing auto injection of components
pub struct ShouldBeWithPlayer;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo marker component
pub struct Interactible;
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
@ -53,66 +17,14 @@ pub struct Fox;
/// Demo marker component /// Demo marker component
pub struct Robot; pub struct Robot;
fn player_move_demo(
keycode: Res<Input<KeyCode>>,
mut players: Query<&mut Transform, With<Player>>,
) {
let speed = 0.2;
if let Ok(mut player) = players.get_single_mut() {
if keycode.pressed(KeyCode::Left) {
player.translation.x += speed;
}
if keycode.pressed(KeyCode::Right) {
player.translation.x -= speed;
}
if keycode.pressed(KeyCode::Up) {
player.translation.z += speed;
}
if keycode.pressed(KeyCode::Down) {
player.translation.z -= speed;
}
}
}
// collision tests/debug
pub fn test_collision_events(
mut collision_events: EventReader<CollisionEvent>,
mut contact_force_events: EventReader<ContactForceEvent>,
) {
for collision_event in collision_events.iter() {
println!("collision");
match collision_event {
CollisionEvent::Started(_entity1, _entity2, _) => {
println!("collision started")
}
CollisionEvent::Stopped(_entity1, _entity2, _) => {
println!("collision ended")
}
}
}
for contact_force_event in contact_force_events.iter() {
println!("Received contact force event: {:?}", contact_force_event);
}
}
pub struct GamePlugin; pub struct GamePlugin;
impl Plugin for GamePlugin { impl Plugin for GamePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(PickingPlugin) app.register_type::<Robot>()
.register_type::<Interactible>()
.register_type::<SoundMaterial>()
.register_type::<Player>()
.register_type::<Robot>()
.register_type::<Fox>() .register_type::<Fox>()
// little helper utility, to automatically inject components that are dependant on an other component
// ie, here an Entity with a Player component should also always have a ShouldBeWithPlayer component
// you get a warning if you use this, as I consider this to be stop-gap solution (usually you should have either a bundle, or directly define all needed components)
.add_systems( .add_systems(
Update, Update,
( (
player_move_demo,
spawn_test, spawn_test,
animation_control, animation_control,
animation_change_on_proximity_foxes, animation_change_on_proximity_foxes,

View File

@ -1,37 +0,0 @@
use super::Player;
use bevy::prelude::*;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct Pickable;
// very simple, crude picking (as in picking up objects) implementation
pub fn picking(
players: Query<&GlobalTransform, With<Player>>,
pickables: Query<(Entity, &GlobalTransform), With<Pickable>>,
mut commands: Commands,
) {
for player_transforms in players.iter() {
for (pickable, pickable_transforms) in pickables.iter() {
let distance = player_transforms
.translation()
.distance(pickable_transforms.translation());
if distance < 2.5 {
commands.entity(pickable).despawn_recursive();
}
}
}
}
pub struct PickingPlugin;
impl Plugin for PickingPlugin {
fn build(&self, app: &mut App) {
app.register_type::<Pickable>().add_systems(
Update,
(
picking, //.run_if(in_state(AppState::Running)),
),
);
}
}

View File

@ -1,15 +1,10 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_editor_pls::prelude::*; use bevy_editor_pls::prelude::*;
use bevy_gltf_worlflow_examples_common::CommonPlugin;
mod core; mod core;
use crate::core::*; use crate::core::*;
pub mod assets;
use assets::*;
pub mod state;
use state::*;
mod game; mod game;
use game::*; use game::*;
@ -23,8 +18,7 @@ fn main() {
// editor // editor
EditorPlugin::default(), EditorPlugin::default(),
// our custom plugins // our custom plugins
StatePlugin, CommonPlugin,
AssetsPlugin,
CorePlugin, // reusable plugins CorePlugin, // reusable plugins
GamePlugin, // specific to our game GamePlugin, // specific to our game
ComponentsTestPlugin, // Showcases different type of components /structs ComponentsTestPlugin, // Showcases different type of components /structs

View File

@ -6,39 +6,39 @@ struct UnitTest;
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)] #[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleTestF32(f32); struct TupleTestF32(f32);
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)] #[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleTestU64(u64); struct TupleTestU64(u64);
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)] #[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
#[reflect(Component)] #[reflect(Component)]
pub struct TuppleTestStr(String); pub struct TupleTestStr(String);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleTest2(f32, u64, String); struct TupleTest2(f32, u64, String);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleTestBool(bool); struct TupleTestBool(bool);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleVec2(Vec2); struct TupleVec2(Vec2);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleVec3(Vec3); struct TupleVec3(Vec3);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleVec(Vec<String>); struct TupleVec(Vec<String>);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleTestColor(Color); struct TupleTestColor(Color);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
@ -65,16 +65,16 @@ impl Plugin for ComponentsTestPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.register_type::<BasicTest>() app.register_type::<BasicTest>()
.register_type::<UnitTest>() .register_type::<UnitTest>()
.register_type::<TuppleTestF32>() .register_type::<TupleTestF32>()
.register_type::<TuppleTestU64>() .register_type::<TupleTestU64>()
.register_type::<TuppleTestStr>() .register_type::<TupleTestStr>()
.register_type::<TuppleTestBool>() .register_type::<TupleTestBool>()
.register_type::<TuppleTest2>() .register_type::<TupleTest2>()
.register_type::<TuppleVec2>() .register_type::<TupleVec2>()
.register_type::<TuppleVec3>() .register_type::<TupleVec3>()
.register_type::<EnumTest>() .register_type::<EnumTest>()
.register_type::<TuppleTestColor>() .register_type::<TupleTestColor>()
.register_type::<TuppleVec>() .register_type::<TupleVec>()
.register_type::<Vec<String>>(); .register_type::<Vec<String>>();
} }
} }

View File

@ -7,6 +7,8 @@ license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
bevy="0.12" bevy="0.12"
bevy_gltf_blueprints = { path = "../../../crates/bevy_gltf_blueprints" } bevy_gltf_blueprints = { path = "../../../crates/bevy_gltf_blueprints" }
bevy_gltf_worlflow_examples_common = { path = "../../common" }
bevy_rapier3d = { version = "0.23.0", features = [ "serde-serialize", "debug-render-3d", "enhanced-determinism"] } bevy_rapier3d = { version = "0.23.0", features = [ "serde-serialize", "debug-render-3d", "enhanced-determinism"] }
bevy_asset_loader = { version = "0.18", features = ["standard_dynamic_assets" ]} bevy_asset_loader = { version = "0.18", features = ["standard_dynamic_assets" ]}
bevy_editor_pls = { version = "0.6" } bevy_editor_pls = { version = "0.6" }

View File

@ -15,9 +15,39 @@ It also allows you to setup 'blueprints' in Blender by using collections (the re
``` ```
cargo run --features bevy/dynamic_linking cargo run --features bevy/dynamic_linking
``` ```
### Additional notes ## Wasm instructions
### Setup
as per the bevy documentation:
```shell
rustup target add wasm32-unknown-unknown
cargo install wasm-bindgen-cli
```
### Building this example
navigate to the current folder , and then
```shell
cargo build --release --target wasm32-unknown-unknown --target-dir ./target
wasm-bindgen --out-name wasm_example \
--out-dir ./target/wasm \
--target web target/wasm32-unknown-unknown/release/bevy_gltf_blueprints_basic_wasm_example.wasm
```
### Running this example
run a web server in the current folder, and navigate to the page, you should see the example in your browser
## Additional notes
* You usually define either the Components directly or use ```Proxy components``` that get replaced in Bevy systems with the actual Components that you want (usually when for some reason, ie external crates with unregistered components etc) you cannot use the components directly. * You usually define either the Components directly or use ```Proxy components``` that get replaced in Bevy systems with the actual Components that you want (usually when for some reason, ie external crates with unregistered components etc) you cannot use the components directly.
* this example contains code for future features, not finished yet ! please disregard anything related to saving & loading * this example contains code for future features, not finished yet ! please disregard anything related to saving & loading

View File

@ -1,53 +0,0 @@
- [x] seperate "spawn velocity" into a seperate (optional) component
- [x] try dynamically spawned entities with save & load
- [ ] fix issues with multiple entites having the same name ??
- [ ] fix issues with system ordering
- [x] add sets for proxy-replacements
- [x] annoying camera movement : camera should be saveable as well
=> we need to be able to only save the camera position (and name)
- [ ] only spawn "new level" stuff when transitioning from menu to game ?
- [ ] put more thoughts into this
- [ ] rework how save-loading is triggered
- [ ] no hardcoded keypresses
- [ ] ability to choose what save to load ?
- [ ] take a look at bevy_moonshine_save
- [ ] move to system pipelines
- [x] split Blueprints into a seperate crate: perhaps bevy_gltf_blueprints
- [x] how to deal with states that are not defined as part of the plugin/crate ?
- [x] same issue for the assets
- [x] support multiple main scenes in the blender plugin ?
- [ ] study possibilities of expanding the bevy & blender tooling side to define UIS
- likely using the blender data only as a placeholder/ directly replace in Python
- system ordering ?
load level => inject components => spawn blueprint entities/rehydrate => (loading) => replace proxies
OR
load level => inject components => (loading) => spawn blueprint entities/rehydrate => replace proxies
- perhaps it does make more sense to save ALL entities and not just the dynamic ones? mostly as we have the blueprints anyway, which should cut down on needed data ?
- different approaches for dealing with saving/loading
in particular the problem of entites that are defined as part of the level but can be despawned (items that get picked up etc)
Bevy side
* var 1 : spawn all entities completely, despawn those saveables that are not actually present in the save data but that have been spawned
* problems: needs correct ordering of systems otherwise the diffing above will not work
* pros: minimal save files, only bevy boilerplate
* cons: requires spawning potentially huge gltf files just to "peek" inside of them to know if they are saveable
* var 2 : save both saveables & unsaveables but only save the absolute minimal data for unsaveables
* problems: how to combine different filters into a single save file ?
* pros: easier diffing, more homogeneous handling
* cons: a lot bigger save file with mostly useless data
Blender side
* var 3 => CHOSEN OPTION : mark INSTANCES in advance as static/dynamic (ie saveable or not), thus this data would be present in the world/level and not inside the gltf data
* problems: would require adding custom properties to each instance in Blender (COULD be automated by 'peeking' inside the collection)
* pros: simpler, and this might be more "editor-like" where you would mark each item as static or not
* cons: potentially a lot of manual work / error prone

View File

@ -1,485 +0,0 @@
(
resources: {},
entities: {
20: (
components: {
"bevy_render::camera::projection::Projection": Perspective((
fov: 0.3995965,
aspect_ratio: 1.7777778,
near: 0.1,
far: 100.0,
)),
"bevy_render::primitives::Frustum": (),
"bevy_transform::components::transform::Transform": (
translation: (
x: 34.821884,
y: 49.024857,
z: -36.79615,
),
rotation: (-0.1694689, 0.82838506, 0.40884802, 0.3433684),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core_pipeline::tonemapping::Tonemapping": BlenderFilmic,
"bevy_core_pipeline::tonemapping::DebandDither": Enabled,
"bevy_render::view::ColorGrading": (
exposure: 0.0,
gamma: 1.0,
pre_saturation: 1.0,
post_saturation: 1.0,
),
"bevy_core::name::Name": (
hash: 17702508670109176045,
name: "Camera",
),
"advanced::core::camera::camera_tracking::CameraTrackingOffset": ((
x: 26.0,
y: 48.0,
z: -26.0,
)),
"bevy_pbr::light::ClusterConfig": FixedZ(
total: 4096,
z_slices: 24,
z_config: (
first_slice_depth: 5.0,
far_z_mode: MaxLightRange,
),
dynamic_resizing: true,
),
"bevy_core_pipeline::bloom::settings::BloomSettings": (
intensity: 0.01,
low_frequency_boost: 0.7,
low_frequency_boost_curvature: 0.95,
high_pass_frequency: 1.0,
prefilter_settings: (
threshold: 0.0,
threshold_softness: 0.0,
),
composite_mode: Additive,
),
},
),
34: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 4.697565,
y: 1.5983224,
z: 8.962274,
),
rotation: (0.000000000000000031724054, -0.00000000000000000000647681, -0.000013119204, 1.0),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 9837288155836662016,
name: "Health_Pickup.001",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
},
),
54: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 8.799996,
y: 1.02484,
z: -10.799994,
),
rotation: (0.0, 0.0, 0.0, 1.0),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 17978181434632022651,
name: "Player",
),
"advanced::core::camera::camera_tracking::CameraTrackable": (),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Player"),
"advanced::game::Player": (),
"advanced::game::SoundMaterial": Wood,
},
),
60: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 3.6351967,
y: 1.7298106,
z: -7.313273,
),
rotation: (0.0, 0.0, 0.0, 1.0),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 7225506896223411979,
name: "MagicTeapot.001",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("MagicTeapot"),
},
),
64: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: -4.6068983,
y: 1.5983224,
z: -10.579347,
),
rotation: (0.000000000000000031724054, 0.00000000000000000000647681, 0.000013119204, 1.0),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 3089896164553476909,
name: "Health_Pickup.002",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
},
),
72: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: -11.560788,
y: 0.0,
z: 7.6554174,
),
rotation: (0.0, 0.0, 0.0, 1.0),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 16961132108296874979,
name: "Container.001",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Container"),
"advanced::game::picking::Pickable": (),
},
),
80: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: -21.397858,
y: 0.3833189,
z: -0.32418346,
),
rotation: (0.0, 0.0, 0.0, 1.0),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 5104740624378885265,
name: "Container.002",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Container"),
"advanced::game::picking::Pickable": (),
},
),
82: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 2.9156065,
y: 1.4984571,
z: 2.1909573,
),
rotation: (0.058853183, 0.0726243, 0.2048649, 0.97431636),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 107557640935939866,
name: "test5159735758431545549",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: -1.2580805,
y: -0.39687577,
z: 0.4816798,
),
angvel: (
x: 0.2979751,
y: 0.07926611,
z: 0.8434645,
),
),
},
),
86: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 0.26087752,
y: 1.5525806,
z: 1.5980839,
),
rotation: (0.059497803, -0.0000018232388, 0.13145457, 0.9895351),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 3398656236303073559,
name: "test7470642598731063943",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: -0.9268077,
y: -0.19806683,
z: 0.41948256,
),
angvel: (
x: 0.26946256,
y: -0.000006710977,
z: 0.5953494,
),
),
},
),
90: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 2.6515265,
y: 1.5944021,
z: -4.391837,
),
rotation: (-0.030030435, -0.0000006527225, 0.029748484, 0.9991062),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 12541900054595385134,
name: "test3938024405863834719",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: -0.28430828,
y: -0.022357654,
z: -0.2870027,
),
angvel: (
x: -0.17986917,
y: -0.0000035613396,
z: 0.17818078,
),
),
},
),
94: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: -4.2356462,
y: 1.596993,
z: 0.7254991,
),
rotation: (-0.0221751, -0.0000000001891749, 0.011065631, 0.99969286),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 6757906322211730861,
name: "test11007490954016878479",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: -0.21747473,
y: -0.014912919,
z: -0.43581253,
),
angvel: (
x: -0.2727097,
y: -0.0000000034594905,
z: 0.13608481,
),
),
},
),
98: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 3.1525247,
y: 1.5518407,
z: -2.9611976,
),
rotation: (-0.09219627, 0.1602262, -0.11205085, 0.9763565),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 12588565107899185946,
name: "test5980867849331267699",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: 0.8323179,
y: -0.20597076,
z: -0.68975484,
),
angvel: (
x: -0.37971017,
y: 0.49603412,
z: -0.6079359,
),
),
},
),
4294967310: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 4.826278,
y: 1.2710563,
z: -3.1997645,
),
rotation: (-0.303028, 0.00000087800436, -0.23889118, 0.9225535),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 15533546218717453536,
name: "test12380979123759326444",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: 1.2146912,
y: -1.1640646,
z: -1.5408095,
),
angvel: (
x: -1.1932359,
y: 0.000002945365,
z: -0.94068503,
),
),
},
),
4294967314: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 3.9906094,
y: 1.4824095,
z: 2.4394412,
),
rotation: (0.06015042, 0.085218765, 0.2215642, 0.9695509),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 2466794778849297109,
name: "test12475628281920299197",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: -1.0818624,
y: -0.37798148,
z: 0.45334253,
),
angvel: (
x: 0.25961447,
y: 0.14854014,
z: 0.7426717,
),
),
},
),
4294967321: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 2.2306876,
y: 0.989814,
z: -1.3596333,
),
rotation: (0.30614096, 0.002587511, -0.42789298, 0.8503991),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 1545925632270385398,
name: "test15780367212768138828",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: 1.3027526,
y: -1.8947054,
z: 1.6179247,
),
angvel: (
x: 1.4565696,
y: -0.16299045,
z: -1.3631926,
),
),
},
),
},
)

View File

@ -1,5 +0,0 @@
use bevy::prelude::*;
use bevy_asset_loader::prelude::*;
#[derive(AssetCollection, Resource)]
pub struct CoreAssets {}

View File

@ -1,13 +0,0 @@
use bevy::gltf::Gltf;
use bevy::prelude::*;
use bevy::utils::HashMap;
use bevy_asset_loader::prelude::*;
#[derive(AssetCollection, Resource)]
pub struct GameAssets {
#[asset(key = "world")]
pub world: Handle<Gltf>,
#[asset(key = "models", collection(typed, mapped))]
pub models: HashMap<String, Handle<Gltf>>,
}

View File

@ -1,35 +0,0 @@
pub mod assets_core;
pub use assets_core::*;
pub mod assets_game;
pub use assets_game::*;
use bevy::prelude::*;
use bevy_asset_loader::prelude::*;
use crate::state::AppState;
pub struct AssetsPlugin;
impl Plugin for AssetsPlugin {
fn build(&self, app: &mut App) {
app
// load core assets (ie assets needed in the main menu, and everywhere else before loading more assets in game)
.add_loading_state(
LoadingState::new(AppState::CoreLoading).continue_to_state(AppState::MenuRunning),
)
.add_dynamic_collection_to_loading_state::<_, StandardDynamicAssetCollection>(
AppState::CoreLoading,
"assets_core.assets.ron",
)
.add_collection_to_loading_state::<_, CoreAssets>(AppState::CoreLoading)
// load game assets
.add_loading_state(
LoadingState::new(AppState::AppLoading).continue_to_state(AppState::AppRunning),
)
.add_dynamic_collection_to_loading_state::<_, StandardDynamicAssetCollection>(
AppState::AppLoading,
"assets_game.assets.ron",
)
.add_collection_to_loading_state::<_, GameAssets>(AppState::AppLoading);
}
}

View File

@ -1,24 +0,0 @@
use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings};
use bevy::core_pipeline::tonemapping::{DebandDither, Tonemapping};
use bevy::prelude::*;
use super::CameraTrackingOffset;
pub fn camera_replace_proxies(
mut commands: Commands,
mut added_cameras: Query<(Entity, &mut Camera), (Added<Camera>, With<CameraTrackingOffset>)>,
) {
for (entity, mut camera) in added_cameras.iter_mut() {
info!("detected added camera, updating proxy");
camera.hdr = true;
commands
.entity(entity)
.insert(DebandDither::Enabled)
.insert(Tonemapping::BlenderFilmic)
.insert(BloomSettings {
intensity: 0.01,
composite_mode: BloomCompositeMode::Additive,
..default()
});
}
}

View File

@ -1,58 +0,0 @@
use bevy::prelude::*;
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
/// Component for cameras, with an offset from the Trackable target
///
pub struct CameraTracking {
pub offset: Vec3,
}
impl Default for CameraTracking {
fn default() -> Self {
CameraTracking {
offset: Vec3::new(0.0, 6.0, 8.0),
}
}
}
#[derive(Component, Reflect, Debug, Deref, DerefMut)]
#[reflect(Component)]
/// Component for cameras, with an offset from the Trackable target
pub struct CameraTrackingOffset(Vec3);
impl Default for CameraTrackingOffset {
fn default() -> Self {
CameraTrackingOffset(Vec3::new(0.0, 6.0, 8.0))
}
}
impl CameraTrackingOffset {
fn new(input: Vec3) -> Self {
CameraTrackingOffset(input)
}
}
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Add this component to an entity if you want it to be tracked by a Camera
pub struct CameraTrackable;
pub fn camera_track(
mut tracking_cameras: Query<
(&mut Transform, &CameraTrackingOffset),
(
With<Camera>,
With<CameraTrackingOffset>,
Without<CameraTrackable>,
),
>,
camera_tracked: Query<&Transform, With<CameraTrackable>>,
) {
for (mut camera_transform, tracking_offset) in tracking_cameras.iter_mut() {
for tracked_transform in camera_tracked.iter() {
let target_position = tracked_transform.translation + tracking_offset.0;
let eased_position = camera_transform.translation.lerp(target_position, 0.1);
camera_transform.translation = eased_position; // + tracking.offset;// tracked_transform.translation + tracking.offset;
*camera_transform = camera_transform.looking_at(tracked_transform.translation, Vec3::Y);
}
}
}

View File

@ -1,24 +0,0 @@
pub mod camera_tracking;
pub use camera_tracking::*;
pub mod camera_replace_proxies;
pub use camera_replace_proxies::*;
use bevy::prelude::*;
use bevy_gltf_blueprints::GltfBlueprintsSet;
pub struct CameraPlugin;
impl Plugin for CameraPlugin {
fn build(&self, app: &mut App) {
app.register_type::<CameraTrackable>()
.register_type::<CameraTracking>()
.register_type::<CameraTrackingOffset>()
.add_systems(
Update,
(
camera_replace_proxies.after(GltfBlueprintsSet::AfterSpawn),
camera_track,
),
);
}
}

View File

@ -1,25 +0,0 @@
use bevy::prelude::*;
use bevy::pbr::{CascadeShadowConfig, CascadeShadowConfigBuilder};
// fixme might be too specific to might needs, should it be moved out ? also these are all for lights, not models
pub fn lighting_replace_proxies(
mut added_dirights: Query<(Entity, &mut DirectionalLight), Added<DirectionalLight>>,
mut added_spotlights: Query<&mut SpotLight, Added<SpotLight>>,
mut commands: Commands,
) {
for (entity, mut light) in added_dirights.iter_mut() {
light.illuminance *= 5.0;
light.shadows_enabled = true;
let shadow_config: CascadeShadowConfig = CascadeShadowConfigBuilder {
first_cascade_far_bound: 15.0,
maximum_distance: 135.0,
..default()
}
.into();
commands.entity(entity).insert(shadow_config);
}
for mut light in added_spotlights.iter_mut() {
light.shadows_enabled = true;
}
}

View File

@ -1,18 +0,0 @@
mod lighting_replace_proxies;
use lighting_replace_proxies::*;
use bevy::pbr::{DirectionalLightShadowMap, NotShadowCaster};
use bevy::prelude::*;
pub struct LightingPlugin;
impl Plugin for LightingPlugin {
fn build(&self, app: &mut App) {
app
.insert_resource(DirectionalLightShadowMap { size: 4096 })
// FIXME: adding these since they are missing
.register_type::<NotShadowCaster>()
.add_systems(PreUpdate, lighting_replace_proxies) // FIXME: you should actually run this in a specific state most likely
;
}
}

View File

@ -1,31 +1,14 @@
pub mod camera;
pub use camera::*;
pub mod lighting;
pub use lighting::*;
pub mod relationships;
pub use relationships::*;
pub mod physics;
pub use physics::*;
use bevy::prelude::*; use bevy::prelude::*;
use bevy_gltf_blueprints::*; use bevy_gltf_blueprints::*;
pub struct CorePlugin; pub struct CorePlugin;
impl Plugin for CorePlugin { impl Plugin for CorePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(( app.add_plugins((BlueprintsPlugin {
LightingPlugin, library_folder: "models/library".into(),
CameraPlugin, format: GltfFormat::GLB,
PhysicsPlugin, aabbs: true,
BlueprintsPlugin { ..Default::default()
library_folder: "models/library".into(), },));
format: GltfFormat::GLB,
aabbs: true,
..Default::default()
},
));
} }
} }

View File

@ -1,12 +0,0 @@
use bevy::prelude::{info, ResMut};
use bevy_rapier3d::prelude::RapierConfiguration;
pub fn pause_physics(mut physics_config: ResMut<RapierConfiguration>) {
info!("pausing physics");
physics_config.physics_pipeline_active = false;
}
pub fn resume_physics(mut physics_config: ResMut<RapierConfiguration>) {
info!("unpausing physics");
physics_config.physics_pipeline_active = true;
}

View File

@ -1,37 +0,0 @@
pub mod physics_replace_proxies;
use bevy_rapier3d::{
prelude::{NoUserData, RapierPhysicsPlugin},
render::RapierDebugRenderPlugin,
};
pub use physics_replace_proxies::*;
pub mod utils;
pub mod controls;
pub use controls::*;
use crate::state::GameState;
use bevy::prelude::*;
// use super::blueprints::GltfBlueprintsSet;
use bevy_gltf_blueprints::GltfBlueprintsSet;
// use crate::Collider;
pub struct PhysicsPlugin;
impl Plugin for PhysicsPlugin {
fn build(&self, app: &mut App) {
app.add_plugins((
RapierPhysicsPlugin::<NoUserData>::default(),
RapierDebugRenderPlugin::default(),
))
.register_type::<AutoAABBCollider>()
.register_type::<physics_replace_proxies::Collider>()
// find a way to make serde's stuff serializable
// .register_type::<bevy_rapier3d::dynamics::CoefficientCombineRule>()
//bevy_rapier3d::dynamics::CoefficientCombineRule
.add_systems(
Update,
physics_replace_proxies.after(GltfBlueprintsSet::AfterSpawn),
)
.add_systems(OnEnter(GameState::InGame), resume_physics)
.add_systems(OnExit(GameState::InGame), pause_physics);
}
}

View File

@ -1,101 +0,0 @@
use bevy::prelude::*;
// use bevy::render::primitives::Aabb;
use bevy_rapier3d::geometry::Collider as RapierCollider;
use bevy_rapier3d::prelude::{ActiveCollisionTypes, ActiveEvents, ComputedColliderShape};
use super::utils::*;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum Collider {
Ball(f32),
Cuboid(Vec3),
Capsule(Vec3, Vec3, f32),
#[default]
Mesh,
}
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum AutoAABBCollider {
#[default]
Cuboid,
Ball,
Capsule,
}
// replaces all physics stand-ins with the actual rapier types
pub fn physics_replace_proxies(
meshes: Res<Assets<Mesh>>,
mesh_handles: Query<&Handle<Mesh>>,
mut proxy_colliders: Query<
(Entity, &Collider, &Name, &mut Visibility),
(Without<RapierCollider>, Added<Collider>),
>,
// needed for tri meshes
children: Query<&Children>,
mut commands: Commands,
) {
for proxy_colider in proxy_colliders.iter_mut() {
let (entity, collider_proxy, name, mut visibility) = proxy_colider;
// we hide the collider meshes: perhaps they should be removed altogether once processed ?
if name.ends_with("_collider") || name.ends_with("_sensor") {
*visibility = Visibility::Hidden;
}
let mut rapier_collider: RapierCollider;
match collider_proxy {
Collider::Ball(radius) => {
info!("generating collider from proxy: ball");
rapier_collider = RapierCollider::ball(*radius);
commands.entity(entity)
.insert(rapier_collider)
.insert(ActiveEvents::COLLISION_EVENTS) // FIXME: this is just for demo purposes !!!
;
}
Collider::Cuboid(size) => {
info!("generating collider from proxy: cuboid");
rapier_collider = RapierCollider::cuboid(size.x, size.y, size.z);
commands.entity(entity)
.insert(rapier_collider)
.insert(ActiveEvents::COLLISION_EVENTS) // FIXME: this is just for demo purposes !!!
;
}
Collider::Capsule(a, b, radius) => {
info!("generating collider from proxy: capsule");
rapier_collider = RapierCollider::capsule(*a, *b, *radius);
commands.entity(entity)
.insert(rapier_collider)
.insert(ActiveEvents::COLLISION_EVENTS) // FIXME: this is just for demo purposes !!!
;
}
Collider::Mesh => {
info!("generating collider from proxy: mesh");
for (_, collider_mesh) in
Mesh::search_in_children(entity, &children, &meshes, &mesh_handles)
{
rapier_collider = RapierCollider::from_bevy_mesh(
collider_mesh,
&ComputedColliderShape::TriMesh,
)
.unwrap();
commands
.entity(entity)
.insert(rapier_collider)
// FIXME: this is just for demo purposes !!!
.insert(
ActiveCollisionTypes::default()
| ActiveCollisionTypes::KINEMATIC_STATIC
| ActiveCollisionTypes::STATIC_STATIC
| ActiveCollisionTypes::DYNAMIC_STATIC,
)
.insert(ActiveEvents::COLLISION_EVENTS);
// .insert(ActiveEvents::COLLISION_EVENTS)
// break;
// RapierCollider::convex_hull(points)
}
}
}
}
}

View File

@ -1,175 +0,0 @@
use bevy::prelude::*;
use bevy::render::mesh::{MeshVertexAttributeId, PrimitiveTopology, VertexAttributeValues};
// TAKEN VERBATIB FROM https://github.com/janhohenheim/foxtrot/blob/src/util/trait_extension.rs
pub(crate) trait Vec3Ext: Copy {
fn is_approx_zero(self) -> bool;
fn split(self, up: Vec3) -> SplitVec3;
}
impl Vec3Ext for Vec3 {
#[inline]
fn is_approx_zero(self) -> bool {
self.length_squared() < 1e-5
}
#[inline]
fn split(self, up: Vec3) -> SplitVec3 {
let vertical = up * self.dot(up);
let horizontal = self - vertical;
SplitVec3 {
vertical,
horizontal,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct SplitVec3 {
pub(crate) vertical: Vec3,
pub(crate) horizontal: Vec3,
}
pub(crate) trait Vec2Ext: Copy {
fn is_approx_zero(self) -> bool;
fn x0y(self) -> Vec3;
}
impl Vec2Ext for Vec2 {
#[inline]
fn is_approx_zero(self) -> bool {
self.length_squared() < 1e-5
}
#[inline]
fn x0y(self) -> Vec3 {
Vec3::new(self.x, 0., self.y)
}
}
pub(crate) trait MeshExt {
fn transform(&mut self, transform: Transform);
fn transformed(&self, transform: Transform) -> Mesh;
fn read_coords_mut(&mut self, id: impl Into<MeshVertexAttributeId>) -> &mut Vec<[f32; 3]>;
fn search_in_children<'a>(
parent: Entity,
children: &'a Query<&Children>,
meshes: &'a Assets<Mesh>,
mesh_handles: &'a Query<&Handle<Mesh>>,
) -> Vec<(Entity, &'a Mesh)>;
}
impl MeshExt for Mesh {
fn transform(&mut self, transform: Transform) {
for coords in self.read_coords_mut(Mesh::ATTRIBUTE_POSITION.clone()) {
let vec3 = (*coords).into();
let transformed = transform.transform_point(vec3);
*coords = transformed.into();
}
for normal in self.read_coords_mut(Mesh::ATTRIBUTE_NORMAL.clone()) {
let vec3 = (*normal).into();
let transformed = transform.rotation.mul_vec3(vec3);
*normal = transformed.into();
}
}
fn transformed(&self, transform: Transform) -> Mesh {
let mut mesh = self.clone();
mesh.transform(transform);
mesh
}
fn read_coords_mut(&mut self, id: impl Into<MeshVertexAttributeId>) -> &mut Vec<[f32; 3]> {
// Guaranteed by Bevy for the current usage
match self
.attribute_mut(id)
.expect("Failed to read unknown mesh attribute")
{
VertexAttributeValues::Float32x3(values) => values,
// Guaranteed by Bevy for the current usage
_ => unreachable!(),
}
}
fn search_in_children<'a>(
parent: Entity,
children_query: &'a Query<&Children>,
meshes: &'a Assets<Mesh>,
mesh_handles: &'a Query<&Handle<Mesh>>,
) -> Vec<(Entity, &'a Mesh)> {
if let Ok(children) = children_query.get(parent) {
let mut result: Vec<_> = children
.iter()
.filter_map(|entity| mesh_handles.get(*entity).ok().map(|mesh| (*entity, mesh)))
.map(|(entity, mesh_handle)| {
(
entity,
meshes
.get(mesh_handle)
.expect("Failed to get mesh from handle"),
)
})
.map(|(entity, mesh)| {
assert_eq!(mesh.primitive_topology(), PrimitiveTopology::TriangleList);
(entity, mesh)
})
.collect();
let mut inner_result = children
.iter()
.flat_map(|entity| {
Self::search_in_children(*entity, children_query, meshes, mesh_handles)
})
.collect();
result.append(&mut inner_result);
result
} else {
Vec::new()
}
}
}
pub(crate) trait F32Ext: Copy {
fn is_approx_zero(self) -> bool;
fn squared(self) -> f32;
fn lerp(self, other: f32, ratio: f32) -> f32;
}
impl F32Ext for f32 {
#[inline]
fn is_approx_zero(self) -> bool {
self.abs() < 1e-5
}
#[inline]
fn squared(self) -> f32 {
self * self
}
#[inline]
fn lerp(self, other: f32, ratio: f32) -> f32 {
self.mul_add(1. - ratio, other * ratio)
}
}
pub(crate) trait TransformExt: Copy {
fn horizontally_looking_at(self, target: Vec3, up: Vec3) -> Transform;
fn lerp(self, other: Transform, ratio: f32) -> Transform;
}
impl TransformExt for Transform {
fn horizontally_looking_at(self, target: Vec3, up: Vec3) -> Transform {
let direction = target - self.translation;
let horizontal_direction = direction - up * direction.dot(up);
let look_target = self.translation + horizontal_direction;
self.looking_at(look_target, up)
}
fn lerp(self, other: Transform, ratio: f32) -> Transform {
let translation = self.translation.lerp(other.translation, ratio);
let rotation = self.rotation.slerp(other.rotation, ratio);
let scale = self.scale.lerp(other.scale, ratio);
Transform {
translation,
rotation,
scale,
}
}
}

View File

@ -1,75 +0,0 @@
use bevy::prelude::*;
use bevy::render::mesh::{MeshVertexAttributeId, PrimitiveTopology, VertexAttributeValues};
// TAKEN VERBATIB FROM https://github.com/janhohenheim/foxtrot/blob/6e31fc02652fc9d085a4adde0a73ab007dbbb0dc/src/util/trait_extension.rs
pub trait Vec3Ext {
#[allow(clippy::wrong_self_convention)] // Because [`Vec3`] is [`Copy`]
fn is_approx_zero(self) -> bool;
fn x0z(self) -> Vec3;
}
impl Vec3Ext for Vec3 {
fn is_approx_zero(self) -> bool {
[self.x, self.y, self.z].iter().all(|&x| x.abs() < 1e-5)
}
fn x0z(self) -> Vec3 {
Vec3::new(self.x, 0., self.z)
}
}
pub trait MeshExt {
fn transform(&mut self, transform: Transform);
fn transformed(&self, transform: Transform) -> Mesh;
fn read_coords_mut(&mut self, id: impl Into<MeshVertexAttributeId>) -> &mut Vec<[f32; 3]>;
fn search_in_children<'a>(
children: &'a Children,
meshes: &'a Assets<Mesh>,
mesh_handles: &'a Query<&Handle<Mesh>>,
) -> (Entity, &'a Mesh);
}
impl MeshExt for Mesh {
fn transform(&mut self, transform: Transform) {
for attribute in [Mesh::ATTRIBUTE_POSITION, Mesh::ATTRIBUTE_NORMAL] {
for coords in self.read_coords_mut(attribute.clone()) {
let vec3 = (*coords).into();
let transformed = transform.transform_point(vec3);
*coords = transformed.into();
}
}
}
fn transformed(&self, transform: Transform) -> Mesh {
let mut mesh = self.clone();
mesh.transform(transform);
mesh
}
fn read_coords_mut(&mut self, id: impl Into<MeshVertexAttributeId>) -> &mut Vec<[f32; 3]> {
match self.attribute_mut(id).unwrap() {
VertexAttributeValues::Float32x3(values) => values,
// Guaranteed by Bevy
_ => unreachable!(),
}
}
fn search_in_children<'a>(
children: &'a Children,
meshes: &'a Assets<Mesh>,
mesh_handles: &'a Query<&Handle<Mesh>>,
) -> (Entity, &'a Mesh) {
let entity_handles: Vec<_> = children
.iter()
.filter_map(|entity| mesh_handles.get(*entity).ok().map(|mesh| (*entity, mesh)))
.collect();
assert_eq!(
entity_handles.len(),
1,
"Collider must contain exactly one mesh, but found {}",
entity_handles.len()
);
let (entity, mesh_handle) = entity_handles.first().unwrap();
let mesh = meshes.get(mesh_handle).unwrap();
assert_eq!(mesh.primitive_topology(), PrimitiveTopology::TriangleList);
(*entity, mesh)
}
}

View File

@ -1,11 +0,0 @@
pub mod relationships_insert_dependant_components;
pub use relationships_insert_dependant_components::*;
use bevy::prelude::*;
pub struct EcsRelationshipsPlugin;
impl Plugin for EcsRelationshipsPlugin {
fn build(&self, app: &mut App) {
app;
}
}

View File

@ -1,15 +0,0 @@
use bevy::prelude::*;
pub fn insert_dependant_component<
Dependant: Component,
Dependency: Component + std::default::Default,
>(
mut commands: Commands,
entities_without_depency: Query<(Entity, &Name), (With<Dependant>, Without<Dependency>)>,
) {
for (entity, name) in entities_without_depency.iter() {
let name = name.clone().to_string();
commands.entity(entity).insert(Dependency::default());
warn!("found an entity called {} with a {} component but without an {}, please check your assets", name.clone(), std::any::type_name::<Dependant>(), std::any::type_name::<Dependency>());
}
}

View File

@ -1,10 +1,6 @@
use bevy::prelude::*; use bevy::prelude::*;
use crate::{
assets::GameAssets,
state::{GameState, InAppRunning},
};
use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag}; use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag};
use bevy_gltf_worlflow_examples_common::{assets::GameAssets, GameState, InAppRunning};
use bevy_rapier3d::prelude::Velocity; use bevy_rapier3d::prelude::Velocity;
use rand::Rng; use rand::Rng;
@ -15,7 +11,6 @@ pub fn setup_game(
models: Res<Assets<bevy::gltf::Gltf>>, models: Res<Assets<bevy::gltf::Gltf>>,
mut next_game_state: ResMut<NextState<GameState>>, mut next_game_state: ResMut<NextState<GameState>>,
) { ) {
println!("setting up all stuff");
commands.insert_resource(AmbientLight { commands.insert_resource(AmbientLight {
color: Color::WHITE, color: Color::WHITE,
brightness: 0.2, brightness: 0.2,

View File

@ -1,6 +1,5 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_gltf_worlflow_examples_common::{AppState, InMainMenu};
use crate::state::{AppState, GameState, InMainMenu};
pub fn setup_main_menu(mut commands: Commands) { pub fn setup_main_menu(mut commands: Commands) {
commands.spawn((Camera2dBundle::default(), InMainMenu)); commands.spawn((Camera2dBundle::default(), InMainMenu));

View File

@ -4,113 +4,19 @@ pub use in_game::*;
pub mod in_main_menu; pub mod in_main_menu;
pub use in_main_menu::*; pub use in_main_menu::*;
pub mod picking;
pub use picking::*;
use crate::{
insert_dependant_component,
state::{AppState, GameState},
};
use bevy::prelude::*; use bevy::prelude::*;
use bevy_rapier3d::prelude::*; use bevy_gltf_worlflow_examples_common::{AppState, GameState};
// this file is just for demo purposes, contains various types of components, systems etc
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum SoundMaterial {
Metal,
Wood,
Rock,
Cloth,
Squishy,
#[default]
None,
}
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo marker component
pub struct Player;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo component showing auto injection of components
pub struct ShouldBeWithPlayer;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo marker component
pub struct Interactible;
fn player_move_demo(
keycode: Res<Input<KeyCode>>,
mut players: Query<&mut Transform, With<Player>>,
) {
let speed = 0.2;
if let Ok(mut player) = players.get_single_mut() {
if keycode.pressed(KeyCode::Left) {
player.translation.x += speed;
}
if keycode.pressed(KeyCode::Right) {
player.translation.x -= speed;
}
if keycode.pressed(KeyCode::Up) {
player.translation.z += speed;
}
if keycode.pressed(KeyCode::Down) {
player.translation.z -= speed;
}
}
}
// collision tests/debug
pub fn test_collision_events(
mut collision_events: EventReader<CollisionEvent>,
mut contact_force_events: EventReader<ContactForceEvent>,
) {
for collision_event in collision_events.read() {
println!("collision");
match collision_event {
CollisionEvent::Started(_entity1, _entity2, _) => {
println!("collision started")
}
CollisionEvent::Stopped(_entity1, _entity2, _) => {
println!("collision ended")
}
}
}
for contact_force_event in contact_force_events.read() {
println!("Received contact force event: {:?}", contact_force_event);
}
}
pub struct GamePlugin; pub struct GamePlugin;
impl Plugin for GamePlugin { impl Plugin for GamePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(PickingPlugin) app.add_systems(
.register_type::<Interactible>() Update,
.register_type::<SoundMaterial>() (spawn_test, spawn_test_unregisted_components).run_if(in_state(GameState::InGame)),
.register_type::<Player>() )
// little helper utility, to automatically inject components that are dependant on an other component .add_systems(OnEnter(AppState::MenuRunning), setup_main_menu)
// ie, here an Entity with a Player component should also always have a ShouldBeWithPlayer component .add_systems(OnExit(AppState::MenuRunning), teardown_main_menu)
// you get a warning if you use this, as I consider this to be stop-gap solution (usually you should have either a bundle, or directly define all needed components) .add_systems(Update, main_menu.run_if(in_state(AppState::MenuRunning)))
.add_systems( .add_systems(OnEnter(AppState::AppRunning), setup_game);
Update,
(
// insert_dependant_component::<Player, ShouldBeWithPlayer>,
player_move_demo, //.run_if(in_state(AppState::Running)),
// test_collision_events
spawn_test,
spawn_test_unregisted_components,
)
.run_if(in_state(GameState::InGame)),
)
.add_systems(OnEnter(AppState::MenuRunning), setup_main_menu)
.add_systems(OnExit(AppState::MenuRunning), teardown_main_menu)
.add_systems(Update, main_menu.run_if(in_state(AppState::MenuRunning)))
.add_systems(OnEnter(AppState::AppRunning), setup_game);
} }
} }

View File

@ -1,34 +0,0 @@
use super::Player;
use bevy::prelude::*;
use bevy_gltf_blueprints::GltfBlueprintsSet;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct Pickable;
// very simple, crude picking (as in picking up objects) implementation
pub fn picking(
players: Query<&GlobalTransform, With<Player>>,
pickables: Query<(Entity, &GlobalTransform), With<Pickable>>,
mut commands: Commands,
) {
for player_transforms in players.iter() {
for (pickable, pickable_transforms) in pickables.iter() {
let distance = player_transforms
.translation()
.distance(pickable_transforms.translation());
if distance < 2.5 {
commands.entity(pickable).despawn_recursive();
}
}
}
}
pub struct PickingPlugin;
impl Plugin for PickingPlugin {
fn build(&self, app: &mut App) {
app.register_type::<Pickable>()
.add_systems(Update, (picking.after(GltfBlueprintsSet::AfterSpawn),));
}
}

View File

@ -1,15 +1,10 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_editor_pls::prelude::*; use bevy_editor_pls::prelude::*;
use bevy_gltf_worlflow_examples_common::CommonPlugin;
mod core; mod core;
use crate::core::*; use crate::core::*;
pub mod assets;
use assets::*;
pub mod state;
use state::*;
mod game; mod game;
use game::*; use game::*;
@ -23,8 +18,7 @@ fn main() {
// editor // editor
EditorPlugin::default(), EditorPlugin::default(),
// our custom plugins // our custom plugins
StatePlugin, CommonPlugin,
AssetsPlugin,
CorePlugin, // reusable plugins CorePlugin, // reusable plugins
GamePlugin, // specific to our game GamePlugin, // specific to our game
ComponentsTestPlugin, // Showcases different type of components /structs ComponentsTestPlugin, // Showcases different type of components /structs

View File

@ -1,54 +0,0 @@
use bevy::app::AppExit;
use bevy::prelude::*;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)]
pub enum AppState {
#[default]
CoreLoading,
MenuRunning,
AppLoading,
AppRunning,
AppEnding,
// FIXME: not sure
LoadingGame,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)]
pub enum GameState {
#[default]
None,
InMenu,
InGame,
InGameOver,
InSaving,
InLoading,
}
// tag components for all entities within a certain state (for despawning them if needed) , FIXME: seems kinda hack-ish
#[derive(Component)]
pub struct InCoreLoading;
#[derive(Component, Default)]
pub struct InMenuRunning;
#[derive(Component)]
pub struct InAppLoading;
#[derive(Component)]
pub struct InAppRunning;
// components for tagging in game vs in game menu stuff
#[derive(Component, Default)]
pub struct InMainMenu;
#[derive(Component, Default)]
pub struct InMenu;
#[derive(Component, Default)]
pub struct InGame;
pub struct StatePlugin;
impl Plugin for StatePlugin {
fn build(&self, app: &mut App) {
app.add_state::<AppState>().add_state::<GameState>();
}
}

View File

@ -6,39 +6,39 @@ struct UnitTest;
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)] #[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleTestF32(f32); struct TupleTestF32(f32);
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)] #[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleTestU64(u64); struct TupleTestU64(u64);
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)] #[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
#[reflect(Component)] #[reflect(Component)]
pub struct TuppleTestStr(String); pub struct TupleTestStr(String);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleTest2(f32, u64, String); struct TupleTest2(f32, u64, String);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleTestBool(bool); struct TupleTestBool(bool);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleVec2(Vec2); struct TupleVec2(Vec2);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleVec3(Vec3); struct TupleVec3(Vec3);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleVec(Vec<String>); struct TupleVec(Vec<String>);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct TuppleTestColor(Color); struct TupleTestColor(Color);
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
@ -65,16 +65,16 @@ impl Plugin for ComponentsTestPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.register_type::<BasicTest>() app.register_type::<BasicTest>()
.register_type::<UnitTest>() .register_type::<UnitTest>()
.register_type::<TuppleTestF32>() .register_type::<TupleTestF32>()
.register_type::<TuppleTestU64>() .register_type::<TupleTestU64>()
.register_type::<TuppleTestStr>() .register_type::<TupleTestStr>()
.register_type::<TuppleTestBool>() .register_type::<TupleTestBool>()
.register_type::<TuppleTest2>() .register_type::<TupleTest2>()
.register_type::<TuppleVec2>() .register_type::<TupleVec2>()
.register_type::<TuppleVec3>() .register_type::<TupleVec3>()
.register_type::<EnumTest>() .register_type::<EnumTest>()
.register_type::<TuppleTestColor>() .register_type::<TupleTestColor>()
.register_type::<TuppleVec>() .register_type::<TupleVec>()
.register_type::<Vec<String>>(); .register_type::<Vec<String>>();
} }
} }

View File

@ -1,13 +0,0 @@
[package]
name = "bevy_gltf_blueprints_basic_scene_components_example"
version = "0.3.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[dependencies]
bevy="0.12"
bevy_gltf_blueprints = { path = "../../../crates/bevy_gltf_blueprints" }
bevy_rapier3d = { version = "0.23.0", features = [ "serde-serialize", "debug-render-3d", "enhanced-determinism"] }
bevy_asset_loader = { version = "0.18", features = ["standard_dynamic_assets" ]}
bevy_editor_pls = { version = "0.6" }
rand = "0.8.5"

View File

@ -1,11 +0,0 @@
# Basic scene components demo
This example showcases the use of "scene" components ie components that are injected into the root scene/level so that you can control things
like ambient lighting, bloom, shadowmap resolution, & ao directly from Blender.
## Running this example
```
cargo run --features bevy/dynamic_linking
```

View File

@ -1,6 +0,0 @@
({
"world":File (path: "models/World.glb"),
"models": Folder (
path: "models/library",
),
})

View File

@ -1,485 +0,0 @@
(
resources: {},
entities: {
20: (
components: {
"bevy_render::camera::projection::Projection": Perspective((
fov: 0.3995965,
aspect_ratio: 1.7777778,
near: 0.1,
far: 100.0,
)),
"bevy_render::primitives::Frustum": (),
"bevy_transform::components::transform::Transform": (
translation: (
x: 34.821884,
y: 49.024857,
z: -36.79615,
),
rotation: (-0.1694689, 0.82838506, 0.40884802, 0.3433684),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core_pipeline::tonemapping::Tonemapping": BlenderFilmic,
"bevy_core_pipeline::tonemapping::DebandDither": Enabled,
"bevy_render::view::ColorGrading": (
exposure: 0.0,
gamma: 1.0,
pre_saturation: 1.0,
post_saturation: 1.0,
),
"bevy_core::name::Name": (
hash: 17702508670109176045,
name: "Camera",
),
"advanced::core::camera::camera_tracking::CameraTrackingOffset": ((
x: 26.0,
y: 48.0,
z: -26.0,
)),
"bevy_pbr::light::ClusterConfig": FixedZ(
total: 4096,
z_slices: 24,
z_config: (
first_slice_depth: 5.0,
far_z_mode: MaxLightRange,
),
dynamic_resizing: true,
),
"bevy_core_pipeline::bloom::settings::BloomSettings": (
intensity: 0.01,
low_frequency_boost: 0.7,
low_frequency_boost_curvature: 0.95,
high_pass_frequency: 1.0,
prefilter_settings: (
threshold: 0.0,
threshold_softness: 0.0,
),
composite_mode: Additive,
),
},
),
34: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 4.697565,
y: 1.5983224,
z: 8.962274,
),
rotation: (0.000000000000000031724054, -0.00000000000000000000647681, -0.000013119204, 1.0),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 9837288155836662016,
name: "Health_Pickup.001",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
},
),
54: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 8.799996,
y: 1.02484,
z: -10.799994,
),
rotation: (0.0, 0.0, 0.0, 1.0),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 17978181434632022651,
name: "Player",
),
"advanced::core::camera::camera_tracking::CameraTrackable": (),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Player"),
"advanced::game::Player": (),
"advanced::game::SoundMaterial": Wood,
},
),
60: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 3.6351967,
y: 1.7298106,
z: -7.313273,
),
rotation: (0.0, 0.0, 0.0, 1.0),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 7225506896223411979,
name: "MagicTeapot.001",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("MagicTeapot"),
},
),
64: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: -4.6068983,
y: 1.5983224,
z: -10.579347,
),
rotation: (0.000000000000000031724054, 0.00000000000000000000647681, 0.000013119204, 1.0),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 3089896164553476909,
name: "Health_Pickup.002",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
},
),
72: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: -11.560788,
y: 0.0,
z: 7.6554174,
),
rotation: (0.0, 0.0, 0.0, 1.0),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 16961132108296874979,
name: "Container.001",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Container"),
"advanced::game::picking::Pickable": (),
},
),
80: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: -21.397858,
y: 0.3833189,
z: -0.32418346,
),
rotation: (0.0, 0.0, 0.0, 1.0),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 5104740624378885265,
name: "Container.002",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Container"),
"advanced::game::picking::Pickable": (),
},
),
82: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 2.9156065,
y: 1.4984571,
z: 2.1909573,
),
rotation: (0.058853183, 0.0726243, 0.2048649, 0.97431636),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 107557640935939866,
name: "test5159735758431545549",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: -1.2580805,
y: -0.39687577,
z: 0.4816798,
),
angvel: (
x: 0.2979751,
y: 0.07926611,
z: 0.8434645,
),
),
},
),
86: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 0.26087752,
y: 1.5525806,
z: 1.5980839,
),
rotation: (0.059497803, -0.0000018232388, 0.13145457, 0.9895351),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 3398656236303073559,
name: "test7470642598731063943",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: -0.9268077,
y: -0.19806683,
z: 0.41948256,
),
angvel: (
x: 0.26946256,
y: -0.000006710977,
z: 0.5953494,
),
),
},
),
90: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 2.6515265,
y: 1.5944021,
z: -4.391837,
),
rotation: (-0.030030435, -0.0000006527225, 0.029748484, 0.9991062),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 12541900054595385134,
name: "test3938024405863834719",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: -0.28430828,
y: -0.022357654,
z: -0.2870027,
),
angvel: (
x: -0.17986917,
y: -0.0000035613396,
z: 0.17818078,
),
),
},
),
94: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: -4.2356462,
y: 1.596993,
z: 0.7254991,
),
rotation: (-0.0221751, -0.0000000001891749, 0.011065631, 0.99969286),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 6757906322211730861,
name: "test11007490954016878479",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: -0.21747473,
y: -0.014912919,
z: -0.43581253,
),
angvel: (
x: -0.2727097,
y: -0.0000000034594905,
z: 0.13608481,
),
),
},
),
98: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 3.1525247,
y: 1.5518407,
z: -2.9611976,
),
rotation: (-0.09219627, 0.1602262, -0.11205085, 0.9763565),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 12588565107899185946,
name: "test5980867849331267699",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: 0.8323179,
y: -0.20597076,
z: -0.68975484,
),
angvel: (
x: -0.37971017,
y: 0.49603412,
z: -0.6079359,
),
),
},
),
4294967310: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 4.826278,
y: 1.2710563,
z: -3.1997645,
),
rotation: (-0.303028, 0.00000087800436, -0.23889118, 0.9225535),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 15533546218717453536,
name: "test12380979123759326444",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: 1.2146912,
y: -1.1640646,
z: -1.5408095,
),
angvel: (
x: -1.1932359,
y: 0.000002945365,
z: -0.94068503,
),
),
},
),
4294967314: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 3.9906094,
y: 1.4824095,
z: 2.4394412,
),
rotation: (0.06015042, 0.085218765, 0.2215642, 0.9695509),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 2466794778849297109,
name: "test12475628281920299197",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: -1.0818624,
y: -0.37798148,
z: 0.45334253,
),
angvel: (
x: 0.25961447,
y: 0.14854014,
z: 0.7426717,
),
),
},
),
4294967321: (
components: {
"bevy_transform::components::transform::Transform": (
translation: (
x: 2.2306876,
y: 0.989814,
z: -1.3596333,
),
rotation: (0.30614096, 0.002587511, -0.42789298, 0.8503991),
scale: (
x: 1.0,
y: 1.0,
z: 1.0,
),
),
"bevy_core::name::Name": (
hash: 1545925632270385398,
name: "test15780367212768138828",
),
"bevy_gltf_blueprints::spawn_from_blueprints::BlueprintName": ("Health_Pickup"),
"advanced::game::picking::Pickable": (),
"bevy_rapier3d::dynamics::rigid_body::Velocity": (
linvel: (
x: 1.3027526,
y: -1.8947054,
z: 1.6179247,
),
angvel: (
x: 1.4565696,
y: -0.16299045,
z: -1.3631926,
),
),
},
),
},
)

View File

@ -1,5 +0,0 @@
use bevy::prelude::*;
use bevy_asset_loader::prelude::*;
#[derive(AssetCollection, Resource)]
pub struct CoreAssets {}

View File

@ -1,13 +0,0 @@
use bevy::gltf::Gltf;
use bevy::prelude::*;
use bevy::utils::HashMap;
use bevy_asset_loader::prelude::*;
#[derive(AssetCollection, Resource)]
pub struct GameAssets {
#[asset(key = "world")]
pub world: Handle<Gltf>,
#[asset(key = "models", collection(typed, mapped))]
pub models: HashMap<String, Handle<Gltf>>,
}

View File

@ -1,35 +0,0 @@
pub mod assets_core;
pub use assets_core::*;
pub mod assets_game;
pub use assets_game::*;
use bevy::prelude::*;
use bevy_asset_loader::prelude::*;
use crate::state::AppState;
pub struct AssetsPlugin;
impl Plugin for AssetsPlugin {
fn build(&self, app: &mut App) {
app
// load core assets (ie assets needed in the main menu, and everywhere else before loading more assets in game)
.add_loading_state(
LoadingState::new(AppState::CoreLoading).continue_to_state(AppState::MenuRunning),
)
.add_dynamic_collection_to_loading_state::<_, StandardDynamicAssetCollection>(
AppState::CoreLoading,
"assets_core.assets.ron",
)
.add_collection_to_loading_state::<_, CoreAssets>(AppState::CoreLoading)
// load game assets
.add_loading_state(
LoadingState::new(AppState::AppLoading).continue_to_state(AppState::AppRunning),
)
.add_dynamic_collection_to_loading_state::<_, StandardDynamicAssetCollection>(
AppState::AppLoading,
"assets_game.assets.ron",
)
.add_collection_to_loading_state::<_, GameAssets>(AppState::AppLoading);
}
}

View File

@ -1,58 +0,0 @@
use bevy::prelude::*;
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
/// Component for cameras, with an offset from the Trackable target
///
pub struct CameraTracking {
pub offset: Vec3,
}
impl Default for CameraTracking {
fn default() -> Self {
CameraTracking {
offset: Vec3::new(0.0, 6.0, 8.0),
}
}
}
#[derive(Component, Reflect, Debug, Deref, DerefMut)]
#[reflect(Component)]
/// Component for cameras, with an offset from the Trackable target
pub struct CameraTrackingOffset(Vec3);
impl Default for CameraTrackingOffset {
fn default() -> Self {
CameraTrackingOffset(Vec3::new(0.0, 6.0, 8.0))
}
}
impl CameraTrackingOffset {
fn new(input: Vec3) -> Self {
CameraTrackingOffset(input)
}
}
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Add this component to an entity if you want it to be tracked by a Camera
pub struct CameraTrackable;
pub fn camera_track(
mut tracking_cameras: Query<
(&mut Transform, &CameraTrackingOffset),
(
With<Camera>,
With<CameraTrackingOffset>,
Without<CameraTrackable>,
),
>,
camera_tracked: Query<&Transform, With<CameraTrackable>>,
) {
for (mut camera_transform, tracking_offset) in tracking_cameras.iter_mut() {
for tracked_transform in camera_tracked.iter() {
let target_position = tracked_transform.translation + tracking_offset.0;
let eased_position = camera_transform.translation.lerp(target_position, 0.1);
camera_transform.translation = eased_position; // + tracking.offset;// tracked_transform.translation + tracking.offset;
*camera_transform = camera_transform.looking_at(tracked_transform.translation, Vec3::Y);
}
}
}

View File

@ -1,31 +0,0 @@
pub mod camera;
pub use camera::*;
pub mod lighting;
pub use lighting::*;
pub mod relationships;
pub use relationships::*;
pub mod physics;
pub use physics::*;
use bevy::prelude::*;
use bevy_gltf_blueprints::*;
pub struct CorePlugin;
impl Plugin for CorePlugin {
fn build(&self, app: &mut App) {
app.add_plugins((
LightingPlugin,
CameraPlugin,
PhysicsPlugin,
BlueprintsPlugin {
library_folder: "models/library".into(),
format: GltfFormat::GLB,
aabbs: true,
..Default::default()
},
));
}
}

View File

@ -1,25 +0,0 @@
use bevy::{
ecs::system::Res,
input::{keyboard::KeyCode, Input},
prelude::{info, ResMut},
};
use bevy_rapier3d::{prelude::RapierConfiguration, render::DebugRenderContext};
pub fn pause_physics(mut physics_config: ResMut<RapierConfiguration>) {
info!("pausing physics");
physics_config.physics_pipeline_active = false;
}
pub fn resume_physics(mut physics_config: ResMut<RapierConfiguration>) {
info!("unpausing physics");
physics_config.physics_pipeline_active = true;
}
pub fn toggle_physics_debug(
mut debug_config: ResMut<DebugRenderContext>,
keycode: Res<Input<KeyCode>>,
) {
if keycode.just_pressed(KeyCode::D) {
debug_config.enabled = !debug_config.enabled;
}
}

View File

@ -1,101 +0,0 @@
use bevy::prelude::*;
// use bevy::render::primitives::Aabb;
use bevy_rapier3d::geometry::Collider as RapierCollider;
use bevy_rapier3d::prelude::{ActiveCollisionTypes, ActiveEvents, ComputedColliderShape};
use super::utils::*;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum Collider {
Ball(f32),
Cuboid(Vec3),
Capsule(Vec3, Vec3, f32),
#[default]
Mesh,
}
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum AutoAABBCollider {
#[default]
Cuboid,
Ball,
Capsule,
}
// replaces all physics stand-ins with the actual rapier types
pub fn physics_replace_proxies(
meshes: Res<Assets<Mesh>>,
mesh_handles: Query<&Handle<Mesh>>,
mut proxy_colliders: Query<
(Entity, &Collider, &Name, &mut Visibility),
(Without<RapierCollider>, Added<Collider>),
>,
// needed for tri meshes
children: Query<&Children>,
mut commands: Commands,
) {
for proxy_colider in proxy_colliders.iter_mut() {
let (entity, collider_proxy, name, mut visibility) = proxy_colider;
// we hide the collider meshes: perhaps they should be removed altogether once processed ?
if name.ends_with("_collider") || name.ends_with("_sensor") {
*visibility = Visibility::Hidden;
}
let mut rapier_collider: RapierCollider;
match collider_proxy {
Collider::Ball(radius) => {
info!("generating collider from proxy: ball");
rapier_collider = RapierCollider::ball(*radius);
commands.entity(entity)
.insert(rapier_collider)
.insert(ActiveEvents::COLLISION_EVENTS) // FIXME: this is just for demo purposes !!!
;
}
Collider::Cuboid(size) => {
info!("generating collider from proxy: cuboid");
rapier_collider = RapierCollider::cuboid(size.x, size.y, size.z);
commands.entity(entity)
.insert(rapier_collider)
.insert(ActiveEvents::COLLISION_EVENTS) // FIXME: this is just for demo purposes !!!
;
}
Collider::Capsule(a, b, radius) => {
info!("generating collider from proxy: capsule");
rapier_collider = RapierCollider::capsule(*a, *b, *radius);
commands.entity(entity)
.insert(rapier_collider)
.insert(ActiveEvents::COLLISION_EVENTS) // FIXME: this is just for demo purposes !!!
;
}
Collider::Mesh => {
info!("generating collider from proxy: mesh");
for (_, collider_mesh) in
Mesh::search_in_children(entity, &children, &meshes, &mesh_handles)
{
rapier_collider = RapierCollider::from_bevy_mesh(
collider_mesh,
&ComputedColliderShape::TriMesh,
)
.unwrap();
commands
.entity(entity)
.insert(rapier_collider)
// FIXME: this is just for demo purposes !!!
.insert(
ActiveCollisionTypes::default()
| ActiveCollisionTypes::KINEMATIC_STATIC
| ActiveCollisionTypes::STATIC_STATIC
| ActiveCollisionTypes::DYNAMIC_STATIC,
)
.insert(ActiveEvents::COLLISION_EVENTS);
// .insert(ActiveEvents::COLLISION_EVENTS)
// break;
// RapierCollider::convex_hull(points)
}
}
}
}
}

View File

@ -1,175 +0,0 @@
use bevy::prelude::*;
use bevy::render::mesh::{MeshVertexAttributeId, PrimitiveTopology, VertexAttributeValues};
// TAKEN VERBATIB FROM https://github.com/janhohenheim/foxtrot/blob/src/util/trait_extension.rs
pub(crate) trait Vec3Ext: Copy {
fn is_approx_zero(self) -> bool;
fn split(self, up: Vec3) -> SplitVec3;
}
impl Vec3Ext for Vec3 {
#[inline]
fn is_approx_zero(self) -> bool {
self.length_squared() < 1e-5
}
#[inline]
fn split(self, up: Vec3) -> SplitVec3 {
let vertical = up * self.dot(up);
let horizontal = self - vertical;
SplitVec3 {
vertical,
horizontal,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct SplitVec3 {
pub(crate) vertical: Vec3,
pub(crate) horizontal: Vec3,
}
pub(crate) trait Vec2Ext: Copy {
fn is_approx_zero(self) -> bool;
fn x0y(self) -> Vec3;
}
impl Vec2Ext for Vec2 {
#[inline]
fn is_approx_zero(self) -> bool {
self.length_squared() < 1e-5
}
#[inline]
fn x0y(self) -> Vec3 {
Vec3::new(self.x, 0., self.y)
}
}
pub(crate) trait MeshExt {
fn transform(&mut self, transform: Transform);
fn transformed(&self, transform: Transform) -> Mesh;
fn read_coords_mut(&mut self, id: impl Into<MeshVertexAttributeId>) -> &mut Vec<[f32; 3]>;
fn search_in_children<'a>(
parent: Entity,
children: &'a Query<&Children>,
meshes: &'a Assets<Mesh>,
mesh_handles: &'a Query<&Handle<Mesh>>,
) -> Vec<(Entity, &'a Mesh)>;
}
impl MeshExt for Mesh {
fn transform(&mut self, transform: Transform) {
for coords in self.read_coords_mut(Mesh::ATTRIBUTE_POSITION.clone()) {
let vec3 = (*coords).into();
let transformed = transform.transform_point(vec3);
*coords = transformed.into();
}
for normal in self.read_coords_mut(Mesh::ATTRIBUTE_NORMAL.clone()) {
let vec3 = (*normal).into();
let transformed = transform.rotation.mul_vec3(vec3);
*normal = transformed.into();
}
}
fn transformed(&self, transform: Transform) -> Mesh {
let mut mesh = self.clone();
mesh.transform(transform);
mesh
}
fn read_coords_mut(&mut self, id: impl Into<MeshVertexAttributeId>) -> &mut Vec<[f32; 3]> {
// Guaranteed by Bevy for the current usage
match self
.attribute_mut(id)
.expect("Failed to read unknown mesh attribute")
{
VertexAttributeValues::Float32x3(values) => values,
// Guaranteed by Bevy for the current usage
_ => unreachable!(),
}
}
fn search_in_children<'a>(
parent: Entity,
children_query: &'a Query<&Children>,
meshes: &'a Assets<Mesh>,
mesh_handles: &'a Query<&Handle<Mesh>>,
) -> Vec<(Entity, &'a Mesh)> {
if let Ok(children) = children_query.get(parent) {
let mut result: Vec<_> = children
.iter()
.filter_map(|entity| mesh_handles.get(*entity).ok().map(|mesh| (*entity, mesh)))
.map(|(entity, mesh_handle)| {
(
entity,
meshes
.get(mesh_handle)
.expect("Failed to get mesh from handle"),
)
})
.map(|(entity, mesh)| {
assert_eq!(mesh.primitive_topology(), PrimitiveTopology::TriangleList);
(entity, mesh)
})
.collect();
let mut inner_result = children
.iter()
.flat_map(|entity| {
Self::search_in_children(*entity, children_query, meshes, mesh_handles)
})
.collect();
result.append(&mut inner_result);
result
} else {
Vec::new()
}
}
}
pub(crate) trait F32Ext: Copy {
fn is_approx_zero(self) -> bool;
fn squared(self) -> f32;
fn lerp(self, other: f32, ratio: f32) -> f32;
}
impl F32Ext for f32 {
#[inline]
fn is_approx_zero(self) -> bool {
self.abs() < 1e-5
}
#[inline]
fn squared(self) -> f32 {
self * self
}
#[inline]
fn lerp(self, other: f32, ratio: f32) -> f32 {
self.mul_add(1. - ratio, other * ratio)
}
}
pub(crate) trait TransformExt: Copy {
fn horizontally_looking_at(self, target: Vec3, up: Vec3) -> Transform;
fn lerp(self, other: Transform, ratio: f32) -> Transform;
}
impl TransformExt for Transform {
fn horizontally_looking_at(self, target: Vec3, up: Vec3) -> Transform {
let direction = target - self.translation;
let horizontal_direction = direction - up * direction.dot(up);
let look_target = self.translation + horizontal_direction;
self.looking_at(look_target, up)
}
fn lerp(self, other: Transform, ratio: f32) -> Transform {
let translation = self.translation.lerp(other.translation, ratio);
let rotation = self.rotation.slerp(other.rotation, ratio);
let scale = self.scale.lerp(other.scale, ratio);
Transform {
translation,
rotation,
scale,
}
}
}

View File

@ -1,11 +0,0 @@
pub mod relationships_insert_dependant_components;
pub use relationships_insert_dependant_components::*;
use bevy::prelude::*;
pub struct EcsRelationshipsPlugin;
impl Plugin for EcsRelationshipsPlugin {
fn build(&self, app: &mut App) {
app;
}
}

View File

@ -1,15 +0,0 @@
use bevy::prelude::*;
pub fn insert_dependant_component<
Dependant: Component,
Dependency: Component + std::default::Default,
>(
mut commands: Commands,
entities_without_depency: Query<(Entity, &Name), (With<Dependant>, Without<Dependency>)>,
) {
for (entity, name) in entities_without_depency.iter() {
let name = name.clone().to_string();
commands.entity(entity).insert(Dependency::default());
warn!("found an entity called {} with a {} component but without an {}, please check your assets", name.clone(), std::any::type_name::<Dependant>(), std::any::type_name::<Dependency>());
}
}

View File

@ -1,80 +0,0 @@
use bevy::prelude::*;
use crate::{
assets::GameAssets,
state::{GameState, InAppRunning},
};
use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag};
use bevy_rapier3d::prelude::Velocity;
use rand::Rng;
pub fn setup_game(
mut commands: Commands,
game_assets: Res<GameAssets>,
models: Res<Assets<bevy::gltf::Gltf>>,
mut next_game_state: ResMut<NextState<GameState>>,
) {
println!("setting up all stuff");
// here we actually spawn our game world/level
commands.spawn((
SceneBundle {
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
scene: models
.get(game_assets.world.id())
.expect("main level should have been loaded")
.scenes[0]
.clone(),
..default()
},
bevy::prelude::Name::from("world"),
GameWorldTag,
InAppRunning,
));
next_game_state.set(GameState::InGame)
}
pub fn spawn_test(
keycode: Res<Input<KeyCode>>,
mut commands: Commands,
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
) {
if keycode.just_pressed(KeyCode::T) {
let world = game_world.single_mut();
let world = world.1[0];
let mut rng = rand::thread_rng();
let range = 5.5;
let x: f32 = rng.gen_range(-range..range);
let y: f32 = rng.gen_range(-range..range);
let mut rng = rand::thread_rng();
let range = 0.8;
let vel_x: f32 = rng.gen_range(-range..range);
let vel_y: f32 = rng.gen_range(2.0..2.5);
let vel_z: f32 = rng.gen_range(-range..range);
let name_index: u64 = rng.gen();
let new_entity = commands
.spawn((
BluePrintBundle {
blueprint: BlueprintName("Health_Pickup".to_string()),
..Default::default()
},
bevy::prelude::Name::from(format!("test{}", name_index)),
// BlueprintName("Health_Pickup".to_string()),
// SpawnHere,
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
Velocity {
linvel: Vec3::new(vel_x, vel_y, vel_z),
angvel: Vec3::new(0.0, 0.0, 0.0),
},
))
.id();
commands.entity(world).add_child(new_entity);
}
}

View File

@ -1,113 +0,0 @@
use bevy::prelude::*;
use crate::state::{AppState, GameState, InMainMenu};
pub fn setup_main_menu(mut commands: Commands) {
commands.spawn((Camera2dBundle::default(), InMainMenu));
commands.spawn((
TextBundle::from_section(
"SOME GAME TITLE !!",
TextStyle {
//font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 18.0,
color: Color::WHITE,
..Default::default()
},
)
.with_style(Style {
position_type: PositionType::Absolute,
top: Val::Px(100.0),
left: Val::Px(200.0),
..default()
}),
InMainMenu,
));
commands.spawn((
TextBundle::from_section(
"New Game (press Enter to start, press T once the game is started for demo spawning)",
TextStyle {
//font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 18.0,
color: Color::WHITE,
..Default::default()
},
)
.with_style(Style {
position_type: PositionType::Absolute,
top: Val::Px(200.0),
left: Val::Px(200.0),
..default()
}),
InMainMenu,
));
/*
commands.spawn((
TextBundle::from_section(
"Load Game",
TextStyle {
//font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 18.0,
color: Color::WHITE,
..Default::default()
},
)
.with_style(Style {
position_type: PositionType::Absolute,
top: Val::Px(250.0),
left: Val::Px(200.0),
..default()
}),
InMainMenu
));
commands.spawn((
TextBundle::from_section(
"Exit Game",
TextStyle {
//font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 18.0,
color: Color::WHITE,
..Default::default()
},
)
.with_style(Style {
position_type: PositionType::Absolute,
top: Val::Px(300.0),
left: Val::Px(200.0),
..default()
}),
InMainMenu
));*/
}
pub fn teardown_main_menu(bla: Query<Entity, With<InMainMenu>>, mut commands: Commands) {
for bli in bla.iter() {
commands.entity(bli).despawn_recursive();
}
}
pub fn main_menu(
keycode: Res<Input<KeyCode>>,
mut next_app_state: ResMut<NextState<AppState>>,
// mut next_game_state: ResMut<NextState<GameState>>,
// mut save_requested_events: EventWriter<SaveRequest>,
// mut load_requested_events: EventWriter<LoadRequest>,
) {
if keycode.just_pressed(KeyCode::Return) {
next_app_state.set(AppState::AppLoading);
// next_game_state.set(GameState::None);
}
if keycode.just_pressed(KeyCode::L) {
next_app_state.set(AppState::AppLoading);
// load_requested_events.send(LoadRequest { path: "toto".into() })
}
if keycode.just_pressed(KeyCode::S) {
// save_requested_events.send(SaveRequest { path: "toto".into() })
}
}

View File

@ -1,115 +0,0 @@
pub mod in_game;
pub use in_game::*;
pub mod in_main_menu;
pub use in_main_menu::*;
pub mod picking;
pub use picking::*;
use crate::{
insert_dependant_component,
state::{AppState, GameState},
};
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;
// this file is just for demo purposes, contains various types of components, systems etc
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub enum SoundMaterial {
Metal,
Wood,
Rock,
Cloth,
Squishy,
#[default]
None,
}
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo marker component
pub struct Player;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo component showing auto injection of components
pub struct ShouldBeWithPlayer;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// Demo marker component
pub struct Interactible;
fn player_move_demo(
keycode: Res<Input<KeyCode>>,
mut players: Query<&mut Transform, With<Player>>,
) {
let speed = 0.2;
if let Ok(mut player) = players.get_single_mut() {
if keycode.pressed(KeyCode::Left) {
player.translation.x += speed;
}
if keycode.pressed(KeyCode::Right) {
player.translation.x -= speed;
}
if keycode.pressed(KeyCode::Up) {
player.translation.z += speed;
}
if keycode.pressed(KeyCode::Down) {
player.translation.z -= speed;
}
}
}
// collision tests/debug
pub fn test_collision_events(
mut collision_events: EventReader<CollisionEvent>,
mut contact_force_events: EventReader<ContactForceEvent>,
) {
for collision_event in collision_events.read() {
println!("collision");
match collision_event {
CollisionEvent::Started(_entity1, _entity2, _) => {
println!("collision started")
}
CollisionEvent::Stopped(_entity1, _entity2, _) => {
println!("collision ended")
}
}
}
for contact_force_event in contact_force_events.read() {
println!("Received contact force event: {:?}", contact_force_event);
}
}
pub struct GamePlugin;
impl Plugin for GamePlugin {
fn build(&self, app: &mut App) {
app.add_plugins(PickingPlugin)
.register_type::<Interactible>()
.register_type::<SoundMaterial>()
.register_type::<Player>()
// little helper utility, to automatically inject components that are dependant on an other component
// ie, here an Entity with a Player component should also always have a ShouldBeWithPlayer component
// you get a warning if you use this, as I consider this to be stop-gap solution (usually you should have either a bundle, or directly define all needed components)
.add_systems(
Update,
(
// insert_dependant_component::<Player, ShouldBeWithPlayer>,
player_move_demo, //.run_if(in_state(AppState::Running)),
// test_collision_events
spawn_test,
)
.run_if(in_state(GameState::InGame)),
)
.add_systems(OnEnter(AppState::MenuRunning), setup_main_menu)
.add_systems(OnExit(AppState::MenuRunning), teardown_main_menu)
.add_systems(Update, main_menu.run_if(in_state(AppState::MenuRunning)))
.add_systems(OnEnter(AppState::AppRunning), setup_game);
}
}

View File

@ -1,33 +0,0 @@
use bevy::prelude::*;
use bevy_editor_pls::prelude::*;
mod core;
use crate::core::*;
pub mod assets;
use assets::*;
pub mod state;
use state::*;
mod game;
use game::*;
mod test_components;
use test_components::*;
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(AssetPlugin::default()),
// editor
EditorPlugin::default(),
// our custom plugins
StatePlugin,
AssetsPlugin,
CorePlugin, // reusable plugins
GamePlugin, // specific to our game
ComponentsTestPlugin, // Showcases different type of components /structs
))
.run();
}

View File

@ -1,54 +0,0 @@
use bevy::app::AppExit;
use bevy::prelude::*;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)]
pub enum AppState {
#[default]
CoreLoading,
MenuRunning,
AppLoading,
AppRunning,
AppEnding,
// FIXME: not sure
LoadingGame,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)]
pub enum GameState {
#[default]
None,
InMenu,
InGame,
InGameOver,
InSaving,
InLoading,
}
// tag components for all entities within a certain state (for despawning them if needed) , FIXME: seems kinda hack-ish
#[derive(Component)]
pub struct InCoreLoading;
#[derive(Component, Default)]
pub struct InMenuRunning;
#[derive(Component)]
pub struct InAppLoading;
#[derive(Component)]
pub struct InAppRunning;
// components for tagging in game vs in game menu stuff
#[derive(Component, Default)]
pub struct InMainMenu;
#[derive(Component, Default)]
pub struct InMenu;
#[derive(Component, Default)]
pub struct InGame;
pub struct StatePlugin;
impl Plugin for StatePlugin {
fn build(&self, app: &mut App) {
app.add_state::<AppState>().add_state::<GameState>();
}
}

Some files were not shown because too many files have changed in this diff Show More