gizmo_physics/components/
physics_material.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
8pub enum CombineMode {
9 Average, Min, Max, #[default]
13 GeometricMean, }
15
16impl CombineMode {
17 pub fn combine(self, a: f32, b: f32) -> f32 {
18 match self {
19 CombineMode::Average => (a + b) * 0.5,
20 CombineMode::Min => a.min(b),
21 CombineMode::Max => a.max(b),
22 CombineMode::GeometricMean => (a * b).max(0.0).sqrt(), }
24 }
25}
26
27fn resolve_combine_mode(m1: CombineMode, m2: CombineMode) -> CombineMode {
28 match (m1, m2) {
29 (CombineMode::Max, _) | (_, CombineMode::Max) => CombineMode::Max,
30 (CombineMode::Min, _) | (_, CombineMode::Min) => CombineMode::Min,
31 (CombineMode::GeometricMean, _) | (_, CombineMode::GeometricMean) => {
32 CombineMode::GeometricMean
33 }
34 _ => CombineMode::Average,
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
43pub struct PhysicsMaterial {
44 pub static_friction: f32,
45 pub dynamic_friction: f32,
46 pub restitution: f32,
47 pub density: f32,
48 pub friction_combine: CombineMode,
49 pub restitution_combine: CombineMode,
50}
51
52impl Default for PhysicsMaterial {
53 fn default() -> Self {
54 Self {
55 static_friction: 0.6,
56 dynamic_friction: 0.5,
57 restitution: 0.3,
58 density: 1.0,
59 friction_combine: CombineMode::GeometricMean,
60 restitution_combine: CombineMode::Max,
61 }
62 }
63}
64
65impl PhysicsMaterial {
66 pub fn combine(a: &PhysicsMaterial, b: &PhysicsMaterial) -> CombinedMaterial {
68 let f_mode = resolve_combine_mode(a.friction_combine, b.friction_combine);
69 let r_mode = resolve_combine_mode(a.restitution_combine, b.restitution_combine);
70
71 CombinedMaterial {
72 static_friction: f_mode.combine(a.static_friction, b.static_friction),
73 dynamic_friction: f_mode.combine(a.dynamic_friction, b.dynamic_friction),
74 restitution: r_mode.combine(a.restitution, b.restitution),
75 density: CombineMode::Average.combine(a.density, b.density),
76 }
77 }
78
79 pub const RUBBER: Self = Self {
82 static_friction: 1.0,
83 dynamic_friction: 0.9,
84 restitution: 0.8,
85 density: 1.1,
86 friction_combine: CombineMode::Max,
87 restitution_combine: CombineMode::Max,
88 };
89
90 pub const ICE: Self = Self {
91 static_friction: 0.05,
92 dynamic_friction: 0.03,
93 restitution: 0.05,
94 density: 0.92,
95 friction_combine: CombineMode::Min,
96 restitution_combine: CombineMode::Min,
97 };
98
99 pub const METAL: Self = Self {
100 static_friction: 0.4,
101 dynamic_friction: 0.3,
102 restitution: 0.3,
103 density: 7.8,
104 friction_combine: CombineMode::GeometricMean,
105 restitution_combine: CombineMode::Average,
106 };
107
108 pub const WOOD: Self = Self {
109 static_friction: 0.5,
110 dynamic_friction: 0.4,
111 restitution: 0.4,
112 density: 0.6,
113 friction_combine: CombineMode::GeometricMean,
114 restitution_combine: CombineMode::Average,
115 };
116
117 pub const CONCRETE: Self = Self {
118 static_friction: 0.8,
119 dynamic_friction: 0.7,
120 restitution: 0.1,
121 density: 2.4,
122 friction_combine: CombineMode::GeometricMean,
123 restitution_combine: CombineMode::Min,
124 };
125
126 pub const GLASS: Self = Self {
127 static_friction: 0.2,
128 dynamic_friction: 0.15,
129 restitution: 0.6,
130 density: 2.5,
131 friction_combine: CombineMode::Min,
132 restitution_combine: CombineMode::Max,
133 };
134
135 pub const ASPHALT: Self = Self {
136 static_friction: 0.75,
137 dynamic_friction: 0.65,
138 restitution: 0.05,
139 density: 2.3,
140 friction_combine: CombineMode::GeometricMean,
141 restitution_combine: CombineMode::Min,
142 };
143
144 pub const SAND: Self = Self {
145 static_friction: 0.55,
146 dynamic_friction: 0.45,
147 restitution: 0.02,
148 density: 1.6,
149 friction_combine: CombineMode::Average,
150 restitution_combine: CombineMode::Min,
151 };
152}
153
154#[derive(Debug, Clone, Copy)]
156pub struct CombinedMaterial {
157 pub static_friction: f32,
158 pub dynamic_friction: f32,
159 pub restitution: f32,
160 pub density: f32,
161}
162
163gizmo_core::impl_component!(PhysicsMaterial);
164
165#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn test_combine_geometric_mean() {
175 let a = PhysicsMaterial {
176 static_friction: 0.9,
177 ..Default::default()
178 };
179 let b = PhysicsMaterial {
180 static_friction: 0.4,
181 ..Default::default()
182 };
183 let c = PhysicsMaterial::combine(&a, &b);
184 let expected = (0.9f32 * 0.4).sqrt();
185 assert!((c.static_friction - expected).abs() < 1e-5);
186 }
187
188 #[test]
189 fn test_rubber_ice_low_friction() {
190 let r = PhysicsMaterial::RUBBER;
191 let i = PhysicsMaterial::ICE;
192 let c = PhysicsMaterial::combine(&r, &i);
196 assert!(
197 c.dynamic_friction > 0.5,
198 "Rubber's Max mode dominates Ice's Min mode"
199 );
200 }
201
202 #[test]
203 fn test_restitution_max() {
204 let a = PhysicsMaterial {
205 restitution: 0.9,
206 restitution_combine: CombineMode::Max,
207 ..Default::default()
208 };
209 let b = PhysicsMaterial {
210 restitution: 0.2,
211 restitution_combine: CombineMode::Max,
212 ..Default::default()
213 };
214 let c = PhysicsMaterial::combine(&a, &b);
215 assert!((c.restitution - 0.9).abs() < 1e-5);
216 }
217}