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