khora_core/physics/
mod.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! # Physics Abstractions
16//!
17//! Universal traits and types for physics simulation providers.
18
19pub mod collision;
20pub mod dynamic_tree;
21pub mod solver;
22
23pub use collision::*;
24pub use dynamic_tree::*;
25pub use solver::*;
26
27use bincode::{Decode, Encode};
28use serde::{Deserialize, Serialize};
29
30use crate::math::{LinearRgba, Quat, Vec3};
31
32/// Opaque handle to a rigid body in the physics engine.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode)]
34pub struct RigidBodyHandle(pub u64);
35
36/// Opaque handle to a collider in the physics engine.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode)]
38pub struct ColliderHandle(pub u64);
39
40/// Defines the type of a rigid body.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Encode, Decode)]
42pub enum BodyType {
43    /// Responds to forces and collisions.
44    Dynamic,
45    /// Fixed in place, does not move.
46    Static,
47    /// Controlled by the user, not by forces.
48    Kinematic,
49}
50
51/// Description for creating a rigid body.
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct RigidBodyDesc {
54    /// Initial position.
55    pub position: Vec3,
56    /// Initial rotation.
57    pub rotation: Quat,
58    /// Body type.
59    pub body_type: BodyType,
60    /// Linear velocity.
61    pub linear_velocity: Vec3,
62    /// Angular velocity.
63    pub angular_velocity: Vec3,
64    /// Mass of the body in kilograms.
65    pub mass: f32,
66    /// Whether to enable Continuous Collision Detection (CCD).
67    pub ccd_enabled: bool,
68}
69
70/// Description for creating a collider.
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct ColliderDesc {
73    /// Parent rigid body to attach to (if any).
74    pub parent_body: Option<RigidBodyHandle>,
75    /// Relative or absolute position.
76    pub position: Vec3,
77    /// Relative or absolute rotation.
78    pub rotation: Quat,
79    /// Shape definition.
80    pub shape: ColliderShape,
81    /// Whether to enable collision events for this collider.
82    pub active_events: bool,
83    /// Friction coefficient.
84    pub friction: f32,
85    /// Restitution (bounciness) coefficient.
86    pub restitution: f32,
87}
88
89/// Supported collider shapes.
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub enum ColliderShape {
92    /// Box with half-extents.
93    Box(Vec3),
94    /// Sphere with radius.
95    Sphere(f32),
96    /// Capsule with half-height and radius.
97    Capsule(f32, f32),
98}
99
100impl ColliderShape {
101    /// Computes the axis-aligned bounding box (AABB) for this shape in local space.
102    pub fn compute_aabb(&self) -> crate::math::Aabb {
103        match self {
104            ColliderShape::Box(half_extents) => crate::math::Aabb::from_half_extents(*half_extents),
105            ColliderShape::Sphere(radius) => {
106                crate::math::Aabb::from_half_extents(Vec3::new(*radius, *radius, *radius))
107            }
108            ColliderShape::Capsule(half_height, radius) => {
109                let r = Vec3::new(*radius, *radius, *radius);
110                let h = Vec3::new(0.0, *half_height, 0.0);
111                crate::math::Aabb::from_half_extents(r + h)
112            }
113        }
114    }
115}
116
117/// Interface contract for any physics engine implementation (e.g., Rapier).
118pub trait PhysicsProvider: Send + Sync {
119    /// Advances the simulation by `dt` seconds.
120    fn step(&mut self, dt: f32);
121
122    /// Sets the global gravity vector.
123    fn set_gravity(&mut self, gravity: Vec3);
124
125    /// Adds a rigid body to the simulation.
126    fn add_body(&mut self, desc: RigidBodyDesc) -> RigidBodyHandle;
127
128    /// Removes a rigid body from the simulation.
129    fn remove_body(&mut self, handle: RigidBodyHandle);
130
131    /// Adds a collider to the simulation.
132    fn add_collider(&mut self, desc: ColliderDesc) -> ColliderHandle;
133
134    /// Removes a collider from the simulation.
135    fn remove_collider(&mut self, handle: ColliderHandle);
136
137    /// Synchronizes the position and rotation of a rigid body.
138    fn get_body_transform(&self, handle: RigidBodyHandle) -> (Vec3, Quat);
139
140    /// Manually sets the position and rotation of a rigid body.
141    fn set_body_transform(&mut self, handle: RigidBodyHandle, pos: Vec3, rot: Quat);
142
143    /// Returns a list of all active rigid body handles.
144    fn get_all_bodies(&self) -> Vec<RigidBodyHandle>;
145
146    /// Returns a list of all active collider handles.
147    fn get_all_colliders(&self) -> Vec<ColliderHandle>;
148
149    /// Updates the properties of an existing rigid body.
150    fn update_body_properties(&mut self, handle: RigidBodyHandle, desc: RigidBodyDesc);
151
152    /// Updates the properties of an existing collider.
153    fn update_collider_properties(&mut self, handle: ColliderHandle, desc: ColliderDesc);
154
155    /// Returns debug rendering lines from the physics engine.
156    fn get_debug_render_data(&self) -> (Vec<Vec3>, Vec<[u32; 2]>);
157
158    /// Casts a ray into the physics world and returns the closest hit.
159    fn cast_ray(&self, ray: &Ray, max_toi: f32, solid: bool) -> Option<RaycastHit>;
160
161    /// Returns the collision events that occurred during the last step.
162    fn get_collision_events(&self) -> Vec<CollisionEvent>;
163
164    /// Resolves movement for a kinematic character controller.
165    /// Returns the actual translation applied and whether the character is grounded.
166    fn move_character(
167        &self,
168        collider: ColliderHandle,
169        desired_translation: Vec3,
170        options: &CharacterControllerOptions,
171    ) -> (Vec3, bool);
172}
173
174/// Options for resolving kinematic character movement.
175#[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode)]
176pub struct CharacterControllerOptions {
177    /// Max height of obstacles the character can step over.
178    pub autostep_height: f32,
179    /// Min width of obstacles for autostepping.
180    pub autostep_min_width: f32,
181    /// Whether autostepping is enabled.
182    pub autostep_enabled: bool,
183    /// Max angle for climbing slopes.
184    pub max_slope_climb_angle: f32,
185    /// Min angle for sliding down slopes.
186    pub min_slope_slide_angle: f32,
187    /// Distance to maintain from obstacles.
188    pub offset: f32,
189}
190
191/// Events representing collision start/end.
192#[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode)]
193pub enum CollisionEvent {
194    /// Collision between two colliders started.
195    Started(ColliderHandle, ColliderHandle),
196    /// Collision between two colliders stopped.
197    Stopped(ColliderHandle, ColliderHandle),
198}
199
200/// A ray in 3D space.
201#[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode)]
202pub struct Ray {
203    /// Origin point.
204    pub origin: Vec3,
205    /// Direction vector (should be normalized).
206    pub direction: Vec3,
207}
208
209/// Information about a raycast hit.
210#[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode)]
211pub struct RaycastHit {
212    /// The collider that was hit.
213    pub collider: ColliderHandle,
214    /// Distance from ray origin to hit point.
215    pub distance: f32,
216    /// Normal vector at the hit point.
217    pub normal: Vec3,
218    /// Exact position of the hit.
219    pub position: Vec3,
220}
221
222/// Detailed information about a contact between two colliders.
223#[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode)]
224pub struct ContactManifold {
225    /// Normal vector pointing from entity A to entity B.
226    pub normal: Vec3,
227    /// Intersection depth.
228    pub depth: f32,
229    /// Contact point in world space.
230    pub point: Vec3,
231}
232
233impl ContactManifold {
234    /// Returns the inverted manifold (flipped normal).
235    pub fn inverted(&self) -> Self {
236        Self {
237            normal: -self.normal,
238            depth: self.depth,
239            point: self.point,
240        }
241    }
242}
243
244/// A simple line for debug rendering.
245#[derive(Debug, Clone, Copy)]
246pub struct DebugLine {
247    /// Start point.
248    pub start: Vec3,
249    /// End point.
250    pub end: Vec3,
251    /// Color.
252    pub color: LinearRgba,
253}