use crate::scene::node::constructor::NodeConstructor;
use crate::{
core::{
algebra::Vector3,
log::Log,
math::aabb::AxisAlignedBoundingBox,
num_traits::{NumCast, One, ToPrimitive, Zero},
pool::Handle,
reflect::prelude::*,
type_traits::prelude::*,
uuid::{uuid, Uuid},
variable::InheritableVariable,
visitor::prelude::*,
},
scene::{
base::{Base, BaseBuilder},
graph::{
physics::{CoefficientCombineRule, ContactPair, IntersectionPair, PhysicsWorld},
Graph,
},
node::{Node, NodeTrait, SyncContext},
rigidbody::RigidBody,
Scene,
},
};
use fyrox_core::algebra::{Isometry3, Translation3};
use fyrox_core::uuid_provider;
use fyrox_graph::constructor::ConstructorProvider;
use fyrox_graph::SceneGraph;
use rapier3d::geometry::{self, ColliderHandle};
use std::fmt::Write;
use std::{
cell::Cell,
ops::{Add, BitAnd, BitOr, Deref, DerefMut, Mul, Not, Shl},
};
use strum_macros::{AsRefStr, EnumString, VariantNames};
#[derive(Clone, Debug, PartialEq, Visit, Reflect)]
pub struct BallShape {
#[reflect(min_value = 0.001, step = 0.05)]
pub radius: f32,
}
impl Default for BallShape {
fn default() -> Self {
Self { radius: 0.5 }
}
}
#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
pub struct CylinderShape {
#[reflect(min_value = 0.001, step = 0.05)]
pub half_height: f32,
#[reflect(min_value = 0.001, step = 0.05)]
pub radius: f32,
}
impl Default for CylinderShape {
fn default() -> Self {
Self {
half_height: 0.5,
radius: 0.5,
}
}
}
#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
pub struct ConeShape {
#[reflect(min_value = 0.001, step = 0.05)]
pub half_height: f32,
#[reflect(min_value = 0.001, step = 0.05)]
pub radius: f32,
}
impl Default for ConeShape {
fn default() -> Self {
Self {
half_height: 0.5,
radius: 0.5,
}
}
}
#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
pub struct CuboidShape {
#[reflect(min_value = 0.001, step = 0.05)]
pub half_extents: Vector3<f32>,
}
impl Default for CuboidShape {
fn default() -> Self {
Self {
half_extents: Vector3::new(0.5, 0.5, 0.5),
}
}
}
#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
pub struct CapsuleShape {
pub begin: Vector3<f32>,
pub end: Vector3<f32>,
#[reflect(min_value = 0.001, step = 0.05)]
pub radius: f32,
}
impl Default for CapsuleShape {
fn default() -> Self {
Self {
begin: Default::default(),
end: Vector3::new(0.0, 1.0, 0.0),
radius: 0.5,
}
}
}
#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
pub struct SegmentShape {
pub begin: Vector3<f32>,
pub end: Vector3<f32>,
}
impl Default for SegmentShape {
fn default() -> Self {
Self {
begin: Default::default(),
end: Vector3::new(0.0, 1.0, 0.0),
}
}
}
#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
pub struct TriangleShape {
pub a: Vector3<f32>,
pub b: Vector3<f32>,
pub c: Vector3<f32>,
}
impl Default for TriangleShape {
fn default() -> Self {
Self {
a: Default::default(),
b: Vector3::new(1.0, 0.0, 0.0),
c: Vector3::new(0.0, 0.0, 1.0),
}
}
}
#[derive(Default, Clone, Copy, PartialEq, Hash, Debug, Visit, Reflect, Eq)]
pub struct GeometrySource(pub Handle<Node>);
uuid_provider!(GeometrySource = "6fea7c72-c488-48a1-935f-2752a8a10e9a");
#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq, Eq)]
pub struct TrimeshShape {
pub sources: Vec<GeometrySource>,
}
#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq, Eq)]
pub struct HeightfieldShape {
pub geometry_source: GeometrySource,
}
#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq, Eq)]
pub struct ConvexPolyhedronShape {
pub geometry_source: GeometrySource,
}
#[derive(Clone, Copy, Default, PartialEq, Reflect, Eq)]
pub struct BitMask(pub u32);
uuid_provider!(BitMask = "f2db0c2a-921b-4728-9ce4-2506d95c60fa");
impl BitMask {
pub const fn all() -> Self {
Self(u32::MAX)
}
pub const fn none() -> Self {
Self(0)
}
pub const fn with(self, index: usize) -> Self {
Self(self.0 | (1 << index))
}
pub const fn without(self, index: usize) -> Self {
Self(self.0 & !(1 << index))
}
pub fn bit(&self, index: usize) -> bool {
(self.0 >> index) & 1 != 0
}
pub fn set_bit(&mut self, index: usize, value: bool) {
if value {
self.0 |= 1 << index;
} else {
self.0 &= !(1 << index);
}
}
}
impl std::fmt::Debug for BitMask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "BitMask({:08x})", self.0)
}
}
impl std::fmt::Display for BitMask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut value = self.0;
for i in 0..4 {
if i != 0 {
f.write_char(' ')?;
}
for _ in 0..8 {
let bit = if value & 1 == 1 { '1' } else { '0' };
value >>= 1;
f.write_char(bit)?;
}
}
Ok(())
}
}
impl Visit for BitMask {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
self.0.visit(name, visitor)
}
}
impl BitOr for BitMask {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl BitAnd for BitMask {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0)
}
}
impl Mul for BitMask {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self(self.0 * rhs.0)
}
}
impl One for BitMask {
fn one() -> Self {
Self(1)
}
}
impl Add for BitMask {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Zero for BitMask {
fn zero() -> Self {
Self(0)
}
fn is_zero(&self) -> bool {
self.0 == 0
}
}
impl Shl for BitMask {
type Output = Self;
fn shl(self, rhs: Self) -> Self::Output {
Self(self.0 << rhs.0)
}
}
impl Not for BitMask {
type Output = Self;
fn not(self) -> Self::Output {
Self(!self.0)
}
}
impl ToPrimitive for BitMask {
fn to_i64(&self) -> Option<i64> {
Some(self.0 as i64)
}
fn to_u64(&self) -> Option<u64> {
Some(self.0 as u64)
}
}
impl NumCast for BitMask {
fn from<T: ToPrimitive>(n: T) -> Option<Self> {
n.to_u32().map(Self)
}
}
#[derive(Visit, Debug, Clone, Copy, PartialEq, Reflect, Eq)]
pub struct InteractionGroups {
pub memberships: BitMask,
pub filter: BitMask,
}
impl InteractionGroups {
pub fn new(memberships: BitMask, filter: BitMask) -> Self {
Self {
memberships,
filter,
}
}
}
impl std::fmt::Display for InteractionGroups {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} (filter: {})", self.memberships, self.filter)
}
}
impl Default for InteractionGroups {
fn default() -> Self {
Self {
memberships: BitMask::all(),
filter: BitMask::all(),
}
}
}
impl From<geometry::InteractionGroups> for InteractionGroups {
fn from(g: geometry::InteractionGroups) -> Self {
Self {
memberships: BitMask(g.memberships.bits()),
filter: BitMask(g.filter.bits()),
}
}
}
bitflags::bitflags! {
#[derive(Default, Copy, Clone)]
pub struct QueryFilterFlags: u32 {
const EXCLUDE_FIXED = 1 << 1;
const EXCLUDE_KINEMATIC = 1 << 2;
const EXCLUDE_DYNAMIC = 1 << 3;
const EXCLUDE_SENSORS = 1 << 4;
const EXCLUDE_SOLIDS = 1 << 5;
const ONLY_DYNAMIC = Self::EXCLUDE_FIXED.bits() | Self::EXCLUDE_KINEMATIC.bits();
const ONLY_KINEMATIC = Self::EXCLUDE_DYNAMIC.bits() | Self::EXCLUDE_FIXED.bits();
const ONLY_FIXED = Self::EXCLUDE_DYNAMIC.bits() | Self::EXCLUDE_KINEMATIC.bits();
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TOIStatus {
OutOfIterations,
Converged,
Failed,
Penetrating,
}
impl From<rapier3d::parry::query::ShapeCastStatus> for TOIStatus {
fn from(value: rapier3d::parry::query::ShapeCastStatus) -> Self {
match value {
rapier3d::parry::query::ShapeCastStatus::OutOfIterations => Self::OutOfIterations,
rapier3d::parry::query::ShapeCastStatus::Converged => Self::Converged,
rapier3d::parry::query::ShapeCastStatus::Failed => Self::Failed,
rapier3d::parry::query::ShapeCastStatus::PenetratingOrWithinTargetDist => {
Self::Penetrating
}
}
}
}
impl From<rapier2d::parry::query::ShapeCastStatus> for TOIStatus {
fn from(value: rapier2d::parry::query::ShapeCastStatus) -> Self {
match value {
rapier2d::parry::query::ShapeCastStatus::OutOfIterations => Self::OutOfIterations,
rapier2d::parry::query::ShapeCastStatus::Converged => Self::Converged,
rapier2d::parry::query::ShapeCastStatus::Failed => Self::Failed,
rapier2d::parry::query::ShapeCastStatus::PenetratingOrWithinTargetDist => {
Self::Penetrating
}
}
}
}
#[derive(Clone, Debug, PartialEq, Visit, Reflect, AsRefStr, EnumString, VariantNames)]
pub enum ColliderShape {
Ball(BallShape),
Cylinder(CylinderShape),
Cone(ConeShape),
Cuboid(CuboidShape),
Capsule(CapsuleShape),
Segment(SegmentShape),
Triangle(TriangleShape),
Trimesh(TrimeshShape),
Heightfield(HeightfieldShape),
Polyhedron(ConvexPolyhedronShape),
}
uuid_provider!(ColliderShape = "2e627337-71ea-4b33-a5f1-be697f705a86");
impl Default for ColliderShape {
fn default() -> Self {
Self::Ball(Default::default())
}
}
impl ColliderShape {
pub fn ball(radius: f32) -> Self {
Self::Ball(BallShape { radius })
}
pub fn cylinder(half_height: f32, radius: f32) -> Self {
Self::Cylinder(CylinderShape {
half_height,
radius,
})
}
pub fn cone(half_height: f32, radius: f32) -> Self {
Self::Cone(ConeShape {
half_height,
radius,
})
}
pub fn cuboid(hx: f32, hy: f32, hz: f32) -> Self {
Self::Cuboid(CuboidShape {
half_extents: Vector3::new(hx, hy, hz),
})
}
pub fn capsule(begin: Vector3<f32>, end: Vector3<f32>, radius: f32) -> Self {
Self::Capsule(CapsuleShape { begin, end, radius })
}
pub fn capsule_x(half_height: f32, radius: f32) -> Self {
let p = Vector3::x() * half_height;
Self::capsule(-p, p, radius)
}
pub fn capsule_y(half_height: f32, radius: f32) -> Self {
let p = Vector3::y() * half_height;
Self::capsule(-p, p, radius)
}
pub fn capsule_z(half_height: f32, radius: f32) -> Self {
let p = Vector3::z() * half_height;
Self::capsule(-p, p, radius)
}
pub fn segment(begin: Vector3<f32>, end: Vector3<f32>) -> Self {
Self::Segment(SegmentShape { begin, end })
}
pub fn triangle(a: Vector3<f32>, b: Vector3<f32>, c: Vector3<f32>) -> Self {
Self::Triangle(TriangleShape { a, b, c })
}
pub fn trimesh(geometry_sources: Vec<GeometrySource>) -> Self {
Self::Trimesh(TrimeshShape {
sources: geometry_sources,
})
}
pub fn heightfield(geometry_source: GeometrySource) -> Self {
Self::Heightfield(HeightfieldShape { geometry_source })
}
}
#[derive(Reflect, Visit, Debug, ComponentProvider)]
#[reflect(derived_type = "Node")]
pub struct Collider {
base: Base,
#[reflect(setter = "set_shape")]
pub(crate) shape: InheritableVariable<ColliderShape>,
#[reflect(min_value = 0.0, step = 0.05, setter = "set_friction")]
pub(crate) friction: InheritableVariable<f32>,
#[reflect(setter = "set_density")]
pub(crate) density: InheritableVariable<Option<f32>>,
#[reflect(min_value = 0.0, step = 0.05, setter = "set_restitution")]
pub(crate) restitution: InheritableVariable<f32>,
#[reflect(setter = "set_is_sensor")]
pub(crate) is_sensor: InheritableVariable<bool>,
#[reflect(setter = "set_collision_groups")]
pub(crate) collision_groups: InheritableVariable<InteractionGroups>,
#[reflect(setter = "set_solver_groups")]
pub(crate) solver_groups: InheritableVariable<InteractionGroups>,
#[reflect(setter = "set_friction_combine_rule")]
pub(crate) friction_combine_rule: InheritableVariable<CoefficientCombineRule>,
#[reflect(setter = "set_restitution_combine_rule")]
pub(crate) restitution_combine_rule: InheritableVariable<CoefficientCombineRule>,
#[visit(skip)]
#[reflect(hidden)]
pub(crate) native: Cell<ColliderHandle>,
}
impl Default for Collider {
fn default() -> Self {
Self {
base: Default::default(),
shape: Default::default(),
friction: InheritableVariable::new_modified(0.0),
density: InheritableVariable::new_modified(None),
restitution: InheritableVariable::new_modified(0.0),
is_sensor: InheritableVariable::new_modified(false),
collision_groups: Default::default(),
solver_groups: Default::default(),
friction_combine_rule: Default::default(),
restitution_combine_rule: Default::default(),
native: Cell::new(ColliderHandle::invalid()),
}
}
}
impl Deref for Collider {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for Collider {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl Clone for Collider {
fn clone(&self) -> Self {
Self {
base: self.base.clone(),
shape: self.shape.clone(),
friction: self.friction.clone(),
density: self.density.clone(),
restitution: self.restitution.clone(),
is_sensor: self.is_sensor.clone(),
collision_groups: self.collision_groups.clone(),
solver_groups: self.solver_groups.clone(),
friction_combine_rule: self.friction_combine_rule.clone(),
restitution_combine_rule: self.restitution_combine_rule.clone(),
native: Cell::new(ColliderHandle::invalid()),
}
}
}
impl TypeUuidProvider for Collider {
fn type_uuid() -> Uuid {
uuid!("bfaa2e82-9c19-4b99-983b-3bc115744a1d")
}
}
impl Collider {
pub fn set_shape(&mut self, shape: ColliderShape) -> ColliderShape {
self.shape.set_value_and_mark_modified(shape)
}
pub fn shape(&self) -> &ColliderShape {
&self.shape
}
pub fn shape_value(&self) -> ColliderShape {
(*self.shape).clone()
}
pub fn shape_mut(&mut self) -> &mut ColliderShape {
self.shape.get_value_mut_and_mark_modified()
}
pub fn set_restitution(&mut self, restitution: f32) -> f32 {
self.restitution.set_value_and_mark_modified(restitution)
}
pub fn restitution(&self) -> f32 {
*self.restitution
}
pub fn set_density(&mut self, density: Option<f32>) -> Option<f32> {
self.density.set_value_and_mark_modified(density)
}
pub fn density(&self) -> Option<f32> {
*self.density
}
pub fn set_friction(&mut self, friction: f32) -> f32 {
self.friction.set_value_and_mark_modified(friction)
}
pub fn friction(&self) -> f32 {
*self.friction
}
pub fn set_collision_groups(&mut self, groups: InteractionGroups) -> InteractionGroups {
self.collision_groups.set_value_and_mark_modified(groups)
}
pub fn collision_groups(&self) -> InteractionGroups {
*self.collision_groups
}
pub fn set_solver_groups(&mut self, groups: InteractionGroups) -> InteractionGroups {
self.solver_groups.set_value_and_mark_modified(groups)
}
pub fn solver_groups(&self) -> InteractionGroups {
*self.solver_groups
}
pub fn set_is_sensor(&mut self, is_sensor: bool) -> bool {
self.is_sensor.set_value_and_mark_modified(is_sensor)
}
pub fn is_sensor(&self) -> bool {
*self.is_sensor
}
pub fn set_friction_combine_rule(
&mut self,
rule: CoefficientCombineRule,
) -> CoefficientCombineRule {
self.friction_combine_rule.set_value_and_mark_modified(rule)
}
pub fn friction_combine_rule(&self) -> CoefficientCombineRule {
*self.friction_combine_rule
}
pub fn set_restitution_combine_rule(
&mut self,
rule: CoefficientCombineRule,
) -> CoefficientCombineRule {
self.restitution_combine_rule
.set_value_and_mark_modified(rule)
}
pub fn restitution_combine_rule(&self) -> CoefficientCombineRule {
*self.restitution_combine_rule
}
pub fn contacts<'a>(
&self,
physics: &'a PhysicsWorld,
) -> impl Iterator<Item = ContactPair> + 'a {
physics.contacts_with(self.native.get())
}
pub fn active_contacts<'a>(
&self,
physics: &'a PhysicsWorld,
) -> impl Iterator<Item = ContactPair> + 'a {
self.contacts(physics)
.filter(|pair| pair.has_any_active_contact)
}
pub fn intersects<'a>(
&self,
physics: &'a PhysicsWorld,
) -> impl Iterator<Item = IntersectionPair> + 'a {
physics.intersections_with(self.native.get())
}
pub fn active_intersects<'a>(
&self,
physics: &'a PhysicsWorld,
) -> impl Iterator<Item = Handle<Collider>> + 'a {
let self_handle = self.handle().to_variant();
self.intersects(physics)
.filter(|pair| pair.has_any_active_contact)
.map(move |pair| pair.other(self_handle))
}
pub(crate) fn needs_sync_model(&self) -> bool {
self.shape.need_sync()
|| self.friction.need_sync()
|| self.density.need_sync()
|| self.restitution.need_sync()
|| self.is_sensor.need_sync()
|| self.collision_groups.need_sync()
|| self.solver_groups.need_sync()
|| self.friction_combine_rule.need_sync()
|| self.restitution_combine_rule.need_sync()
}
}
impl ConstructorProvider<Node, Graph> for Collider {
fn constructor() -> NodeConstructor {
NodeConstructor::new::<Self>()
.with_variant("Collider", |_| {
ColliderBuilder::new(BaseBuilder::new().with_name("Collider"))
.with_shape(ColliderShape::Cuboid(Default::default()))
.build_node()
.into()
})
.with_group("Physics")
}
}
impl NodeTrait for Collider {
fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
self.base.local_bounding_box()
}
fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
self.base.world_bounding_box()
}
fn id(&self) -> Uuid {
Self::type_uuid()
}
fn on_removed_from_graph(&mut self, graph: &mut Graph) {
graph.physics.remove_collider(self.native.get());
self.native.set(ColliderHandle::invalid());
Log::info(format!(
"Native collider was removed for node: {}",
self.name()
));
}
fn on_unlink(&mut self, graph: &mut Graph) {
if graph.physics.remove_collider(self.native.get()) {
self.native.set(ColliderHandle::invalid());
}
}
fn on_local_transform_changed(&self, context: &mut SyncContext) {
if self.native.get() != ColliderHandle::invalid() {
if let Some(native) = context.physics.colliders.get_mut(self.native.get()) {
native.set_position_wrt_parent(
Isometry3 {
rotation: **self.local_transform().rotation(),
translation: Translation3 {
vector: **self.local_transform().position(),
},
}
.into(),
);
}
}
}
fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
context
.physics
.sync_to_collider_node(context.nodes, self_handle, self);
}
fn validate(&self, scene: &Scene) -> Result<(), String> {
let mut message = String::new();
if scene
.graph
.try_get_of_type::<RigidBody>(self.parent())
.is_err()
{
message += "3D Collider must be a direct child of a 3D Rigid Body node, \
otherwise it will not have any effect!";
}
match &*self.shape {
ColliderShape::Trimesh(trimesh) => {
for source in trimesh.sources.iter() {
if !scene.graph.is_valid_handle(source.0) {
message += &format!("Trimesh data source {} handle is invalid!", source.0);
}
}
}
ColliderShape::Heightfield(heightfield) => {
if !scene.graph.is_valid_handle(heightfield.geometry_source.0) {
message += &format!(
"Heightfield data source {} handle is invalid!",
heightfield.geometry_source.0
);
}
}
ColliderShape::Polyhedron(polyhedron) => {
if !scene.graph.is_valid_handle(polyhedron.geometry_source.0) {
message += &format!(
"Polyhedron data source {} handle is invalid!",
polyhedron.geometry_source.0
);
}
}
_ => (),
}
if message.is_empty() {
Ok(())
} else {
Err(message)
}
}
}
pub struct ColliderBuilder {
base_builder: BaseBuilder,
shape: ColliderShape,
friction: f32,
density: Option<f32>,
restitution: f32,
is_sensor: bool,
collision_groups: InteractionGroups,
solver_groups: InteractionGroups,
friction_combine_rule: CoefficientCombineRule,
restitution_combine_rule: CoefficientCombineRule,
}
impl ColliderBuilder {
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
base_builder,
shape: Default::default(),
friction: 0.0,
density: None,
restitution: 0.0,
is_sensor: false,
collision_groups: Default::default(),
solver_groups: Default::default(),
friction_combine_rule: Default::default(),
restitution_combine_rule: Default::default(),
}
}
pub fn with_shape(mut self, shape: ColliderShape) -> Self {
self.shape = shape;
self
}
pub fn with_density(mut self, density: Option<f32>) -> Self {
self.density = density;
self
}
pub fn with_restitution(mut self, restitution: f32) -> Self {
self.restitution = restitution;
self
}
pub fn with_friction(mut self, friction: f32) -> Self {
self.friction = friction;
self
}
pub fn with_sensor(mut self, sensor: bool) -> Self {
self.is_sensor = sensor;
self
}
pub fn with_solver_groups(mut self, solver_groups: InteractionGroups) -> Self {
self.solver_groups = solver_groups;
self
}
pub fn with_collision_groups(mut self, collision_groups: InteractionGroups) -> Self {
self.collision_groups = collision_groups;
self
}
pub fn with_friction_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
self.friction_combine_rule = rule;
self
}
pub fn with_restitution_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
self.restitution_combine_rule = rule;
self
}
pub fn build_collider(self) -> Collider {
Collider {
base: self.base_builder.build_base(),
shape: self.shape.into(),
friction: self.friction.into(),
density: self.density.into(),
restitution: self.restitution.into(),
is_sensor: self.is_sensor.into(),
collision_groups: self.collision_groups.into(),
solver_groups: self.solver_groups.into(),
friction_combine_rule: self.friction_combine_rule.into(),
restitution_combine_rule: self.restitution_combine_rule.into(),
native: Cell::new(ColliderHandle::invalid()),
}
}
pub fn build_node(self) -> Node {
Node::new(self.build_collider())
}
pub fn build(self, graph: &mut Graph) -> Handle<Collider> {
graph.add_node(self.build_node()).to_variant()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::core::algebra::Vector2;
use crate::scene::{
base::BaseBuilder,
collider::{ColliderBuilder, ColliderShape},
graph::Graph,
rigidbody::{RigidBodyBuilder, RigidBodyType},
};
#[test]
fn test_collider_intersect() {
let mut graph = Graph::new();
let mut create_rigid_body = |is_sensor| {
let cube_half_size = 0.5;
let collider_sensor = ColliderBuilder::new(BaseBuilder::new())
.with_shape(ColliderShape::cuboid(
cube_half_size,
cube_half_size,
cube_half_size,
))
.with_sensor(is_sensor)
.build(&mut graph);
RigidBodyBuilder::new(BaseBuilder::new().with_child(collider_sensor))
.with_body_type(RigidBodyType::Static)
.build(&mut graph);
collider_sensor
};
let collider_sensor = create_rigid_body(true);
let collider_non_sensor = create_rigid_body(false);
graph.update(Vector2::new(800.0, 600.0), 1.0, Default::default());
graph.update(Vector2::new(800.0, 600.0), 1.0, Default::default());
assert_eq!(0, graph[collider_sensor].contacts(&graph.physics).count());
assert_eq!(
0,
graph[collider_non_sensor].contacts(&graph.physics).count()
);
assert_eq!(1, graph[collider_sensor].intersects(&graph.physics).count());
assert_eq!(
1,
graph[collider_non_sensor]
.intersects(&graph.physics)
.count()
);
}
#[test]
fn test_bitmask_display() {
assert_eq!(
BitMask(1).to_string(),
"10000000 00000000 00000000 00000000"
);
assert_eq!(
BitMask(15).to_string(),
"11110000 00000000 00000000 00000000"
);
assert_eq!(
BitMask(16).to_string(),
"00001000 00000000 00000000 00000000"
);
assert_eq!(
BitMask(256).to_string(),
"00000000 10000000 00000000 00000000"
);
assert_eq!(
BitMask(1 << 31).to_string(),
"00000000 00000000 00000000 00000001"
);
}
#[test]
fn test_bitmask_debug() {
assert_eq!(format!("{:?}", BitMask(1)), "BitMask(00000001)");
assert_eq!(format!("{:?}", BitMask(15)), "BitMask(0000000f)");
assert_eq!(format!("{:?}", BitMask(16)), "BitMask(00000010)");
assert_eq!(format!("{:?}", BitMask(256)), "BitMask(00000100)");
assert_eq!(format!("{:?}", BitMask(1 << 31)), "BitMask(80000000)");
}
#[test]
fn test_bitmask_set_bit() {
let mut mask = BitMask::none();
mask.set_bit(0, true);
assert!(mask.bit(0));
assert_eq!(mask.to_string(), "10000000 00000000 00000000 00000000");
mask.set_bit(3, true);
assert!(mask.bit(3));
assert_eq!(mask.to_string(), "10010000 00000000 00000000 00000000");
mask.set_bit(0, false);
assert!(!mask.bit(0));
assert_eq!(mask.to_string(), "00010000 00000000 00000000 00000000");
}
#[test]
fn test_bitmask_with() {
let mask = BitMask::none().with(8);
assert_eq!(mask.to_string(), "00000000 10000000 00000000 00000000");
let mask = BitMask::all().without(8);
assert_eq!(mask.to_string(), "11111111 01111111 11111111 11111111");
let mask = BitMask::none().with(1).with(2).with(3);
assert_eq!(mask.to_string(), "01110000 00000000 00000000 00000000");
}
}