feat(bevy_gltf_blueprints): added ability to generate blueprint/scene aabbs automatically (#57)

* settable via a config flag & systems won't run if the flag is not set
 * caches aabbs per blueprint name so they do not need to be recomputed multiple time for the same blueprint
 * closes #56
This commit is contained in:
Mark Moissette 2023-11-25 02:35:48 +01:00 committed by GitHub
parent 8e67f76d28
commit 9a765d5e12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 94 additions and 13 deletions

2
Cargo.lock generated
View File

@ -755,7 +755,7 @@ dependencies = [
[[package]] [[package]]
name = "bevy_gltf_blueprints" name = "bevy_gltf_blueprints"
version = "0.3.1" version = "0.3.2"
dependencies = [ dependencies = [
"bevy", "bevy",
"bevy_gltf_components 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "bevy_gltf_components 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bevy_gltf_blueprints" name = "bevy_gltf_blueprints"
version = "0.3.1" version = "0.3.2"
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

@ -102,6 +102,8 @@ fn main() {
BlueprintsPlugin{ BlueprintsPlugin{
library_folder: "advanced/models/library".into() // replace this with your blueprints library path , relative to the assets folder, library_folder: "advanced/models/library".into() // replace this with your blueprints library path , relative to the assets folder,
format: GltfFormat::GLB,// optional, use either format: GltfFormat::GLB, or format: GltfFormat::GLTF, or ..Default::default() if you want to keep the default .glb extension, this sets what extensions/ gltf files will be looked for by the library format: GltfFormat::GLB,// optional, use either format: GltfFormat::GLB, or format: GltfFormat::GLTF, or ..Default::default() if you want to keep the default .glb extension, this sets what extensions/ gltf files will be looked for by the library
aabbs: true, // defaults to false, enable this to automatically calculate aabb for the scene/blueprint
..Default::default()
} }
) )
.run(); .run();

View File

@ -0,0 +1,61 @@
use bevy::{math::Vec3A, prelude::*, render::primitives::Aabb};
use crate::{BluePrintsConfig, BlueprintName, SpawnedRoot};
/// helper system that computes the compound aabbs of the scenes/blueprints
pub fn compute_scene_aabbs(
root_entities: Query<
(Entity, &Name, &Children, &BlueprintName),
(With<SpawnedRoot>, Without<Aabb>),
>,
children: Query<&Children>,
existing_aabbs: Query<&Aabb>,
mut blueprints_config: ResMut<BluePrintsConfig>,
mut commands: Commands,
) {
// compute compound aabb
for root_entity in root_entities.iter() {
let name = &root_entity.3 .0;
let root_entity = root_entity.2.first().unwrap();
// only recompute aabb if it has not already been done before
if !blueprints_config.aabb_cache.contains_key(&name.to_string()) {
let aabb = compute_descendant_aabb(*root_entity, &children, &existing_aabbs);
commands.entity(*root_entity).insert(aabb);
blueprints_config.aabb_cache.insert(name.to_string(), aabb);
}
}
}
pub fn compute_descendant_aabb(
root_entity: Entity,
children: &Query<&Children>,
existing_aabbs: &Query<&Aabb>,
) -> Aabb {
if let Ok(children_list) = children.get(root_entity) {
let mut chilren_aabbs: Vec<Aabb> = vec![];
for child in children_list.iter() {
if let Ok(aabb) = existing_aabbs.get(*child) {
chilren_aabbs.push(*aabb);
} else {
let aabb = compute_descendant_aabb(*child, children, &existing_aabbs);
chilren_aabbs.push(aabb);
}
}
let mut min = Vec3A::splat(f32::MAX);
let mut max = Vec3A::splat(f32::MIN);
for aabb in chilren_aabbs.iter() {
min = min.min(aabb.min());
max = max.max(aabb.max());
}
let aabb = Aabb::from_min_max(Vec3::from(min), Vec3::from(max));
return aabb;
}
return Aabb::default();
}

View File

@ -7,13 +7,16 @@ pub(crate) use spawn_post_process::*;
pub mod animation; pub mod animation;
pub use animation::*; pub use animation::*;
pub mod aabb;
pub use aabb::*;
pub mod clone_entity; pub mod clone_entity;
pub use clone_entity::*; pub use clone_entity::*;
use core::fmt; use core::fmt;
use std::path::PathBuf; use std::path::PathBuf;
use bevy::prelude::*; use bevy::{prelude::*, render::primitives::Aabb, utils::HashMap};
use bevy_gltf_components::{ComponentsFromGltfPlugin, GltfComponentsSet}; use bevy_gltf_components::{ComponentsFromGltfPlugin, GltfComponentsSet};
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] #[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
@ -40,37 +43,41 @@ impl Default for BluePrintBundle {
} }
#[derive(Clone, Resource)] #[derive(Clone, Resource)]
pub(crate) struct BluePrintsConfig { pub struct BluePrintsConfig {
pub(crate) format: GltfFormat, pub(crate) format: GltfFormat,
pub(crate) library_folder: PathBuf, pub(crate) library_folder: PathBuf,
pub(crate) aabbs: bool,
pub(crate) aabb_cache: HashMap<String, Aabb>, // cache for aabbs
} }
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
pub enum GltfFormat { pub enum GltfFormat {
#[default] #[default]
GLB, GLB,
GLTF GLTF,
} }
impl fmt::Display for GltfFormat { impl fmt::Display for GltfFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
GltfFormat::GLB => { GltfFormat::GLB => {
write!(f, "glb", ) write!(f, "glb",)
} }
GltfFormat::GLTF => { GltfFormat::GLTF => {
write!(f, "gltf") write!(f, "gltf")
} }
} }
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
/// Plugin for gltf blueprints
pub struct BlueprintsPlugin { pub struct BlueprintsPlugin {
pub format: GltfFormat, pub format: GltfFormat,
/// The base folder where library/blueprints assets are loaded from, relative to the executable. /// The base folder where library/blueprints assets are loaded from, relative to the executable.
pub library_folder: PathBuf, pub library_folder: PathBuf,
pub aabbs: bool,
} }
impl Default for BlueprintsPlugin { impl Default for BlueprintsPlugin {
@ -78,10 +85,15 @@ impl Default for BlueprintsPlugin {
Self { Self {
format: GltfFormat::GLB, format: GltfFormat::GLB,
library_folder: PathBuf::from("models/library"), library_folder: PathBuf::from("models/library"),
aabbs: false,
} }
} }
} }
fn aabbs_enabled(blueprints_config: Res<BluePrintsConfig>) -> bool {
blueprints_config.aabbs
}
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)
@ -91,6 +103,8 @@ impl Plugin for BlueprintsPlugin {
.insert_resource(BluePrintsConfig { .insert_resource(BluePrintsConfig {
format: self.format.clone(), format: self.format.clone(),
library_folder: self.library_folder.clone(), library_folder: self.library_folder.clone(),
aabbs: self.aabbs,
aabb_cache: HashMap::new(),
}) })
.configure_sets( .configure_sets(
Update, Update,
@ -100,21 +114,23 @@ impl Plugin for BlueprintsPlugin {
) )
.add_systems( .add_systems(
Update, Update,
(spawn_from_blueprints) (
// .run_if(in_state(AppState::AppRunning).or_else(in_state(AppState::LoadingGame))) // FIXME: how to replace this with a crate compatible version ? spawn_from_blueprints,
compute_scene_aabbs.run_if(aabbs_enabled),
apply_deferred.run_if(aabbs_enabled),
)
.chain()
.in_set(GltfBlueprintsSet::Spawn), .in_set(GltfBlueprintsSet::Spawn),
) )
.add_systems( .add_systems(
Update, Update,
( (
// spawn_entities,
update_spawned_root_first_child, update_spawned_root_first_child,
apply_deferred, apply_deferred,
cleanup_scene_instances, cleanup_scene_instances,
apply_deferred, apply_deferred,
) )
.chain() .chain()
// .run_if(in_state(AppState::LoadingGame).or_else(in_state(AppState::AppRunning))) // FIXME: how to replace this with a crate compatible version ?
.in_set(GltfBlueprintsSet::AfterSpawn), .in_set(GltfBlueprintsSet::AfterSpawn),
); );
} }

View File

@ -82,9 +82,10 @@ pub(crate) fn spawn_from_blueprints(
transform: transform.clone(), transform: transform.clone(),
..Default::default() ..Default::default()
}, },
bevy::prelude::Name::from(["scene_wrapper", &name.clone()].join("_")), bevy::prelude::Name::from(["scene_wrapper", &name.clone()].join("_")), //TODO: remove this convoluted bit
// Parent(world) // FIXME/ would be good if this worked directly // Parent(world) // FIXME/ would be good if this worked directly
SpawnedRoot, SpawnedRoot,
BlueprintName(blupeprint_name.0.clone()),
Original(entity), Original(entity),
Animations { Animations {
named_animations: gltf.named_animations.clone(), named_animations: gltf.named_animations.clone(),

View File

@ -27,6 +27,7 @@ impl Plugin for CorePlugin {
BlueprintsPlugin { BlueprintsPlugin {
library_folder: "models/library".into(), library_folder: "models/library".into(),
format: GltfFormat::GLB, format: GltfFormat::GLB,
aabbs: true,
..Default::default() ..Default::default()
}, },
)); ));