vox_geometry_rust/collider2.rs
1/*
2 * // Copyright (c) 2021 Feng Yang
3 * //
4 * // I am making my contributions/submissions to this project solely in my
5 * // personal capacity and am not conveying any rights to any intellectual
6 * // property of any third parties.
7 */
8
9use crate::vector2::Vector2D;
10use crate::surface2::*;
11use std::sync::{RwLock, Arc};
12
13/// Internal query result structure.
14pub struct ColliderQueryResult {
15 distance: f64,
16 point: Vector2D,
17 normal: Vector2D,
18 velocity: Vector2D,
19}
20
21impl ColliderQueryResult {
22 pub fn new() -> ColliderQueryResult {
23 return ColliderQueryResult {
24 distance: 0.0,
25 point: Vector2D::new_default(),
26 normal: Vector2D::new_default(),
27 velocity: Vector2D::new_default(),
28 };
29 }
30}
31
32pub struct Collider2Data {
33 _surface: Option<Surface2Ptr>,
34 _friction_coefficient: f64,
35}
36
37impl Collider2Data {
38 pub fn new(surface: Option<Surface2Ptr>) -> Collider2Data {
39 return Collider2Data {
40 _surface: surface,
41 _friction_coefficient: 0.0,
42 };
43 }
44}
45
46///
47/// # Abstract base class for generic collider object.
48///
49/// This class contains basic interfaces for colliders. Most of the
50/// functionalities are implemented within this class, except the member
51/// function Collider2::velocity_at. This class also let the subclasses to
52/// provide a Surface2 instance to define collider surface using
53/// Collider2::set_surface function.
54///
55pub trait Collider2 {
56 /// Returns the velocity of the collider at given \p point.
57 fn velocity_at(&self, point: &Vector2D) -> Vector2D;
58
59 /// Resolves collision for given point.
60 /// - Parameters:
61 /// - radius: Radius of the colliding point.
62 /// - restitution_coefficient: Defines the restitution effect.
63 /// - position: Input and output position of the point.
64 /// - velocity: Input and output velocity of the point.
65 fn resolve_collision(&self, radius: f64,
66 restitution_coefficient: f64,
67 new_position: &mut Vector2D,
68 new_velocity: &mut Vector2D) {
69 if !self.surface().read().unwrap().is_valid_geometry() {
70 return;
71 }
72
73 let mut collider_point = ColliderQueryResult::new();
74
75 self.get_closest_point(self.surface(), new_position, &mut collider_point);
76
77 // Check if the new position is penetrating the surface
78 if self.is_penetrating(&collider_point, new_position, radius) {
79 // Target point is the closest non-penetrating position from the
80 // new position.
81 let target_normal = collider_point.normal;
82 let target_point = collider_point.point + target_normal * radius;
83 let collider_vel_at_target_point = collider_point.velocity;
84
85 // Get new candidate relative velocity from the target point.
86 let relative_vel = *new_velocity - collider_vel_at_target_point;
87 let normal_dot_relative_vel = target_normal.dot(&relative_vel);
88 let mut relative_vel_n = target_normal * normal_dot_relative_vel;
89 let mut relative_vel_t = relative_vel - relative_vel_n;
90
91 // Check if the velocity is facing opposite direction of the surface
92 // normal
93 if normal_dot_relative_vel < 0.0 {
94 // Apply restitution coefficient to the surface normal component of
95 // the velocity
96 let delta_relative_vel_n = relative_vel_n * (-restitution_coefficient - 1.0);
97 relative_vel_n *= -restitution_coefficient;
98
99 // Apply friction to the tangential component of the velocity
100 // From Bridson et al., Robust Treatment of Collisions, Contact and
101 // Friction for Cloth Animation, 2002
102 // http://graphics.stanford.edu/papers/cloth-sig02/cloth.pdf
103 if relative_vel_t.length_squared() > 0.0 {
104 let friction_scale = f64::max(
105 1.0 - self.friction_coefficient() * delta_relative_vel_n.length() /
106 relative_vel_t.length(),
107 0.0);
108 relative_vel_t *= friction_scale;
109 }
110
111 // Reassemble the components
112 *new_velocity = relative_vel_n + relative_vel_t + collider_vel_at_target_point;
113 }
114
115 // Geometric fix
116 *new_position = target_point;
117 }
118 }
119
120 //----------------------------------------------------------------------------------------------
121 /// Returns friction coefficient.
122 fn friction_coefficient(&self) -> f64 {
123 return self.view()._friction_coefficient;
124 }
125
126 /// Sets the friction coefficient.
127 ///
128 /// This function assigns the friction coefficient to the collider. Any
129 /// negative inputs will be clamped to zero.
130 fn set_friction_coefficient(&mut self, new_friction_coefficient: f64) {
131 self.view_mut()._friction_coefficient = new_friction_coefficient;
132 }
133
134 /// Returns the surface instance.
135 fn surface(&self) -> Surface2Ptr {
136 return self.view()._surface.as_ref().unwrap().clone();
137 }
138
139 /// Updates the collider state.
140 fn update(&mut self, current_time_in_seconds: f64,
141 time_interval_in_seconds: f64);
142
143 /// Assigns the surface instance from the subclass.
144 fn set_surface(&mut self, new_surface: Surface2Ptr) {
145 self.view_mut()._surface = Some(new_surface);
146 }
147
148 /// Outputs closest point's information.
149 fn get_closest_point(&self, surface: Surface2Ptr,
150 query_point: &Vector2D,
151 result: &mut ColliderQueryResult) {
152 result.distance = surface.read().unwrap().closest_distance(query_point);
153 result.point = surface.read().unwrap().closest_point(query_point);
154 result.normal = surface.read().unwrap().closest_normal(query_point);
155 result.velocity = self.velocity_at(query_point);
156 }
157
158 /// Returns true if given point is in the opposite side of the surface.
159 fn is_penetrating(&self, collider_point: &ColliderQueryResult,
160 position: &Vector2D,
161 radius: f64) -> bool {
162 // If the new candidate position of the particle is inside
163 // the volume defined by the surface OR the new distance to the surface is
164 // less than the particle's radius, this particle is in colliding state.
165 return self.surface().read().unwrap().is_inside(position) || collider_point.distance < radius;
166 }
167
168 //----------------------------------------------------------------------------------------------
169 fn view(&self) -> &Collider2Data;
170
171 fn view_mut(&mut self) -> &mut Collider2Data;
172}
173
174/// Shared pointer type for the Collider2.
175pub type Collider2Ptr = Arc<RwLock<dyn Collider2 + Send + Sync>>;