Skip to main content

gizmo_physics/components/
physics_material.rs

1use serde::{Deserialize, Serialize};
2
3// ─────────────────────────────────────────────────────────────────────────────
4// Kombine Fonksiyon: iki malzemenin değerlerini nasıl birleştireceğimizi belirler
5// ─────────────────────────────────────────────────────────────────────────────
6
7#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
8pub enum CombineMode {
9    Average, // (a + b) / 2
10    Min,     // a.min(b)
11    Max,     // a.max(b)
12    #[default]
13    GeometricMean, // sqrt(a * b)  — geometric mean (sürtünme için ideal)
14}
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(), // geometric mean
23        }
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// ─────────────────────────────────────────────────────────────────────────────
39// PhysicsMaterial
40// ─────────────────────────────────────────────────────────────────────────────
41
42#[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    /// İki malzemenin temas özelliklerini birleştir
67    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    // ── Hazır Malzemeler ──────────────────────────────────────────────────────
80
81    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/// İki malzemenin birleşiminden elde edilen temas parametreleri
155#[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// ─────────────────────────────────────────────────────────────────────────────
166// Testler
167// ─────────────────────────────────────────────────────────────────────────────
168
169#[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        // Rubber has CombineMode::Max, Ice has CombineMode::Min.
193        // Due to priority Max > Min, the resolved mode is Max.
194        // So the dynamic friction is Max(0.9, 0.03) = 0.9
195        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}