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}