Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve collider spawning performance by not scaling #315

Merged
merged 11 commits into from
Jun 16, 2024
65 changes: 28 additions & 37 deletions src/level_instantiation/on_spawn/collider.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
use crate::{movement::physics::CollisionLayer, GameState};
use anyhow::Context;
use bevy::{prelude::*, transform::TransformSystem::TransformPropagate};
use bevy::prelude::*;
use bevy_mod_sysfail::prelude::*;
use bevy_xpbd_3d::prelude::{Collider as XpbdCollider, *};
use oxidized_navigation::NavMeshAffector;
use serde::{Deserialize, Serialize};
use std::iter;

#[derive(Debug, Clone, Eq, PartialEq, Component, Reflect, Serialize, Deserialize, Default)]
#[reflect(Component, Serialize, Deserialize)]
struct Collider;

pub(super) fn plugin(app: &mut App) {
app.register_type::<Collider>().add_systems(
Update,
spawn
.after(TransformPropagate)
.run_if(in_state(GameState::Playing)),
);
app.register_type::<Collider>()
.add_systems(Update, spawn.run_if(in_state(GameState::Playing)));
}

#[sysfail(Log<anyhow::Error, Error>)]
Expand All @@ -25,39 +22,33 @@ fn spawn(
mut commands: Commands,
children: Query<&Children>,
meshes: Res<Assets<Mesh>>,
mesh_handles: Query<&Handle<Mesh>, Without<RigidBody>>,
global_transforms: Query<&GlobalTransform>,
mesh_handles: Query<&Handle<Mesh>>,
) {
#[cfg(feature = "tracing")]
let _span = info_span!("read_colliders").entered();
for entity in collider_marker.iter() {
let mut all_children_loaded = true;
for child in children.iter_descendants(entity) {
if let Ok(mesh_handle) = mesh_handles.get(child) {
if let Some(mesh) = meshes.get(mesh_handle) {
let global_transform = global_transforms
.get(child)
.context("Failed to get global transform while reading collider")?
.compute_transform();
let scaled_mesh = mesh.clone().scaled_by(global_transform.scale);
let collider = XpbdCollider::trimesh_from_mesh(&scaled_mesh)
.context("Failed to create collider from mesh")?;
commands.entity(child).insert((
collider,
RigidBody::Static,
CollisionLayers::new(
[CollisionLayer::Terrain, CollisionLayer::CameraObstacle],
[CollisionLayer::Character],
),
NavMeshAffector,
));
} else {
all_children_loaded = false;
}
}
}
if all_children_loaded {
commands.entity(entity).remove::<Collider>();
for parent in collider_marker.iter() {
for child in iter::once(parent).chain(children.iter_descendants(parent)) {
let Ok(mesh_handle) = mesh_handles.get(child) else {
continue;
};
// Cannot fail: we already load all the meshes at startup.
let mesh = meshes.get(mesh_handle).unwrap();
let collider = XpbdCollider::trimesh_from_mesh(mesh)
.context("Failed to create collider from mesh")?;
commands.entity(child).insert((
collider,
CollisionLayers::new(
[CollisionLayer::Terrain, CollisionLayer::CameraObstacle],
[CollisionLayer::Character],
),
NavMeshAffector,
));
}
commands
.entity(parent)
.remove::<Collider>()
// If this were on the descendant, the collider would behave as if its local transform were its global transform
// ¯\_(ツ)_/¯
.insert(RigidBody::Static);
}
}