Skip to content

Commit

Permalink
progress multi world with change
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Tornetta <[email protected]>
  • Loading branch information
Vrixyz and AnthonyTornetta committed Jun 27, 2024
1 parent 24fd036 commit f900dac
Show file tree
Hide file tree
Showing 12 changed files with 335 additions and 100 deletions.
47 changes: 25 additions & 22 deletions bevy_rapier3d/examples/multi_world3.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,39 @@
use bevy::prelude::*;
use bevy::{input::common_conditions::input_just_pressed, prelude::*};
use bevy_rapier3d::prelude::*;

const N_WORLDS: usize = 2;

fn main() {
App::new()
.insert_resource(ClearColor(Color::rgb(
.insert_resource(ClearColor(Color::srgb(
0xF9 as f32 / 255.0,
0xF9 as f32 / 255.0,
0xFF as f32 / 255.0,
)))
.add_plugins((
DefaultPlugins,
RapierPhysicsPlugin::<NoUserData>::default().with_default_world(None),
RapierPhysicsPlugin::<NoUserData>::default()
.with_default_world(RapierContextInitialization::NoAutomaticRapierContext),
RapierDebugRenderPlugin::default(),
))
.add_systems(
Startup,
((create_worlds, setup_physics).chain(), setup_graphics),
)
.add_systems(Update, move_platforms)
// .add_systems(Update, change_world)
// .add_systems(Update, despawn_last)
.add_systems(
Update,
change_world.run_if(input_just_pressed(KeyCode::KeyC)),
)
.run();
}

fn create_worlds(mut commands: Commands) {
for i in 0..N_WORLDS {
commands.spawn((RapierContext::default(), WorldId(i)));
let mut world = commands.spawn((RapierContext::default(), WorldId(i)));
if i == 0 {
world.insert(DefaultRapierContext);
}
}
}

Expand All @@ -53,23 +59,20 @@ fn move_platforms(time: Res<Time>, mut query: Query<(&mut Transform, &Platform)>
}
}

/// Demonstrates despawning an entity removing it from its world
// fn despawn_last(query: Query<(&PhysicsWorld, Entity)>, mut commands: Commands) {
// for (bw, entity) in query.iter() {
// if bw.world_id == N_WORLDS - 1 {
// commands.entity(entity).despawn_recursive();
// }
// }
// }

/// Demonstrates how easy it is to move one entity to another world.
// fn change_world(mut query: Query<&mut PhysicsWorld>) {
// for mut bw in query.iter_mut() {
// if bw.world_id == 1 {
// bw.world_id = 0;
// }
// }
// }
fn change_world(
query_context: Query<Entity, With<DefaultRapierContext>>,
mut query_links: Query<(Entity, &mut RapierContextEntityLink)>,
) {
let default_context = query_context.single();
for (e, mut link) in query_links.iter_mut() {
if link.0 == default_context {
continue;
}
link.0 = default_context;
println!("changing world of {} for world {}", e, link.0);
}
}

pub fn setup_physics(
context: Query<(Entity, &WorldId), With<RapierContext>>,
Expand Down
2 changes: 1 addition & 1 deletion src/geometry/collider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt;
#[cfg(all(feature = "dim3", feature = "async-collider"))]
use {crate::geometry::VHACDParameters, bevy::utils::HashMap};

use bevy::prelude::*;
use bevy::{prelude::*, reflect::impl_reflect};

use bevy::utils::HashSet;
use rapier::geometry::Shape;
Expand Down
9 changes: 6 additions & 3 deletions src/plugin/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use bevy::prelude::{Component, Resource};
use bevy::{
prelude::{Component, Resource},
reflect::Reflect,
};

use crate::math::{Real, Vect};

Expand Down Expand Up @@ -43,7 +46,7 @@ pub enum TimestepMode {
}

/// Difference between simulation and rendering time
#[derive(Component, Default)]
#[derive(Component, Default, Reflect)]
pub struct SimulationToRenderTime {
/// Difference between simulation and rendering time
pub diff: f32,
Expand All @@ -59,7 +62,7 @@ impl Default for TimestepMode {
}
}

#[derive(Component, Copy, Clone, Debug)]
#[derive(Component, Copy, Clone, Debug, Reflect)]
/// A resource for specifying configuration information for the physics simulation
pub struct RapierConfiguration {
/// Specifying the gravity of the physics simulation.
Expand Down
9 changes: 9 additions & 0 deletions src/plugin/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ use crate::prelude::{CollisionGroups, RapierRigidBodyHandle};
use rapier::control::CharacterAutostep;
use rapier::geometry::DefaultBroadPhase;

/// Marker component for to access the default [`RapierContext`].
///
/// This is used by [`systemparams::DefaultRapierContextAccess`] and other default accesses
/// to help with getting a reference to the correct RapierContext.
///
/// If you're making a library, you might be interested in [`RapierContextEntityLink`]
/// and leverage a [`Query<&RapierContext>`] to find the correct [`RapierContext`] of an entity.
#[derive(Component, Reflect, Debug, Clone, Copy)]
pub struct DefaultRapierContext;

Expand All @@ -40,6 +47,7 @@ pub struct RapierContext {
/// The island manager, which detects what object is sleeping
/// (not moving much) to reduce computations.
pub islands: IslandManager,
// FIXME: This should be serialized but a bug prevents it
/// The broad-phase, which detects potential contact pairs.
pub broad_phase: DefaultBroadPhase,
/// The narrow-phase, which computes contact points, tests intersections,
Expand Down Expand Up @@ -81,6 +89,7 @@ pub struct RapierContext {
#[cfg_attr(feature = "serde-serialize", serde(skip))]
pub(crate) deleted_colliders: HashMap<ColliderHandle, Entity>,

#[cfg_attr(feature = "serde-serialize", serde(skip))]
pub(crate) collision_events_to_send: Vec<CollisionEvent>,
#[cfg_attr(feature = "serde-serialize", serde(skip))]
pub(crate) contact_force_events_to_send: Vec<ContactForceEvent>,
Expand Down
7 changes: 5 additions & 2 deletions src/plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ pub use self::context::{
DefaultRapierContextAccess, DefaultRapierContextAccessMut, RapierContextAccess,
RapierContextAccessMut,
},
RapierContext, RapierContextEntityLink,
DefaultRapierContext, RapierContext, RapierContextEntityLink,
};
pub use self::plugin::{
NoUserData, PhysicsSet, RapierContextInitialization, RapierPhysicsPlugin,
RapierTransformPropagateSet,
};
pub use self::plugin::{NoUserData, PhysicsSet, RapierPhysicsPlugin, RapierTransformPropagateSet};

#[allow(clippy::type_complexity)]
#[allow(clippy::too_many_arguments)]
Expand Down
100 changes: 79 additions & 21 deletions src/plugin/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use bevy::ecs::{
system::SystemParamItem,
};
use bevy::{prelude::*, transform::TransformSystem};
use rapier::dynamics::IntegrationParameters;
use std::marker::PhantomData;

use super::context::DefaultRapierContext;
Expand All @@ -22,8 +23,10 @@ pub type NoUserData = ();
pub struct RapierPhysicsPlugin<PhysicsHooks = ()> {
schedule: Interned<dyn ScheduleLabel>,
default_system_setup: bool,
/// Read in PreStartup, this will create a default world.
pub default_world_setup: Option<RapierContextInitialization>,
/// Read during [`RapierPhysicsPlugin::build()`],
/// to help initializing [`RapierContextInitialization`] resource.
/// This will be ignored if that resource already exists.
default_world_setup: RapierContextInitialization,
_phantom: PhantomData<PhysicsHooks>,
}

Expand All @@ -39,19 +42,14 @@ where
/// likely always be 1.0 in 3D. In 2D, this is useful to specify a "pixels-per-meter"
/// conversion ratio.
pub fn with_length_unit(mut self, length_unit: f32) -> Self {
self.default_world_setup = Some(RapierContextInitialization {
self.default_world_setup = RapierContextInitialization::InitializeDefaultRapierContext {
length_unit: length_unit,
});
};
self
}
/// Specifies a default configuration for the default `RapierContext`
///
/// If [`None`], no world will be initialized, you are responsible of creating and maintaining
/// a [`RapierContext`] before creating any rapier entities (rigidbodies, colliders, joints),
/// and as long as any [`RapierContextEntityLink`] has a reference to its [`RapierContext`].
pub fn with_default_world(
mut self,
default_world_initialization: Option<RapierContextInitialization>,
default_world_initialization: RapierContextInitialization,
) -> Self {
self.default_world_setup = default_world_initialization;
self
Expand All @@ -73,9 +71,9 @@ where
pub fn pixels_per_meter(pixels_per_meter: f32) -> Self {
Self {
default_system_setup: true,
default_world_setup: Some(RapierContextInitialization {
default_world_setup: RapierContextInitialization::InitializeDefaultRapierContext {
length_unit: pixels_per_meter,
}),
},
..default()
}
}
Expand Down Expand Up @@ -106,14 +104,20 @@ where
)
.chain()
.in_set(RapierTransformPropagateSet),
systems::on_add_entity_with_parent,
systems::on_change_world,
apply_deferred,
systems::sync_removals,
apply_deferred,
#[cfg(all(feature = "dim3", feature = "async-collider"))]
systems::init_async_scene_colliders,
#[cfg(all(feature = "dim3", feature = "async-collider"))]
systems::init_async_colliders,
systems::init_rigid_bodies,
systems::init_colliders,
systems::init_joints,
systems::sync_removals,
apply_deferred,
//systems::sync_removals,
// Run this here so the following systems do not have a 1 frame delay.
apply_deferred,
systems::apply_scale,
Expand Down Expand Up @@ -146,7 +150,7 @@ impl<PhysicsHooksSystemParam> Default for RapierPhysicsPlugin<PhysicsHooksSystem
Self {
schedule: PostUpdate.intern(),
default_system_setup: true,
default_world_setup: Some(RapierContextInitialization { length_unit: 1f32 }),
default_world_setup: default(),
_phantom: PhantomData,
}
}
Expand Down Expand Up @@ -199,11 +203,25 @@ where
.register_type::<SolverGroups>()
.register_type::<ContactForceEventThreshold>()
.register_type::<ContactSkin>()
.register_type::<Group>();
.register_type::<Group>()
.register_type::<RapierContextEntityLink>()
.register_type::<ColliderDebugColor>()
.register_type::<RapierConfiguration>()
.register_type::<SimulationToRenderTime>()
.register_type::<DefaultRapierContext>()
.register_type::<RapierContextInitialization>()
.register_type::<ColliderDebugColor>();

app.insert_resource(Events::<CollisionEvent>::default())
.insert_resource(Events::<ContactForceEvent>::default())
.insert_resource(Events::<MassModifiedEvent>::default());
let default_world_init = app.world().get_resource::<RapierContextInitialization>();
if let Some(world_init) = default_world_init {
warn!("RapierPhysicsPlugin added with a default rapier context initialization but a `RapierContextInitialization` was already existing.\
the following resource will be used in priority: {:?}", world_init);
} else {
app.insert_resource(self.default_world_setup.clone());
}

app.add_systems(
self.schedule,
Expand All @@ -229,7 +247,18 @@ where
);

// These *must* be in the main schedule currently so that they do not miss events.
app.add_systems(PostUpdate, (systems::sync_removals,));
/*app.add_systems(
PostUpdate,
(
// Change any worlds needed before doing any calculations
systems::on_add_entity_with_parent,
systems::on_change_world,
// Make sure to remove any dead bodies after changing_worlds but before everything else
// to avoid it deleting something right after adding it
//systems::sync_removals,
)
.chain(),
);*/

app.add_systems(
self.schedule,
Expand All @@ -256,13 +285,42 @@ where
}
}

#[derive(Resource)]
pub struct RapierContextInitialization {
pub length_unit: f32,
/// Specifies a default configuration for the default `RapierContext`
///
/// If [`None`], no world will be initialized, you are responsible of creating and maintaining
/// a [`RapierContext`] before creating any rapier entities (rigidbodies, colliders, joints),
/// and as long as any [`RapierContextEntityLink`] has a reference to its [`RapierContext`].
#[derive(Resource, Debug, Reflect, Clone)]
pub enum RapierContextInitialization {
NoAutomaticRapierContext,
InitializeDefaultRapierContext { length_unit: f32 },
}

impl Default for RapierContextInitialization {
fn default() -> Self {
RapierContextInitialization::InitializeDefaultRapierContext { length_unit: 1f32 }
}
}

pub fn insert_default_world(mut commands: Commands) {
commands.spawn((RapierContext::default(), DefaultRapierContext));
pub fn insert_default_world(
mut commands: Commands,
initialization_data: Res<RapierContextInitialization>,
) {
match initialization_data.as_ref() {
RapierContextInitialization::NoAutomaticRapierContext => {}
RapierContextInitialization::InitializeDefaultRapierContext { length_unit } => {
commands.spawn((
RapierContext {
integration_parameters: IntegrationParameters {
length_unit: *length_unit,
..default()
},
..RapierContext::default()
},
DefaultRapierContext,
));
}
}
}

pub fn setup_rapier_configuration(
Expand Down
1 change: 1 addition & 0 deletions src/plugin/systems/collider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ pub fn init_colliders(
{
let context_entity = context_link.map_or_else(
|| {
dbg!("unknown rapierContext for object, setting to default");
let context_entity = context.iter().next().unwrap().0;
commands
.entity(entity)
Expand Down
2 changes: 2 additions & 0 deletions src/plugin/systems/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
mod character_controller;
mod collider;
mod joint;
mod multiple_rapier_contexts;
mod remove;
mod rigid_body;
mod writeback;

pub use character_controller::*;
pub use collider::*;
pub use joint::*;
pub use multiple_rapier_contexts::*;
pub use remove::*;
pub use rigid_body::*;
pub use writeback::*;
Expand Down
Loading

0 comments on commit f900dac

Please sign in to comment.