diff --git a/Cargo.toml b/Cargo.toml index b1ad4fd4..f02c910c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ publish = false crate-type = ["cdylib"] [features] -default = ["single-dim2", "serde-serialize", "parallel", "simd-stable"] +default = ["single-dim3", "serde-serialize", "parallel", "simd-stable"] dim2 = [] dim3 = [] single = [] diff --git a/src/bodies/rapier_body.rs b/src/bodies/rapier_body.rs index de2955e5..67c028ec 100644 --- a/src/bodies/rapier_body.rs +++ b/src/bodies/rapier_body.rs @@ -7,7 +7,7 @@ use hashbrown::hash_set::HashSet; #[cfg(feature = "dim3")] use rapier::dynamics::LockedAxes; use rapier::geometry::ColliderHandle; -use rapier::math::Real; +use rapier::math::DEFAULT_EPSILON; use servers::rapier_physics_server_extra::PhysicsCollisionObjects; use servers::rapier_physics_server_extra::PhysicsShapes; use servers::rapier_physics_server_extra::PhysicsSpaces; @@ -111,8 +111,10 @@ pub struct RapierBody { #[cfg(feature = "dim3")] axis_lock: u8, mass: real, + inv_mass: real, mass_properties_update_pending: bool, inertia: Angle, + inv_inertia: Angle, #[cfg(feature = "dim3")] inv_inertia_tensor: Basis, contact_skin: real, @@ -164,8 +166,10 @@ impl RapierBody { #[cfg(feature = "dim3")] axis_lock: 0, mass: 1.0, + inv_mass: 1.0, mass_properties_update_pending: false, inertia: ANGLE_ZERO, + inv_inertia: ANGLE_ZERO, #[cfg(feature = "dim3")] inv_inertia_tensor: Basis::IDENTITY, contact_skin: RapierProjectSettings::get_contact_skin(), @@ -1156,6 +1160,11 @@ impl RapierBody { return; } self.mass = mass_value; + if self.mass.is_zero_approx() { + self.inv_mass = 0.0; + } else { + self.inv_mass = 1.0 / self.mass; + } if self.base.mode.ord() >= BodyMode::RIGID.ord() { self.mass_properties_changed(physics_engine, physics_spaces); } @@ -1195,10 +1204,18 @@ impl RapierBody { } } BodyParameter::CENTER_OF_MASS => { + #[cfg(feature = "dim2")] if p_value.get_type() != VariantType::VECTOR2 { + godot_error!("Invalid body data."); + return; + } + #[cfg(feature = "dim3")] + if p_value.get_type() != VariantType::VECTOR3 { + godot_error!("Invalid body data."); return; } self.center_of_mass = p_value.to(); + self.calculate_center_of_mass = false; if self.base.mode.ord() >= BodyMode::RIGID.ord() { self.mass_properties_changed(physics_engine, physics_spaces); } @@ -1548,107 +1565,74 @@ impl RapierBody { if self.base.mode.ord() < BodyMode::RIGID.ord() { return; } - let mut total_area = 0.0; - let shape_count = self.base.get_shape_count() as usize; - for i in 0..shape_count { - if self.base.is_shape_disabled(i) { - continue; + // compute rigidbody mass properties by changing collider mass. Will get overriden later + let rigid_body_mass_properties = physics_engine.body_get_mass_properties( + self.base.get_space_handle(), + self.base.get_body_handle(), + self.mass, + ); + if self.calculate_center_of_mass || self.calculate_inertia { + if self.calculate_center_of_mass { + self.center_of_mass = + vector_to_godot(rigid_body_mass_properties.local_mprops.local_com.coords); } - if let Some(shape) = physics_shapes.get(&self.base.get_shape(i)) { - total_area += shape.get_base().get_aabb_area(); + if self.calculate_inertia { + let angular_inertia = rigid_body_mass_properties.local_mprops.principal_inertia(); + self.inertia = angle_to_godot(angular_inertia); } } - if self.calculate_center_of_mass { - self.center_of_mass = Vector::default(); - if total_area != 0.0 { - for i in 0..shape_count { - if self.base.is_shape_disabled(i) { - continue; - } - if let Some(shape) = physics_shapes.get(&self.base.get_shape(i)) { - let shape_area = shape.get_base().get_aabb_area(); - if shape_area == 0.0 || self.mass == 0.0 { - continue; - } - let shape_mass = shape_area * self.mass / total_area; - // NOTE: we assume that the shape origin is also its center of mass. - self.center_of_mass += shape_mass * self.base.get_shape_transform(i).origin; - } - } - self.center_of_mass /= self.mass; - } + if self.inertia.is_zero_approx() { + self.inv_inertia = ANGLE_ZERO; } - if self.calculate_inertia { - self.inertia = ANGLE_ZERO; - if total_area != 0.0 { - for i in 0..shape_count { - if self.base.is_shape_disabled(i) { - continue; - } - if let Some(shape) = physics_shapes.get(&self.base.get_shape(i)) { - let shape_area = shape.get_base().get_aabb_area(); - if shape_area == 0.0 || self.mass == 0.0 { - continue; - } - let shape_mass = shape_area * self.mass / total_area; - let mtx = self.base.get_shape_transform(i); - let scale = transform_scale(&mtx); - self.inertia += self.get_inertia_for_shape( - shape.get_moment_of_inertia(shape_mass, scale), - shape_mass, - mtx, - i, - ); - } - } + #[cfg(feature = "dim2")] + if !self.inertia.is_zero_approx() { + self.inv_inertia = 1.0 / self.inertia; + } + #[cfg(feature = "dim3")] + if !self.inertia.is_zero_approx() { + // inv inertia + if !self.inv_inertia.x.is_zero_approx() { + self.inv_inertia.x = 1.0 / self.inertia.x; + } else { + self.inv_inertia.x = 0.0; } + if !self.inv_inertia.y.is_zero_approx() { + self.inv_inertia.y = 1.0 / self.inertia.y; + } else { + self.inv_inertia.y = 0.0; + } + if !self.inv_inertia.z.is_zero_approx() { + self.inv_inertia.z = 1.0 / self.inertia.z; + } else { + self.inv_inertia.z = 0.0; + } + // inv inertia tensor + let rotation_matrix = rigid_body_mass_properties + .local_mprops + .principal_inertia_local_frame + .to_rotation_matrix(); + let vector = rotation_matrix.matrix(); + let column_0 = vector + .column(0) + .pseudo_inverse(DEFAULT_EPSILON) + .unwrap_or_default(); + let column_1 = vector + .column(1) + .pseudo_inverse(DEFAULT_EPSILON) + .unwrap_or_default(); + let column_2 = vector + .column(2) + .pseudo_inverse(DEFAULT_EPSILON) + .unwrap_or_default(); + self.inv_inertia_tensor = Basis::from_cols( + Vector3::new(column_0.x, column_0.y, column_0.z), + Vector3::new(column_1.x, column_1.y, column_1.z), + Vector3::new(column_2.x, column_2.y, column_2.z), + ); } self.apply_mass_properties(force_update, physics_engine); } - #[cfg(feature = "dim2")] - fn get_inertia_for_shape( - &self, - moment_of_inertia: Angle, - shape_mass: Real, - shape_transform: Transform, - _i: usize, - ) -> Real { - let mtx = shape_transform; - let _scale = transform_scale(&mtx); - let shape_origin = mtx.origin - self.center_of_mass; - moment_of_inertia + shape_mass * shape_origin.length_squared() - } - - #[cfg(feature = "dim3")] - fn get_inertia_for_shape( - &self, - moment_of_inertia: Angle, - shape_mass: Real, - shape_transform: Transform, - _i: usize, - ) -> Vector3 { - let mut shape_inertia_tensor = Basis::from_scale(moment_of_inertia); - let shape_transform = shape_transform; - let shape_basis = shape_transform.basis.orthonormalized(); - // NOTE: we don't take the scale of collision shapes into account when computing the inertia tensor! - shape_inertia_tensor = shape_basis * shape_inertia_tensor * shape_basis.transposed(); - let shape_origin = shape_transform.origin - self.center_of_mass; - let shape_outer = shape_origin.outer(shape_origin); - let mut shape_dot = Basis::IDENTITY * (shape_origin.dot(shape_origin)); - shape_dot.set_col_a(shape_dot.col_a() - shape_outer.col_a()); - shape_dot.set_col_b(shape_dot.col_b() - shape_outer.col_b()); - shape_dot.set_col_c(shape_dot.col_c() - shape_outer.col_c()); - shape_dot *= shape_mass; - let mut inertia_tensor = shape_inertia_tensor; - inertia_tensor.set_col_a(inertia_tensor.col_a() + shape_dot.col_a()); - inertia_tensor.set_col_b(inertia_tensor.col_b() + shape_dot.col_b()); - inertia_tensor.set_col_c(inertia_tensor.col_c() + shape_dot.col_c()); - //return inertia_tensor.diagonalize().transposed(); - // TODO - moment_of_inertia - } - pub fn reset_mass_properties( &mut self, physics_engine: &mut PhysicsEngine, @@ -1668,30 +1652,11 @@ impl RapierBody { } pub fn get_inv_mass(&self) -> real { - if self.mass != 0.0 { - return 1.0 / self.mass; - } - 0.0 + self.inv_mass } - #[cfg(feature = "dim2")] pub fn get_inv_inertia(&self) -> Angle { - if self.inertia != ANGLE_ZERO { - return 1.0 / self.inertia; - } - ANGLE_ZERO - } - - #[cfg(feature = "dim3")] - pub fn get_inv_inertia(&self) -> Angle { - if self.inertia != ANGLE_ZERO { - return Vector3::new( - 1.0 / self.inertia.x, - 1.0 / self.inertia.y, - 1.0 / self.inertia.z, - ); - } - ANGLE_ZERO + self.inv_inertia } #[cfg(feature = "dim3")] diff --git a/src/bodies/rapier_direct_body_state_impl.rs b/src/bodies/rapier_direct_body_state_impl.rs index fc9ffa26..71684425 100644 --- a/src/bodies/rapier_direct_body_state_impl.rs +++ b/src/bodies/rapier_direct_body_state_impl.rs @@ -144,7 +144,15 @@ impl RapierDirectBodyStateImpl { #[cfg(feature = "dim3")] pub(super) fn get_inverse_inertia_tensor(&self) -> Basis { - // TODO + let Some(ref physics_singleton) = self.physics_singleton else { + return Basis::IDENTITY; + }; + let physics_data = &physics_singleton.bind().implementation.physics_data; + if let Some(body) = physics_data.collision_objects.get(&self.body) { + if let Some(body) = body.get_body() { + return body.get_inv_inertia_tensor(); + } + } Basis::IDENTITY } diff --git a/src/rapier_wrapper/body.rs b/src/rapier_wrapper/body.rs index f3c1b1ae..b5096bf7 100644 --- a/src/rapier_wrapper/body.rs +++ b/src/rapier_wrapper/body.rs @@ -1,5 +1,3 @@ -use godot::builtin::math::FloatExt; -use godot::log::godot_error; use nalgebra::Point; use rapier::prelude::*; @@ -50,7 +48,7 @@ impl PhysicsEngine { let default_activation = RigidBodyActivation::default(); activation.angular_threshold = default_activation.angular_threshold; activation.normalized_linear_threshold = default_activation.normalized_linear_threshold; - activation.time_until_sleep = 0.5; + //activation.time_until_sleep = 0.5; set_rigid_body_properties_internal(&mut rigid_body, pos, rot, true); rigid_body.user_data = user_data.get_data(); physics_world @@ -371,7 +369,7 @@ impl PhysicsEngine { activation.angular_threshold = default_activation.angular_threshold; activation.normalized_linear_threshold = default_activation.normalized_linear_threshold; - activation.time_until_sleep = 0.5; + //activation.time_until_sleep = 0.5; } if !can_sleep && body.is_sleeping() { body.wake_up(true); @@ -402,7 +400,7 @@ impl PhysicsEngine { body_handle: RigidBodyHandle, mass: Real, inertia: AngVector, - _local_com: Vector, + local_com: Vector, wake_up: bool, force_update: bool, ) { @@ -418,33 +416,19 @@ impl PhysicsEngine { } else { body.lock_rotations(false, wake_up); } - let colliders = body.colliders(); - let colliders_len_inv = 1.0 / (colliders.len() as f32); - for collider in colliders { + for collider in body.colliders() { if let Some(collider) = physics_world .physics_objects .collider_set .get_mut(*collider) { - // reuse local center - let mass_properties = collider.shape().mass_properties(1.0); - if mass_properties.mass().is_zero_approx() { - godot_error!("Collider has zero computed mass"); - } - let local_com = mass_properties.local_com; - //let shape_inertia = mass_properties.inv_principal_inertia_sqrt; - let _mass_properties = MassProperties::new( - local_com, - colliders_len_inv, - inertia, // * colliders_len_inv, - ); - //collider.set_mass_properties(mass_properties); - collider.set_mass(colliders_len_inv); - //collider.set_density(colliders_len_inv) + collider.set_density(0.0); } } - let _props = MassProperties::new(Point { coords: _local_com }, mass, inertia); - //body.set_additional_mass_properties(props, true); + body.set_additional_mass_properties( + MassProperties::new(Point { coords: local_com }, mass, inertia), + wake_up, + ); if force_update { body.recompute_mass_properties_from_colliders( &physics_world.physics_objects.collider_set, @@ -635,4 +619,33 @@ impl PhysicsEngine { body.sleep(); } } + + pub fn body_get_mass_properties( + &mut self, + world_handle: WorldHandle, + body_handle: RigidBodyHandle, + mass: Real, + ) -> RigidBodyMassProps { + if let Some(physics_world) = self.get_mut_world(world_handle) + && let Some(body) = physics_world + .physics_objects + .rigid_body_set + .get_mut(body_handle) + { + for collider in body.colliders() { + if let Some(collider) = physics_world + .physics_objects + .collider_set + .get_mut(*collider) + { + collider.set_mass(mass); + } + } + body.recompute_mass_properties_from_colliders( + &physics_world.physics_objects.collider_set, + ); + return body.mass_properties().clone(); + } + RigidBodyMassProps::default() + } } diff --git a/src/rapier_wrapper/collider.rs b/src/rapier_wrapper/collider.rs index c6b81112..1a5f87ca 100644 --- a/src/rapier_wrapper/collider.rs +++ b/src/rapier_wrapper/collider.rs @@ -137,6 +137,13 @@ pub fn scale_shape(shape: &SharedShape, shape_info: ShapeInfo) -> SharedShape { } } } + #[cfg(feature = "dim3")] + ShapeType::HeightField => { + if let Some(new_shape) = shape.as_heightfield() { + let new_shape = new_shape.clone().scaled(&scale); + return SharedShape::new(new_shape); + } + } ShapeType::Capsule => { if let Some(new_shape) = shape.as_capsule() { if let Some(new_shape) = new_shape.scaled(&scale, SUBDIVISIONS) { diff --git a/src/rapier_wrapper/shape.rs b/src/rapier_wrapper/shape.rs index 83caab58..a4d6c461 100644 --- a/src/rapier_wrapper/shape.rs +++ b/src/rapier_wrapper/shape.rs @@ -98,12 +98,39 @@ impl PhysicsEngine { self.insert_shape(shape) } - pub fn shape_create_concave_polyline(&mut self, points: &Vec>) -> ShapeHandle { + #[cfg(feature = "dim3")] + pub fn shape_create_heightmap( + &mut self, + heights: &[Real], + width: i32, + depth: i32, + ) -> ShapeHandle { + use nalgebra::Vector3; + let width = width as usize; + let depth = depth as usize; + let heights = DMatrix::from_fn(width, depth, |i, j| heights[j * (width) + i]); + let shape = + SharedShape::heightfield(heights, Vector3::new(depth as Real, 1.0, width as Real)); + self.insert_shape(shape) + } + + pub fn shape_create_concave_polyline( + &mut self, + points: &Vec>, + indices: Option>, + ) -> ShapeHandle { let points_vec = point_array_to_vec(points); - let shape = SharedShape::polyline(points_vec, None); + let shape = SharedShape::polyline(points_vec, indices); self.insert_shape(shape) } + pub fn shape_get_aabb(&self, handle: ShapeHandle) -> rapier::prelude::Aabb { + if let Some(shape) = self.get_shape(handle) { + return shape.compute_local_aabb(); + } + rapier::prelude::Aabb::new_invalid() + } + pub fn shape_destroy(&mut self, shape_handle: ShapeHandle) { self.remove_shape(shape_handle) } diff --git a/src/servers/rapier_physics_server_3d.rs b/src/servers/rapier_physics_server_3d.rs index 1db0de25..85f310fe 100644 --- a/src/servers/rapier_physics_server_3d.rs +++ b/src/servers/rapier_physics_server_3d.rs @@ -81,6 +81,14 @@ impl IPhysicsServer3DExtension for RapierPhysicsServer3D { self.implementation.shape_get_type(shape) } + fn shape_set_margin(&mut self, shape: Rid, margin: real) { + self.implementation.shape_set_margin(shape, margin) + } + + fn shape_get_margin(&self, shape: Rid) -> real { + self.implementation.shape_get_margin(shape) + } + fn shape_get_data(&self, shape: Rid) -> Variant { self.implementation.shape_get_data(shape) } diff --git a/src/servers/rapier_physics_server_impl.rs b/src/servers/rapier_physics_server_impl.rs index 57f179ff..b527c0e8 100644 --- a/src/servers/rapier_physics_server_impl.rs +++ b/src/servers/rapier_physics_server_impl.rs @@ -27,11 +27,12 @@ use crate::joints::rapier_pin_joint_3d::RapierPinJoint3D; use crate::rapier_wrapper::prelude::*; use crate::shapes::rapier_capsule_shape::RapierCapsuleShape; use crate::shapes::rapier_circle_shape::RapierCircleShape; -#[cfg(feature = "dim2")] -use crate::shapes::rapier_concave_polygon_shape_2d::RapierConcavePolygonShape2D; +use crate::shapes::rapier_concave_polygon_shape::RapierConcavePolygonShape; use crate::shapes::rapier_convex_polygon_shape::RapierConvexPolygonShape; #[cfg(feature = "dim3")] use crate::shapes::rapier_cylinder_shape_3d::RapierCylinderShape3D; +#[cfg(feature = "dim3")] +use crate::shapes::rapier_heightmap_shape_3d::RapierHeightMapShape3D; use crate::shapes::rapier_rectangle_shape::RapierRectangleShape; #[cfg(feature = "dim2")] use crate::shapes::rapier_segment_shape_2d::RapierSegmentShape2D; @@ -136,19 +137,25 @@ impl RapierPhysicsServerImpl { #[cfg(feature = "dim2")] pub(super) fn concave_polygon_shape_create(&mut self) -> Rid { let rid = rid_from_int64(rid_allocate_id()); - let shape = RapierConcavePolygonShape2D::new(rid); + let shape = RapierConcavePolygonShape::new(rid); self.physics_data.shapes.insert(rid, Box::new(shape)); rid } #[cfg(feature = "dim3")] pub(super) fn concave_polygon_shape_create(&mut self) -> Rid { - Rid::Invalid + let rid = rid_from_int64(rid_allocate_id()); + let shape = RapierConcavePolygonShape::new(rid); + self.physics_data.shapes.insert(rid, Box::new(shape)); + rid } #[cfg(feature = "dim3")] pub(super) fn heightmap_shape_create(&mut self) -> Rid { - Rid::Invalid + let rid = rid_from_int64(rid_allocate_id()); + let shape = RapierHeightMapShape3D::new(rid); + self.physics_data.shapes.insert(rid, Box::new(shape)); + rid } pub(super) fn shape_set_data(&mut self, shape: Rid, data: Variant) { @@ -171,6 +178,12 @@ impl RapierPhysicsServerImpl { ShapeType::CUSTOM } + pub(super) fn shape_set_margin(&mut self, _shape: Rid, _margin: real) {} + + pub(super) fn shape_get_margin(&self, _shape: Rid) -> real { + 0.0 + } + pub(super) fn shape_get_data(&self, shape: Rid) -> Variant { if let Some(shape) = self.physics_data.shapes.get(&shape) { if shape.get_base().is_valid() { diff --git a/src/shapes/mod.rs b/src/shapes/mod.rs index 6d899acd..909cb8b4 100644 --- a/src/shapes/mod.rs +++ b/src/shapes/mod.rs @@ -1,10 +1,11 @@ pub mod rapier_capsule_shape; pub mod rapier_circle_shape; -#[cfg(feature = "dim2")] -pub mod rapier_concave_polygon_shape_2d; +pub mod rapier_concave_polygon_shape; pub mod rapier_convex_polygon_shape; #[cfg(feature = "dim3")] pub mod rapier_cylinder_shape_3d; +#[cfg(feature = "dim3")] +pub mod rapier_heightmap_shape_3d; pub mod rapier_rectangle_shape; #[cfg(feature = "dim2")] pub mod rapier_segment_shape_2d; diff --git a/src/shapes/rapier_capsule_shape.rs b/src/shapes/rapier_capsule_shape.rs index 854ac36a..56d9c49e 100644 --- a/src/shapes/rapier_capsule_shape.rs +++ b/src/shapes/rapier_capsule_shape.rs @@ -6,7 +6,6 @@ use godot::prelude::*; use crate::rapier_wrapper::prelude::*; use crate::shapes::rapier_shape::*; -use crate::types::*; #[cfg_attr( feature = "serde-serialize", derive(serde::Serialize, serde::Deserialize) @@ -39,23 +38,6 @@ impl IRapierShape for RapierCapsuleShape { ShapeType::CAPSULE } - #[cfg(feature = "dim2")] - fn get_moment_of_inertia(&self, mass: f32, scale: Vector) -> f32 { - let he2 = Vector::new(self.radius * 2.0, self.height) * scale; - mass * he2.dot(he2) / 12.0 - } - - #[cfg(feature = "dim3")] - fn get_moment_of_inertia(&self, p_mass: f32, _scale: Vector) -> Vector3 { - // use bad AABB approximation - let extents = self.compute_aabb().size * 0.5; - Vector3::new( - (p_mass / 3.0) * (extents.y * extents.y + extents.z * extents.z), - (p_mass / 3.0) * (extents.x * extents.x + extents.z * extents.z), - (p_mass / 3.0) * (extents.x * extents.x + extents.y * extents.y), - ) - } - fn allows_one_way_collision(&self) -> bool { true } @@ -102,8 +84,7 @@ impl IRapierShape for RapierCapsuleShape { } } let handle = self.create_rapier_shape(physics_engine); - self.base - .set_handle(handle, self.compute_aabb(), physics_engine); + self.base.set_handle(handle, physics_engine); } fn get_data(&self) -> Variant { @@ -114,16 +95,3 @@ impl IRapierShape for RapierCapsuleShape { self.base.get_handle() } } -impl RapierCapsuleShape { - #[cfg(feature = "dim2")] - fn compute_aabb(&self) -> Rect2 { - let he = Vector2::new(self.radius, self.height * 0.5); - Rect2::new(-he, he * 2.0) - } - - #[cfg(feature = "dim3")] - fn compute_aabb(&self) -> Aabb { - let he = Vector3::new(self.radius, self.height * 0.5, self.radius); - Aabb::new(-he, he * 2.0) - } -} diff --git a/src/shapes/rapier_circle_shape.rs b/src/shapes/rapier_circle_shape.rs index b027cdb2..11231ac1 100644 --- a/src/shapes/rapier_circle_shape.rs +++ b/src/shapes/rapier_circle_shape.rs @@ -43,22 +43,6 @@ impl IRapierShape for RapierCircleShape { ShapeType::SPHERE } - #[cfg(feature = "dim2")] - fn get_moment_of_inertia(&self, mass: f32, scale: Vector) -> f32 { - let a = self.radius * scale.x; - let b = self.radius * scale.y; - mass * (a * a + b * b) / 4.0 - } - - #[cfg(feature = "dim3")] - fn get_moment_of_inertia(&self, mass: f32, scale: Vector) -> Angle { - let a = self.radius * scale.x; - let b = self.radius * scale.y; - let c = self.radius * scale.z; - let inertia = mass * (a * a + b * b + c * c) / 4.0; - Vector3::new(inertia, inertia, inertia) - } - fn allows_one_way_collision(&self) -> bool { true } @@ -78,11 +62,7 @@ impl IRapierShape for RapierCircleShape { } } let handle = self.create_rapier_shape(physics_engine); - let rect = Rect::new( - -Vector::splat(self.radius), - Vector::splat(self.radius) * 2.0, - ); - self.base.set_handle(handle, rect, physics_engine); + self.base.set_handle(handle, physics_engine); } fn get_data(&self) -> Variant { diff --git a/src/shapes/rapier_concave_polygon_shape.rs b/src/shapes/rapier_concave_polygon_shape.rs new file mode 100644 index 00000000..ed5cf19d --- /dev/null +++ b/src/shapes/rapier_concave_polygon_shape.rs @@ -0,0 +1,112 @@ +#[cfg(feature = "dim2")] +use godot::classes::physics_server_2d::*; +#[cfg(feature = "dim3")] +use godot::classes::physics_server_3d::*; +use godot::prelude::*; + +use crate::rapier_wrapper::prelude::*; +use crate::shapes::rapier_shape::*; +use crate::types::PackedVectorArray; +#[cfg_attr( + feature = "serde-serialize", + derive(serde::Serialize, serde::Deserialize) +)] +pub struct RapierConcavePolygonShape { + #[cfg_attr(feature = "serde-serialize", serde(skip))] + points: PackedVectorArray, + base: RapierShapeBase, +} +impl RapierConcavePolygonShape { + pub fn new(rid: Rid) -> Self { + Self { + points: PackedVectorArray::default(), + base: RapierShapeBase::new(rid), + } + } +} +#[cfg_attr(feature = "serde-serialize", typetag::serde)] +impl IRapierShape for RapierConcavePolygonShape { + fn get_base(&self) -> &RapierShapeBase { + &self.base + } + + fn get_mut_base(&mut self) -> &mut RapierShapeBase { + &mut self.base + } + + fn get_type(&self) -> ShapeType { + ShapeType::CONCAVE_POLYGON + } + + fn allows_one_way_collision(&self) -> bool { + true + } + + fn create_rapier_shape(&mut self, physics_engine: &mut PhysicsEngine) -> ShapeHandle { + let point_count = self.points.len(); + let mut rapier_points = Vec::with_capacity(point_count); + for i in 0..point_count { + rapier_points.push(vector_to_rapier(self.points[i])); + } + let mut segments = Vec::new(); + for i in (0..point_count).step_by(2) { + let s = [(i) as u32, (i + 1) as u32]; + segments.push(s); + } + physics_engine.shape_create_concave_polyline(&rapier_points, Some(segments)) + } + + fn set_data(&mut self, data: Variant, physics_engine: &mut PhysicsEngine) { + match data.get_type() { + #[cfg(feature = "dim3")] + VariantType::DICTIONARY => { + if let Ok(dictionary) = data.try_to::() { + if let Some(points) = dictionary.get("faces") + && let Ok(arr) = points.try_to::() + { + let len = arr.len(); + if len == 0 { + return; + } + if len % 3 != 0 { + godot_error!( + "ConcavePolygon3D must have a multiple of 3 number of points" + ); + return; + } + self.points = arr; + } + } + } + #[cfg(feature = "dim2")] + VariantType::PACKED_VECTOR2_ARRAY => { + if let Ok(arr) = data.try_to::() { + let len = self.points.len(); + if len == 0 { + return; + } + if len % 2 != 0 { + godot_error!("ConcavePolygon2D must have an even number of points"); + return; + } + self.points = arr; + } + } + _ => { + // Handle dictionary with arrays + godot_error!("Invalid shape data"); + return; + } + } + let handle = self.create_rapier_shape(physics_engine); + self.base.set_handle(handle, physics_engine); + } + + fn get_data(&self) -> Variant { + self.points.to_variant() + } + + fn get_handle(&self) -> ShapeHandle { + self.base.get_handle() + } +} diff --git a/src/shapes/rapier_concave_polygon_shape_2d.rs b/src/shapes/rapier_concave_polygon_shape_2d.rs deleted file mode 100644 index 1c2d5693..00000000 --- a/src/shapes/rapier_concave_polygon_shape_2d.rs +++ /dev/null @@ -1,139 +0,0 @@ -use godot::engine::physics_server_2d::ShapeType; -use godot::prelude::*; - -use crate::rapier_wrapper::prelude::*; -use crate::shapes::rapier_shape::*; -use crate::types::*; -#[cfg_attr( - feature = "serde-serialize", - derive(serde::Serialize, serde::Deserialize) -)] -pub struct RapierConcavePolygonShape2D { - points: Vec, - segments: Vec<[i32; 2]>, - base: RapierShapeBase, -} -impl RapierConcavePolygonShape2D { - pub fn new(rid: Rid) -> Self { - Self { - points: Vec::new(), - segments: Vec::new(), - base: RapierShapeBase::new(rid), - } - } -} -fn find_or_insert_point(points: &mut Vec, new_point: Vector2) -> usize { - for (idx, &point) in points.iter().enumerate() { - if point.distance_squared_to(new_point) < 1e-4 { - return idx; - } - } - // If the point is not found, add it to the vector and return its index - let idx = points.len(); - points.push(new_point); - idx -} -#[cfg_attr(feature = "serde-serialize", typetag::serde)] -impl IRapierShape for RapierConcavePolygonShape2D { - fn get_base(&self) -> &RapierShapeBase { - &self.base - } - - fn get_mut_base(&mut self) -> &mut RapierShapeBase { - &mut self.base - } - - fn get_type(&self) -> ShapeType { - ShapeType::CONCAVE_POLYGON - } - - fn get_moment_of_inertia(&self, mass: f32, scale: Vector) -> f32 { - if self.points.len() < 3 { - return 0.0; - } - let mut aabb_new = Rect2::new(Vector2::ZERO, Vector2::ZERO); - for point in self.points.iter() { - aabb_new = aabb_new.expand(*point * scale); - } - mass * aabb_new.size.dot(aabb_new.size) / 12.0 - } - - fn allows_one_way_collision(&self) -> bool { - true - } - - fn create_rapier_shape(&mut self, physics_engine: &mut PhysicsEngine) -> ShapeHandle { - if self.points.len() >= 3 { - let point_count = self.points.len(); - let mut rapier_points = Vec::with_capacity(point_count + 1); - for i in 0..point_count { - rapier_points.push(vector_to_rapier(self.points[i])); - } - // Close the polyline shape - rapier_points.push(rapier_points[0]); - physics_engine.shape_create_concave_polyline(&rapier_points) - } else { - godot_error!("ConcavePolygon2D must have at least three point"); - ShapeHandle::default() - } - } - - fn set_data(&mut self, data: Variant, physics_engine: &mut PhysicsEngine) { - let mut aabb = Rect2::default(); - match data.get_type() { - VariantType::PACKED_VECTOR2_ARRAY => { - let arr: PackedVector2Array = data.to(); - let len = arr.len(); - if len == 0 { - return; - } - if len % 2 != 0 { - godot_error!("ConcavePolygon2D must have an even number of points"); - return; - } - self.segments.clear(); - self.points.clear(); - for i in (0..len).step_by(2) { - let p1 = arr[i]; - let p2 = arr[i + 1]; - // Find or insert the points into the `points` vector - let idx_p1 = find_or_insert_point(&mut self.points, p1); - let idx_p2 = find_or_insert_point(&mut self.points, p2); - // Create the segment with the indices of the points - let s = [idx_p1 as i32, idx_p2 as i32]; - self.segments.push(s); - } - for &p in self.points.iter() { - aabb = aabb.expand(p); - } - } - _ => { - // Handle dictionary with arrays - godot_error!("Invalid shape data"); - return; - } - } - let handle = self.create_rapier_shape(physics_engine); - self.base.set_handle(handle, aabb, physics_engine); - } - - fn get_data(&self) -> Variant { - let len = self.segments.len(); - if len == 0 { - return Variant::nil(); - } - let mut rsegments = PackedVector2Array::new(); - rsegments.resize(len * 2); - for i in 0..len { - let idx0 = self.segments[i][0] as usize; - let idx1 = self.segments[i][1] as usize; - rsegments[i << 1] = self.points[idx0]; - rsegments[(i << 1) + 1] = self.points[idx1]; - } - rsegments.to_variant() - } - - fn get_handle(&self) -> ShapeHandle { - self.base.get_handle() - } -} diff --git a/src/shapes/rapier_convex_polygon_shape.rs b/src/shapes/rapier_convex_polygon_shape.rs index ab5ef617..1bdcebf0 100644 --- a/src/shapes/rapier_convex_polygon_shape.rs +++ b/src/shapes/rapier_convex_polygon_shape.rs @@ -25,14 +25,6 @@ impl RapierConvexPolygonShape { base: RapierShapeBase::new(rid), } } - - fn compute_aabb(&self, scale: Vector) -> Rect { - let mut aabb_new = Rect::new(Vector::ZERO, Vector::ZERO); - for point in self.points.as_slice() { - aabb_new = aabb_new.expand(*point * scale); - } - aabb_new - } } #[cfg_attr(feature = "serde-serialize", typetag::serde)] impl IRapierShape for RapierConvexPolygonShape { @@ -48,29 +40,6 @@ impl IRapierShape for RapierConvexPolygonShape { ShapeType::CONVEX_POLYGON } - #[cfg(feature = "dim2")] - fn get_moment_of_inertia(&self, mass: f32, scale: Vector) -> f32 { - if self.points.len() < 3 { - return 0.0; - } - let aabb_new = self.compute_aabb(scale); - mass * aabb_new.size.dot(aabb_new.size) / 12.0 - } - - #[cfg(feature = "dim3")] - fn get_moment_of_inertia(&self, mass: f32, scale: Vector) -> Vector3 { - if self.points.len() < 3 { - return Vector3::ZERO; - } - // use bad AABB approximation - let extents = self.compute_aabb(scale).size * 0.5; - Vector3::new( - (mass / 3.0) * (extents.y * extents.y + extents.z * extents.z), - (mass / 3.0) * (extents.x * extents.x + extents.z * extents.z), - (mass / 3.0) * (extents.x * extents.x + extents.y * extents.y), - ) - } - fn allows_one_way_collision(&self) -> bool { true } @@ -125,8 +94,7 @@ impl IRapierShape for RapierConvexPolygonShape { return; } let handle = self.create_rapier_shape(physics_engine); - self.base - .set_handle(handle, self.compute_aabb(Vector::ONE), physics_engine); + self.base.set_handle(handle, physics_engine); } fn get_data(&self) -> Variant { diff --git a/src/shapes/rapier_cylinder_shape_3d.rs b/src/shapes/rapier_cylinder_shape_3d.rs index 75ed5bb8..a5c63680 100644 --- a/src/shapes/rapier_cylinder_shape_3d.rs +++ b/src/shapes/rapier_cylinder_shape_3d.rs @@ -3,7 +3,6 @@ use godot::prelude::*; use crate::rapier_wrapper::prelude::*; use crate::shapes::rapier_shape::*; -use crate::types::*; #[cfg_attr( feature = "serde-serialize", derive(serde::Serialize, serde::Deserialize) @@ -36,16 +35,6 @@ impl IRapierShape for RapierCylinderShape3D { ShapeType::CYLINDER } - fn get_moment_of_inertia(&self, p_mass: f32, _scale: Vector) -> Vector3 { - // use bad AABB approximation - let extents = self.compute_aabb().size * 0.5; - Vector3::new( - (p_mass / 3.0) * (extents.y * extents.y + extents.z * extents.z), - (p_mass / 3.0) * (extents.x * extents.x + extents.z * extents.z), - (p_mass / 3.0) * (extents.x * extents.x + extents.y * extents.y), - ) - } - fn allows_one_way_collision(&self) -> bool { true } @@ -91,8 +80,7 @@ impl IRapierShape for RapierCylinderShape3D { self.radius = self.height * 0.5; } let handle = self.create_rapier_shape(physics_engine); - self.base - .set_handle(handle, self.compute_aabb(), physics_engine); + self.base.set_handle(handle, physics_engine); } fn get_data(&self) -> Variant { @@ -103,9 +91,3 @@ impl IRapierShape for RapierCylinderShape3D { self.base.get_handle() } } -impl RapierCylinderShape3D { - fn compute_aabb(&self) -> Aabb { - let he = Vector3::new(self.radius, self.height * 0.5, self.radius); - Aabb::new(-he, he * 2.0) - } -} diff --git a/src/shapes/rapier_heightmap_shape_3d.rs b/src/shapes/rapier_heightmap_shape_3d.rs new file mode 100644 index 00000000..fa188266 --- /dev/null +++ b/src/shapes/rapier_heightmap_shape_3d.rs @@ -0,0 +1,136 @@ +#[cfg(feature = "dim2")] +use godot::classes::physics_server_2d::*; +#[cfg(feature = "dim3")] +use godot::classes::physics_server_3d::*; +use godot::prelude::*; + +use crate::rapier_wrapper::prelude::*; +use crate::shapes::rapier_shape::IRapierShape; +use crate::shapes::rapier_shape::RapierShapeBase; +use crate::types::*; +#[cfg_attr( + feature = "serde-serialize", + derive(serde::Serialize, serde::Deserialize) +)] +pub struct RapierHeightMapShape3D { + // TODO serialize this + #[cfg_attr(feature = "serde-serialize", serde(skip))] + heights: PackedFloatArray, + width: i32, + depth: i32, + base: RapierShapeBase, +} +impl RapierHeightMapShape3D { + pub fn new(rid: Rid) -> Self { + Self { + heights: PackedFloatArray::new(), + width: 0, + depth: 0, + base: RapierShapeBase::new(rid), + } + } +} +#[cfg_attr(feature = "serde-serialize", typetag::serde)] +impl IRapierShape for RapierHeightMapShape3D { + fn get_base(&self) -> &RapierShapeBase { + &self.base + } + + fn get_mut_base(&mut self) -> &mut RapierShapeBase { + &mut self.base + } + + fn get_type(&self) -> ShapeType { + ShapeType::CONVEX_POLYGON + } + + fn allows_one_way_collision(&self) -> bool { + true + } + + fn create_rapier_shape(&mut self, physics_engine: &mut PhysicsEngine) -> ShapeHandle { + physics_engine.shape_create_heightmap(self.heights.as_slice(), self.width, self.depth) + } + + fn set_data(&mut self, data: Variant, physics_engine: &mut PhysicsEngine) { + match data.get_type() { + VariantType::DICTIONARY => { + if let Ok(dictionary) = data.try_to::() { + let width = dictionary.get_or_nil("width"); + let depth = dictionary.get_or_nil("depth"); + let new_heights = dictionary.get_or_nil("heights"); + if let Ok(width) = width.try_to::() + && let Ok(depth) = depth.try_to::() + { + if width <= 1 || depth <= 1 { + godot_error!("Heightmap must have width and depth at least 2"); + return; + } + let heights: PackedFloatArray; + if let Ok(new_heights) = new_heights.try_to::() { + heights = new_heights; + } + // else if let Ok(image) = heights.try_to::() { + // TODO image support + //} + else { + godot_error!("Invalid heightmap shape data"); + return; + } + // Compute min and max heights or use precomputed values. + let mut min_height: real = 0.0; + let mut max_height: real = 0.0; + if let Some(new_min_height) = dictionary.get("min_height") + && let Some(new_max_height) = dictionary.get("max_height") + { + let new_min_height = variant_to_float(&new_min_height); + let new_max_height = variant_to_float(&new_max_height); + min_height = new_min_height; + max_height = new_max_height; + } else { + let heights_size = heights.len(); + for i in 0..heights_size { + let h = heights[i]; + if h < min_height { + min_height = h; + } else if h > max_height { + max_height = h; + } + } + } + if min_height > max_height { + godot_error!("Invalid heightmap shape data"); + return; + } + self.heights = heights; + self.width = width; + self.depth = depth; + } + } + } + _ => godot_error!("Invalid heightmap shape data"), + } + if self.heights.len() != (self.width * self.depth) as usize { + godot_error!("Invalid heightmap shape data"); + return; + } + if self.width <= 1 || self.depth <= 1 { + godot_error!("Heightmap must have width and depth at least 2"); + return; + } + let handle = self.create_rapier_shape(physics_engine); + self.base.set_handle(handle, physics_engine); + } + + fn get_data(&self) -> Variant { + let mut dictionary = Dictionary::new(); + let _ = dictionary.insert("width", self.width); + let _ = dictionary.insert("depth", self.depth); + let _ = dictionary.insert("heights", self.heights.clone()); + dictionary.to_variant() + } + + fn get_handle(&self) -> ShapeHandle { + self.base.get_handle() + } +} diff --git a/src/shapes/rapier_rectangle_shape.rs b/src/shapes/rapier_rectangle_shape.rs index 20209213..4a0445e7 100644 --- a/src/shapes/rapier_rectangle_shape.rs +++ b/src/shapes/rapier_rectangle_shape.rs @@ -44,24 +44,6 @@ impl IRapierShape for RapierRectangleShape { ShapeType::BOX } - #[cfg(feature = "dim2")] - fn get_moment_of_inertia(&self, mass: f32, scale: Vector) -> f32 { - let he2 = self.half_extents * 2.0 * scale; - mass * he2.dot(he2) / 12.0 - } - - #[cfg(feature = "dim3")] - fn get_moment_of_inertia(&self, mass: f32, _scale: Vector) -> Vector3 { - let lx = self.half_extents.x; - let ly = self.half_extents.y; - let lz = self.half_extents.z; - Vector3::new( - (mass / 3.0) * (ly * ly + lz * lz), - (mass / 3.0) * (lx * lx + lz * lz), - (mass / 3.0) * (lx * lx + ly * ly), - ) - } - fn allows_one_way_collision(&self) -> bool { true } @@ -74,9 +56,8 @@ impl IRapierShape for RapierRectangleShape { fn set_data(&mut self, data: Variant, physics_engine: &mut PhysicsEngine) { if let Ok(v) = data.try_to() { self.half_extents = v; - let aabb = Rect::new(-self.half_extents, self.half_extents * 2.0); let handle = self.create_rapier_shape(physics_engine); - self.base.set_handle(handle, aabb, physics_engine); + self.base.set_handle(handle, physics_engine); } else { godot_error!("Invalid data type for RapierRectangleShape"); } diff --git a/src/shapes/rapier_segment_shape_2d.rs b/src/shapes/rapier_segment_shape_2d.rs index d893fb72..7c22dd7c 100644 --- a/src/shapes/rapier_segment_shape_2d.rs +++ b/src/shapes/rapier_segment_shape_2d.rs @@ -4,7 +4,6 @@ use godot::prelude::*; use crate::rapier_wrapper::prelude::*; use crate::shapes::rapier_shape::IRapierShape; use crate::shapes::rapier_shape::RapierShapeBase; -use crate::types::*; #[cfg_attr( feature = "serde-serialize", derive(serde::Serialize, serde::Deserialize) @@ -12,7 +11,6 @@ use crate::types::*; pub struct RapierSegmentShape2D { a: Vector2, b: Vector2, - n: Vector2, base: RapierShapeBase, } impl RapierSegmentShape2D { @@ -20,7 +18,6 @@ impl RapierSegmentShape2D { Self { a: Vector2::ZERO, b: Vector2::ZERO, - n: Vector2::ZERO, base: RapierShapeBase::new(rid), } } @@ -39,30 +36,15 @@ impl IRapierShape for RapierSegmentShape2D { ShapeType::SEGMENT } - fn get_moment_of_inertia(&self, mass: f32, scale: Vector) -> f32 { - mass * (self.a.distance_to(self.b) * scale.length_squared()) / 12.0 - } - fn allows_one_way_collision(&self) -> bool { true } fn create_rapier_shape(&mut self, physics_engine: &mut PhysicsEngine) -> ShapeHandle { - let direction = self.b - self.a; - let direction_normalized = vector_normalized(direction); - let perpendicular = Vector2::new(-direction_normalized.y, direction_normalized.x); - let height = 0.1; - let p1 = self.a + perpendicular * height / 2.0; - let p2 = self.a - perpendicular * height / 2.0; - let p3 = self.b + perpendicular * height / 2.0; - let p4 = self.b - perpendicular * height / 2.0; - let rapier_points = [ - vector_to_rapier(p1), - vector_to_rapier(p2), - vector_to_rapier(p3), - vector_to_rapier(p4), - ]; - physics_engine.shape_create_convex_polyline(&rapier_points.to_vec()) + let p1 = self.a; + let p2 = self.b; + let rapier_points = [vector_to_rapier(p1), vector_to_rapier(p2)]; + physics_engine.shape_create_concave_polyline(&rapier_points.to_vec(), None) } fn set_data(&mut self, data: Variant, physics_engine: &mut PhysicsEngine) { @@ -73,16 +55,8 @@ impl IRapierShape for RapierSegmentShape2D { let r: Rect2 = data.to(); self.a = r.position; self.b = r.position + r.size; - self.n = (self.b - self.a).orthogonal(); - let mut aabb = Rect2::new(self.a, self.b); - if aabb.size.x == 0.0 { - aabb.size.x = 0.001; - } - if aabb.size.y == 0.0 { - aabb.size.y = 0.001; - } let handle = self.create_rapier_shape(physics_engine); - self.base.set_handle(handle, aabb, physics_engine); + self.base.set_handle(handle, physics_engine); } fn get_data(&self) -> Variant { diff --git a/src/shapes/rapier_separation_ray_shape.rs b/src/shapes/rapier_separation_ray_shape.rs index 03e2f6f5..d455ed60 100644 --- a/src/shapes/rapier_separation_ray_shape.rs +++ b/src/shapes/rapier_separation_ray_shape.rs @@ -7,7 +7,6 @@ use godot::prelude::*; use crate::rapier_wrapper::prelude::*; use crate::shapes::rapier_shape::IRapierShape; use crate::shapes::rapier_shape::RapierShapeBase; -use crate::types::*; #[cfg_attr( feature = "serde-serialize", derive(serde::Serialize, serde::Deserialize) @@ -40,16 +39,6 @@ impl IRapierShape for RapierSeparationRayShape { ShapeType::SEPARATION_RAY } - #[cfg(feature = "dim2")] - fn get_moment_of_inertia(&self, _mass: f32, _scale: Vector) -> f32 { - 0.0 - } - - #[cfg(feature = "dim3")] - fn get_moment_of_inertia(&self, _mass: f32, _scale: Vector) -> Vector3 { - Vector3::new(0.0, 0.0, 0.0) - } - fn allows_one_way_collision(&self) -> bool { false } diff --git a/src/shapes/rapier_shape.rs b/src/shapes/rapier_shape.rs index d2a0d437..a48fa340 100644 --- a/src/shapes/rapier_shape.rs +++ b/src/shapes/rapier_shape.rs @@ -13,7 +13,6 @@ pub trait IRapierShape { fn get_base(&self) -> &RapierShapeBase; fn get_mut_base(&mut self) -> &mut RapierShapeBase; fn get_type(&self) -> ShapeType; - fn get_moment_of_inertia(&self, mass: f32, scale: Vector) -> Angle; fn allows_one_way_collision(&self) -> bool; fn create_rapier_shape(&mut self, physics_engine: &mut PhysicsEngine) -> ShapeHandle; fn set_data(&mut self, data: Variant, physics_engine: &mut PhysicsEngine); @@ -42,16 +41,16 @@ impl RapierShapeBase { } } - pub(super) fn set_handle( - &mut self, - handle: ShapeHandle, - aabb: Rect, - physics_engine: &mut PhysicsEngine, - ) { + pub(super) fn set_handle(&mut self, handle: ShapeHandle, physics_engine: &mut PhysicsEngine) { if self.handle != ShapeHandle::default() { self.destroy_shape(physics_engine); } - self.aabb = aabb; + let rapier_aabb = physics_engine.shape_get_aabb(handle); + let vertices = rapier_aabb.vertices(); + self.aabb = Rect::new( + vector_to_godot(vertices[0].coords), + vector_to_godot(rapier_aabb.extents()), + ); self.handle = handle; } diff --git a/src/shapes/rapier_world_boundary_shape.rs b/src/shapes/rapier_world_boundary_shape.rs index 0be4af01..99fb291e 100644 --- a/src/shapes/rapier_world_boundary_shape.rs +++ b/src/shapes/rapier_world_boundary_shape.rs @@ -41,16 +41,6 @@ impl IRapierShape for RapierWorldBoundaryShape { ShapeType::WORLD_BOUNDARY } - #[cfg(feature = "dim2")] - fn get_moment_of_inertia(&self, _mass: f32, _scale: Vector) -> Angle { - f32::MAX - } - - #[cfg(feature = "dim3")] - fn get_moment_of_inertia(&self, _mass: f32, _scale: Vector) -> Angle { - Vector3::new(f32::MAX, f32::MAX, f32::MAX) - } - fn allows_one_way_collision(&self) -> bool { true } @@ -80,8 +70,7 @@ impl IRapierShape for RapierWorldBoundaryShape { self.normal = arr.at(0).to(); self.d = variant_to_float(&arr.at(1)); let handle = self.create_rapier_shape(physics_engine); - let rect = Rect2::new(Vector2::new(-1e4, -1e4), Vector2::new(1e4 * 2.0, 1e4 * 2.0)); - self.base.set_handle(handle, rect, physics_engine); + self.base.set_handle(handle, physics_engine); } #[cfg(feature = "dim3")] @@ -94,11 +83,7 @@ impl IRapierShape for RapierWorldBoundaryShape { self.normal = plane.normal; self.d = plane.d; let handle = self.create_rapier_shape(physics_engine); - let rect = Aabb::new( - Vector::new(-1e4, -1e4, -1e4), - Vector::new(1e4 * 2.0, 1e4 * 2.0, 1e4 * 2.0), - ); - self.base.set_handle(handle, rect, physics_engine); + self.base.set_handle(handle, physics_engine); } #[cfg(feature = "dim2")]