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

Allow interpolation of scales #512

Merged
merged 12 commits into from
Sep 26, 2024
72 changes: 59 additions & 13 deletions src/collision/collider/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use std::marker::PhantomData;

use crate::{broad_phase::BroadPhaseSet, prelude::*, prepare::PrepareSet};
use crate::{broad_phase::BroadPhaseSet, prelude::*, prepare::PrepareSet, sync::SyncConfig};
#[cfg(all(feature = "bevy_scene", feature = "default-collider"))]
use bevy::scene::SceneInstance;
use bevy::{
Expand Down Expand Up @@ -95,9 +95,53 @@ impl<C: ScalableCollider> Plugin for ColliderBackendPlugin<C> {

// Initialize missing components for colliders.
hooks.on_add(|mut world, entity, _| {
let entity_ref = world.entity(entity);
let existing_global_transform = world
.entity(entity)
.get::<GlobalTransform>()
.copied()
.unwrap_or_default();
let global_transform = if existing_global_transform != GlobalTransform::IDENTITY {
// This collider was built deferred, probably via `ColliderConstructor`.
existing_global_transform
} else {
// This collider *may* have been `spawn`ed directly on a new entity.
// As such, its global transform is not yet available.
// You may notice that this will fail if the hierarchy's scale was updated in this
// frame. Remember that `GlobalTransform` is not updated inbetween fixed updates.
Jondolf marked this conversation as resolved.
Show resolved Hide resolved
// But this is fine, as `update_collider_scale` will be updated in the next fixed update anyways.
Jondolf marked this conversation as resolved.
Show resolved Hide resolved
// The reason why we care about initializing this scale here is for those users that opted out of
// `update_collider_scale` in order to do their own interpolation, which implies that they won't touch
// the `Transform` component before the collider is initialized, which in turn means that it will
// always be initialized with the correct `GlobalTransform`.
let parent_global_transform = world
.entity(entity)
.get::<Parent>()
.and_then(|parent| world.entity(parent.get()).get::<GlobalTransform>().copied())
.unwrap_or_default();
let transform = world
.entity(entity)
.get::<Transform>()
.copied()
.unwrap_or_default();
parent_global_transform * transform
};

let scale = global_transform.compute_transform().scale;
#[cfg(feature = "2d")]
let scale = scale.xy();

// Make sure the is initialized with the correct scale.
Jondolf marked this conversation as resolved.
Show resolved Hide resolved
// This overwrites the scale set by the constructor, but that one is
// meant to be only changed after initialization.
world
.entity_mut(entity)
.get_mut::<C>()
.unwrap()
.set_scale(scale.adjust_precision(), 10);

let entity_ref = world.entity(entity);
let collider = entity_ref.get::<C>().unwrap();

let aabb = entity_ref
.get::<ColliderAabb>()
.copied()
Expand Down Expand Up @@ -612,20 +656,22 @@ pub fn update_collider_scale<C: ScalableCollider>(
// Child colliders
Query<(&ColliderTransform, &mut C), (With<Parent>, Changed<ColliderTransform>)>,
)>,
sync_config: Res<SyncConfig>,
) {
// Update collider scale for root bodies
for (transform, mut collider) in &mut colliders.p0() {
#[cfg(feature = "2d")]
let scale = transform.scale.truncate().adjust_precision();
#[cfg(feature = "3d")]
let scale = transform.scale.adjust_precision();
if scale != collider.scale() {
// TODO: Support configurable subdivision count for shapes that
// can't be represented without approximations after scaling.
collider.set_scale(scale, 10);
if sync_config.transform_to_collider_scale {
// Update collider scale for root bodies
for (transform, mut collider) in &mut colliders.p0() {
#[cfg(feature = "2d")]
let scale = transform.scale.truncate().adjust_precision();
#[cfg(feature = "3d")]
let scale = transform.scale.adjust_precision();
if scale != collider.scale() {
// TODO: Support configurable subdivision count for shapes that
// can't be represented without approximations after scaling.
collider.set_scale(scale, 10);
}
}
}

// Update collider scale for child colliders
for (collider_transform, mut collider) in &mut colliders.p1() {
if collider_transform.scale != collider.scale() {
Expand Down
4 changes: 3 additions & 1 deletion src/prepare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#![allow(clippy::type_complexity)]

use crate::prelude::*;
use crate::{prelude::*, sync::SyncConfig};
use bevy::{
ecs::{
intern::Interned,
Expand Down Expand Up @@ -77,6 +77,8 @@ pub enum PrepareSet {

impl Plugin for PreparePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<SyncConfig>()
.register_type::<SyncConfig>();
app.configure_sets(
self.schedule,
(
Expand Down
14 changes: 12 additions & 2 deletions src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,22 +143,32 @@ impl Plugin for SyncPlugin {
}
}

/// Configures what physics data is synchronized by the [`SyncPlugin`] and how.
/// Configures what physics data is synchronized by the [`SyncPlugin`] and [`PreparePlugin`] and how.
#[derive(Resource, Reflect, Clone, Debug, PartialEq, Eq)]
#[reflect(Resource)]
pub struct SyncConfig {
/// Updates transforms based on [`Position`] and [`Rotation`] changes. Defaults to true.
///
/// This operation is run in [`SyncSet::PositionToTransform`].
pub position_to_transform: bool,
/// Updates [`Position`] and [`Rotation`] based on transform changes,
/// allowing you to move bodies using `Transform`. Defaults to true.
/// allowing you to move bodies using [`Transform`]. Defaults to true.
///
/// This operation is run in [`SyncSet::TransformToPosition`].
pub transform_to_position: bool,
/// Updates [`Collider::scale()`] based on transform changes,
/// allowing you to scale colliders using [`Transform`]. Defaults to true.
///
/// This operation is run in [`PrepareSet::Finalize`]
pub transform_to_collider_scale: bool,
}

impl Default for SyncConfig {
fn default() -> Self {
SyncConfig {
position_to_transform: true,
transform_to_position: true,
transform_to_collider_scale: true,
}
}
}
Expand Down