Skip to main content

nightshade_api/
physics.rs

1//! Forces, raycasts, and collision events for entities spawned with a
2//! [`Body`](crate::prelude::Body).
3
4use nightshade::plugins::physics::joints::{
5    FixedJoint, JointAxisDirection, JointHandle, RevoluteJoint, RopeJoint, SpringJoint,
6    create_fixed_joint, create_revolute_joint, create_rope_joint, create_spring_joint,
7};
8use nightshade::prelude::*;
9
10/// Applies an instantaneous impulse to a dynamic entity. Good for jumps,
11/// knockback, and explosions.
12#[inline]
13pub fn push(world: &mut World, entity: Entity, impulse: Vec3) {
14    physics_world_apply_impulse(world.plugin_resource_mut::<PhysicsWorld>(), entity, impulse);
15}
16
17/// Sets a dynamic entity's linear velocity directly. Good for launching
18/// projectiles.
19#[inline]
20pub fn set_velocity(world: &mut World, entity: Entity, velocity: Vec3) {
21    physics_world_set_linear_velocity(
22        world.plugin_resource_mut::<PhysicsWorld>(),
23        entity,
24        velocity,
25    );
26}
27
28/// Casts a ray against all physics colliders and returns the closest hit.
29#[inline]
30pub fn raycast(
31    world: &World,
32    origin: Vec3,
33    direction: Vec3,
34    max_distance: f32,
35) -> Option<RaycastHit> {
36    physics_world_cast_ray(
37        world.plugin_resource::<PhysicsWorld>(),
38        origin,
39        direction,
40        max_distance,
41        None,
42    )
43}
44
45/// The collision events from the last physics step.
46#[inline]
47pub fn collisions(world: &World) -> &[CollisionEvent] {
48    physics_world_collision_events(world.plugin_resource::<PhysicsWorld>())
49}
50
51/// Welds two bodies together rigidly.
52#[inline]
53pub fn attach_fixed(world: &mut World, parent: Entity, child: Entity) -> Option<JointHandle> {
54    create_fixed_joint(world, parent, child, FixedJoint::new())
55}
56
57/// Hinges two bodies around an axis. Doors, levers, wheels.
58#[inline]
59pub fn attach_hinge(
60    world: &mut World,
61    parent: Entity,
62    child: Entity,
63    axis: JointAxisDirection,
64) -> Option<JointHandle> {
65    create_revolute_joint(world, parent, child, RevoluteJoint::new(axis))
66}
67
68/// Connects two bodies with a spring. Suspension, bouncy attachments.
69#[inline]
70pub fn attach_spring(
71    world: &mut World,
72    parent: Entity,
73    child: Entity,
74    rest_length: f32,
75    stiffness: f32,
76    damping: f32,
77) -> Option<JointHandle> {
78    create_spring_joint(
79        world,
80        parent,
81        child,
82        SpringJoint::new(rest_length, stiffness, damping),
83    )
84}
85
86/// Tethers two bodies with a maximum separation. Chains, leashes, pendulums.
87#[inline]
88pub fn attach_rope(
89    world: &mut World,
90    parent: Entity,
91    child: Entity,
92    max_distance: f32,
93) -> Option<JointHandle> {
94    create_rope_joint(world, parent, child, RopeJoint::new(max_distance))
95}
96
97/// Applies a continuous force to a dynamic entity for this step. Unlike [`push`]
98/// (an instantaneous impulse), this acts over time, for thrust, wind, or custom
99/// gravity.
100#[inline]
101pub fn apply_force(world: &mut World, entity: Entity, force: Vec3) {
102    physics_world_apply_force(world.plugin_resource_mut::<PhysicsWorld>(), entity, force);
103}
104
105/// Applies a continuous torque to a dynamic entity for this step, spinning it up
106/// over time.
107#[inline]
108pub fn apply_torque(world: &mut World, entity: Entity, torque: Vec3) {
109    physics_world_apply_torque(world.plugin_resource_mut::<PhysicsWorld>(), entity, torque);
110}
111
112/// Sets a dynamic entity's angular velocity directly, in radians per second
113/// about each axis.
114#[inline]
115pub fn set_angular_velocity(world: &mut World, entity: Entity, velocity: Vec3) {
116    physics_world_set_angular_velocity(
117        world.plugin_resource_mut::<PhysicsWorld>(),
118        entity,
119        velocity,
120    );
121}
122
123/// The entity's current linear velocity, if it has a body.
124#[inline]
125pub fn velocity(world: &World, entity: Entity) -> Option<Vec3> {
126    physics_world_linear_velocity(world.plugin_resource::<PhysicsWorld>(), entity)
127}
128
129/// The entity's current angular velocity, if it has a body.
130#[inline]
131pub fn angular_velocity(world: &World, entity: Entity) -> Option<Vec3> {
132    physics_world_angular_velocity(world.plugin_resource::<PhysicsWorld>(), entity)
133}
134
135/// Every entity whose collider overlaps a sphere, for proximity tests that do
136/// not need a physical contact.
137#[inline]
138pub fn overlap_sphere(world: &World, center: Vec3, radius: f32) -> Vec<Entity> {
139    physics_world_overlap_sphere(
140        world.plugin_resource::<PhysicsWorld>(),
141        center,
142        radius,
143        None,
144    )
145}
146
147/// Spawns a dynamic cylinder physics body of `half_height` and `radius` at
148/// `position`, with `mass` and a linear RGBA `color`. The shapes
149/// [`spawn_object`](crate::prelude::spawn_object) does not cover come through
150/// here and the collider setters below.
151pub fn spawn_cylinder_body(
152    world: &mut World,
153    position: Vec3,
154    half_height: f32,
155    radius: f32,
156    mass: f32,
157    color: [f32; 4],
158) -> Entity {
159    let material = nightshade::render::material::Material {
160        base_color: color,
161        ..Default::default()
162    };
163    nightshade::plugins::physics::commands::spawn_dynamic_physics_cylinder_with_material(
164        world,
165        position,
166        half_height,
167        radius,
168        mass,
169        material,
170    )
171}
172
173/// Sets the friction of an entity's collider after spawn, applied live to the
174/// running simulation. 0.0 is frictionless ice, 1.0 is grippy.
175pub fn set_friction(world: &mut World, entity: Entity, friction: f32) {
176    if let Some(collider) =
177        world.get_mut::<nightshade::plugins::physics::components::ColliderComponent>(entity)
178    {
179        collider.friction = friction;
180    }
181    physics_world_set_friction(
182        world.plugin_resource_mut::<PhysicsWorld>(),
183        entity,
184        friction,
185    );
186}
187
188/// Sets the restitution (bounciness) of an entity's collider after spawn,
189/// applied live. 0.0 is a dead thud, 1.0 is a perfect elastic bounce.
190pub fn set_restitution(world: &mut World, entity: Entity, restitution: f32) {
191    if let Some(collider) =
192        world.get_mut::<nightshade::plugins::physics::components::ColliderComponent>(entity)
193    {
194        collider.restitution = restitution;
195    }
196    physics_world_set_restitution(
197        world.plugin_resource_mut::<PhysicsWorld>(),
198        entity,
199        restitution,
200    );
201}
202
203/// Sets linear damping on a dynamic body after spawn, applied live. Higher
204/// values bleed off translational motion over time, like moving through fluid.
205pub fn set_linear_damping(world: &mut World, entity: Entity, damping: f32) {
206    if let Some(body) =
207        world.get_mut::<nightshade::plugins::physics::components::RigidBodyComponent>(entity)
208    {
209        body.linear_damping = damping;
210    }
211    physics_world_set_linear_damping(world.plugin_resource_mut::<PhysicsWorld>(), entity, damping);
212}
213
214/// Sets angular damping on a dynamic body after spawn, applied live. Higher
215/// values settle spin over time.
216pub fn set_angular_damping(world: &mut World, entity: Entity, damping: f32) {
217    if let Some(body) =
218        world.get_mut::<nightshade::plugins::physics::components::RigidBodyComponent>(entity)
219    {
220        body.angular_damping = damping;
221    }
222    physics_world_set_angular_damping(world.plugin_resource_mut::<PhysicsWorld>(), entity, damping);
223}
224
225/// Sets the mass of a dynamic body after spawn, applied live, in kilograms.
226pub fn set_mass(world: &mut World, entity: Entity, mass: f32) {
227    if let Some(body) =
228        world.get_mut::<nightshade::plugins::physics::components::RigidBodyComponent>(entity)
229    {
230        body.mass = mass;
231    }
232    physics_world_set_additional_mass(world.plugin_resource_mut::<PhysicsWorld>(), entity, mass);
233}
234
235/// Sets the per-body gravity multiplier after spawn, applied live. 1.0 is
236/// normal, 0.0 makes the body float, 2.0 doubles its fall, negative lifts it.
237pub fn set_gravity_scale(world: &mut World, entity: Entity, scale: f32) {
238    if let Some(body) =
239        world.get_mut::<nightshade::plugins::physics::components::RigidBodyComponent>(entity)
240    {
241        body.gravity_scale = scale;
242    }
243    physics_world_set_gravity_scale(world.plugin_resource_mut::<PhysicsWorld>(), entity, scale);
244}
245
246/// Turns the entity's collider into a sensor (trigger): it stops blocking
247/// movement and instead reports overlaps through [`collisions`], where the
248/// event's `is_sensor` field is true.
249pub fn make_sensor(world: &mut World, entity: Entity) {
250    if let Some(collider) =
251        world.get_mut::<nightshade::plugins::physics::components::ColliderComponent>(entity)
252    {
253        collider.is_sensor = true;
254    }
255}
256
257/// Sets the entity collider's collision groups as two bitmasks. `membership` is
258/// the set of layers it belongs to, `filter` is the set it collides with. Two
259/// colliders interact only when each one's membership intersects the other's
260/// filter.
261pub fn set_collision_groups(world: &mut World, entity: Entity, membership: u32, filter: u32) {
262    if let Some(collider) =
263        world.get_mut::<nightshade::plugins::physics::components::ColliderComponent>(entity)
264    {
265        collider.collision_groups =
266            nightshade::plugins::physics::types::InteractionGroups::new(membership, filter);
267    }
268}