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]]
name = "bevy_gltf_blueprints"
version = "0.3.1"
version = "0.3.2"
dependencies = [
"bevy",
"bevy_gltf_components 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -1,6 +1,6 @@
[package]
name = "bevy_gltf_blueprints"
version = "0.3.1"
version = "0.3.2"
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."
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"

View File

@ -102,6 +102,8 @@ fn main() {
BlueprintsPlugin{
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
aabbs: true, // defaults to false, enable this to automatically calculate aabb for the scene/blueprint
..Default::default()
}
)
.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 use animation::*;
pub mod aabb;
pub use aabb::*;
pub mod clone_entity;
pub use clone_entity::*;
use core::fmt;
use std::path::PathBuf;
use bevy::prelude::*;
use bevy::{prelude::*, render::primitives::Aabb, utils::HashMap};
use bevy_gltf_components::{ComponentsFromGltfPlugin, GltfComponentsSet};
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
@ -40,16 +43,19 @@ impl Default for BluePrintBundle {
}
#[derive(Clone, Resource)]
pub(crate) struct BluePrintsConfig {
pub struct BluePrintsConfig {
pub(crate) format: GltfFormat,
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)]
pub enum GltfFormat {
#[default]
GLB,
GLTF
GLTF,
}
impl fmt::Display for GltfFormat {
@ -65,12 +71,13 @@ impl fmt::Display for GltfFormat {
}
}
#[derive(Debug, Clone)]
/// Plugin for gltf blueprints
pub struct BlueprintsPlugin {
pub format: GltfFormat,
/// The base folder where library/blueprints assets are loaded from, relative to the executable.
pub library_folder: PathBuf,
pub aabbs: bool,
}
impl Default for BlueprintsPlugin {
@ -78,10 +85,15 @@ impl Default for BlueprintsPlugin {
Self {
format: GltfFormat::GLB,
library_folder: PathBuf::from("models/library"),
aabbs: false,
}
}
}
fn aabbs_enabled(blueprints_config: Res<BluePrintsConfig>) -> bool {
blueprints_config.aabbs
}
impl Plugin for BlueprintsPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(ComponentsFromGltfPlugin)
@ -91,6 +103,8 @@ impl Plugin for BlueprintsPlugin {
.insert_resource(BluePrintsConfig {
format: self.format.clone(),
library_folder: self.library_folder.clone(),
aabbs: self.aabbs,
aabb_cache: HashMap::new(),
})
.configure_sets(
Update,
@ -100,21 +114,23 @@ impl Plugin for BlueprintsPlugin {
)
.add_systems(
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),
)
.add_systems(
Update,
(
// spawn_entities,
update_spawned_root_first_child,
apply_deferred,
cleanup_scene_instances,
apply_deferred,
)
.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),
);
}

View File

@ -82,9 +82,10 @@ pub(crate) fn spawn_from_blueprints(
transform: transform.clone(),
..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
SpawnedRoot,
BlueprintName(blupeprint_name.0.clone()),
Original(entity),
Animations {
named_animations: gltf.named_animations.clone(),

View File

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