1use std::error::Error;
9use std::ops::{AddAssign, Mul};
10use std::{fmt, mem, u16};
11
12use crate::{Event, EventType, GamepadId};
13
14use super::base_effect::{BaseEffect, BaseEffectType};
15use super::time::{Repeat, Ticks};
16
17use vec_map::VecMap;
18
19#[derive(Clone, Copy, Debug, Default, PartialEq)]
28pub enum DistanceModel {
29 #[default]
31 None,
32 Linear {
34 ref_distance: f32,
35 rolloff_factor: f32,
36 max_distance: f32,
37 },
38 LinearClamped {
40 ref_distance: f32,
41 rolloff_factor: f32,
42 max_distance: f32,
43 },
44 Inverse {
46 ref_distance: f32,
47 rolloff_factor: f32,
48 },
49 InverseClamped {
51 ref_distance: f32,
52 rolloff_factor: f32,
53 max_distance: f32,
54 },
55 Exponential {
57 ref_distance: f32,
58 rolloff_factor: f32,
59 },
60 ExponentialClamped {
62 ref_distance: f32,
63 rolloff_factor: f32,
64 max_distance: f32,
65 },
66}
67
68impl DistanceModel {
69 fn attenuation(self, mut distance: f32) -> f32 {
70 match self {
75 DistanceModel::Linear {
76 ref_distance,
77 max_distance,
78 rolloff_factor,
79 } => {
80 distance = distance.min(max_distance);
81
82 1.0 - rolloff_factor * (distance - ref_distance) / (max_distance - ref_distance)
83 }
84 DistanceModel::LinearClamped {
85 ref_distance,
86 max_distance,
87 rolloff_factor,
88 } => {
89 distance = distance.max(ref_distance);
90 distance = distance.min(max_distance);
91
92 1.0 - rolloff_factor * (distance - ref_distance) / (max_distance - ref_distance)
93 }
94 DistanceModel::Inverse {
95 ref_distance,
96 rolloff_factor,
97 } => ref_distance / (ref_distance + rolloff_factor * (distance - ref_distance)),
98 DistanceModel::InverseClamped {
99 ref_distance,
100 max_distance,
101 rolloff_factor,
102 } => {
103 distance = distance.max(ref_distance);
104 distance = distance.min(max_distance);
105
106 ref_distance / (ref_distance + rolloff_factor * (distance - ref_distance))
107 }
108 DistanceModel::Exponential {
109 ref_distance,
110 rolloff_factor,
111 } => (distance / ref_distance).powf(-rolloff_factor),
112 DistanceModel::ExponentialClamped {
113 ref_distance,
114 max_distance,
115 rolloff_factor,
116 } => {
117 distance = distance.max(ref_distance);
118 distance = distance.min(max_distance);
119
120 (distance / ref_distance).powf(-rolloff_factor)
121 }
122 DistanceModel::None => 1.0,
123 }
124 }
125
126 pub(crate) fn validate(self) -> Result<(), DistanceModelError> {
127 let (ref_distance, rolloff_factor, max_distance) = match self {
128 DistanceModel::Inverse {
129 ref_distance,
130 rolloff_factor,
131 } => {
132 if ref_distance <= 0.0 {
133 return Err(DistanceModelError::InvalidModelParameter);
134 }
135
136 (ref_distance, rolloff_factor, 0.0)
137 }
138 DistanceModel::InverseClamped {
139 ref_distance,
140 max_distance,
141 rolloff_factor,
142 } => {
143 if ref_distance <= 0.0 {
144 return Err(DistanceModelError::InvalidModelParameter);
145 }
146
147 (ref_distance, rolloff_factor, max_distance)
148 }
149 DistanceModel::Linear {
150 ref_distance,
151 max_distance,
152 rolloff_factor,
153 } => {
154 if ref_distance == max_distance {
155 return Err(DistanceModelError::InvalidModelParameter);
156 }
157
158 (ref_distance, rolloff_factor, max_distance)
159 }
160 DistanceModel::LinearClamped {
161 ref_distance,
162 max_distance,
163 rolloff_factor,
164 } => {
165 if ref_distance == max_distance {
166 return Err(DistanceModelError::InvalidModelParameter);
167 }
168
169 (ref_distance, rolloff_factor, max_distance)
170 }
171 DistanceModel::Exponential {
172 ref_distance,
173 rolloff_factor,
174 } => {
175 if ref_distance <= 0.0 {
176 return Err(DistanceModelError::InvalidModelParameter);
177 }
178
179 (ref_distance, rolloff_factor, 0.0)
180 }
181 DistanceModel::ExponentialClamped {
182 ref_distance,
183 max_distance,
184 rolloff_factor,
185 } => {
186 if ref_distance <= 0.0 {
187 return Err(DistanceModelError::InvalidModelParameter);
188 }
189
190 (ref_distance, rolloff_factor, max_distance)
191 }
192 DistanceModel::None => (0.0, 0.0, 0.0),
193 };
194
195 if ref_distance < 0.0 {
196 Err(DistanceModelError::InvalidReferenceDistance)
197 } else if rolloff_factor < 0.0 {
198 Err(DistanceModelError::InvalidRolloffFactor)
199 } else if max_distance < 0.0 {
200 Err(DistanceModelError::InvalidMaxDistance)
201 } else {
202 Ok(())
203 }
204 }
205}
206
207#[derive(Copy, Clone, Debug, PartialEq, Eq)]
210#[non_exhaustive]
211pub enum DistanceModelError {
212 InvalidReferenceDistance,
214 InvalidRolloffFactor,
216 InvalidMaxDistance,
218 InvalidModelParameter,
220}
221
222impl Error for DistanceModelError {}
223
224impl fmt::Display for DistanceModelError {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 let s = match self {
227 DistanceModelError::InvalidReferenceDistance => "reference distance is < 0",
228 DistanceModelError::InvalidRolloffFactor => "rolloff factor is < 0",
229 DistanceModelError::InvalidMaxDistance => "max distance is < 0",
230 DistanceModelError::InvalidModelParameter => "possible divide by zero",
231 };
232
233 f.write_str(s)
234 }
235}
236
237#[derive(Copy, Clone, PartialEq, Eq, Debug)]
238pub(super) enum EffectState {
239 Playing { since: Ticks },
240 Stopped,
241}
242
243#[derive(Clone, PartialEq, Debug)]
244pub(crate) struct EffectSource {
245 base_effects: Vec<BaseEffect>,
246 pub(super) devices: VecMap<()>,
248 pub(super) repeat: Repeat,
249 pub(super) distance_model: DistanceModel,
250 pub(super) position: [f32; 3],
251 pub(super) gain: f32,
252 pub(super) state: EffectState,
253 pub(super) completion_events: Vec<Event>,
254}
255
256impl EffectSource {
257 pub(super) fn new(
258 base_effects: Vec<BaseEffect>,
259 devices: VecMap<()>,
260 repeat: Repeat,
261 dist_model: DistanceModel,
262 position: [f32; 3],
263 gain: f32,
264 ) -> Self {
265 EffectSource {
266 base_effects,
267 devices,
268 repeat,
269 distance_model: dist_model,
270 position,
271 gain,
272 state: EffectState::Stopped,
273 completion_events: vec![],
274 }
275 }
276
277 pub(super) fn combine_base_effects(&mut self, ticks: Ticks, actor_pos: [f32; 3]) -> Magnitude {
278 let ticks = match self.state {
279 EffectState::Playing { since } => {
280 debug_assert!(ticks >= since);
281 ticks - since
282 }
283 EffectState::Stopped => return Magnitude::zero(),
284 };
285
286 match self.repeat {
287 Repeat::For(max_dur) if ticks > max_dur => {
288 self.state = EffectState::Stopped;
289 self.devices.keys().for_each(|id| {
290 let event = Event::new(GamepadId(id), EventType::ForceFeedbackEffectCompleted);
291 self.completion_events.push(event);
292 });
293 }
294 _ => (),
295 }
296
297 let attenuation = self
298 .distance_model
299 .attenuation(self.position.distance(actor_pos))
300 * self.gain;
301 if attenuation < 0.05 {
302 return Magnitude::zero();
303 }
304
305 let mut final_magnitude = Magnitude::zero();
306 for effect in &self.base_effects {
307 match effect.magnitude_at(ticks) {
308 BaseEffectType::Strong { magnitude } => {
309 final_magnitude.strong = final_magnitude.strong.saturating_add(magnitude)
310 }
311 BaseEffectType::Weak { magnitude } => {
312 final_magnitude.weak = final_magnitude.weak.saturating_add(magnitude)
313 }
314 };
315 }
316 final_magnitude * attenuation
317 }
318
319 pub(super) fn flush_completion_events(&mut self) -> Vec<Event> {
320 mem::take(&mut self.completion_events)
321 }
322}
323
324#[derive(Copy, Clone, Debug)]
326pub(super) struct Magnitude {
327 pub strong: u16,
328 pub weak: u16,
329}
330
331impl Magnitude {
332 pub fn zero() -> Self {
333 Magnitude { strong: 0, weak: 0 }
334 }
335}
336
337impl Mul<f32> for Magnitude {
338 type Output = Magnitude;
339
340 fn mul(self, rhs: f32) -> Self::Output {
341 debug_assert!(rhs >= 0.0);
342 let strong = self.strong as f32 * rhs;
343 let strong = if strong > u16::MAX as f32 {
344 u16::MAX
345 } else {
346 strong as u16
347 };
348 let weak = self.weak as f32 * rhs;
349 let weak = if weak > u16::MAX as f32 {
350 u16::MAX
351 } else {
352 weak as u16
353 };
354 Magnitude { strong, weak }
355 }
356}
357
358impl AddAssign for Magnitude {
359 fn add_assign(&mut self, rhs: Magnitude) {
360 self.strong = self.strong.saturating_add(rhs.strong);
361 self.weak = self.weak.saturating_add(rhs.weak);
362 }
363}
364
365trait SliceVecExt {
366 type Base;
367
368 fn distance(self, from: Self) -> Self::Base;
369}
370
371impl SliceVecExt for [f32; 3] {
372 type Base = f32;
373
374 fn distance(self, from: Self) -> f32 {
375 ((from[0] - self[0]).powi(2) + (from[1] - self[1]).powi(2) + (from[2] - self[2]).powi(2))
376 .sqrt()
377 }
378}