rg3d_sound/source/
spatial.rs1use crate::{
31 context::DistanceModel,
32 listener::Listener,
33 source::{generic::GenericSource, SoundSource},
34};
35use rg3d_core::{
36 algebra::Vector3,
37 inspect::{Inspect, PropertyInfo},
38 visitor::{Visit, VisitResult, Visitor},
39};
40use std::ops::{Deref, DerefMut};
41
42#[derive(Debug, Clone, Inspect)]
44pub struct SpatialSource {
45 pub(in crate) generic: GenericSource,
46 #[inspect(min_value = 0.0, step = 0.05)]
47 radius: f32,
48 position: Vector3<f32>,
49 #[inspect(min_value = 0.0, step = 0.05)]
50 max_distance: f32,
51 #[inspect(min_value = 0.0, step = 0.05)]
52 rolloff_factor: f32,
53 #[inspect(skip)]
55 pub(in crate) prev_left_samples: Vec<f32>,
56 #[inspect(skip)]
57 pub(in crate) prev_right_samples: Vec<f32>,
58 #[inspect(skip)]
59 pub(in crate) prev_sampling_vector: Vector3<f32>,
60 #[inspect(skip)]
61 pub(in crate) prev_distance_gain: Option<f32>,
62}
63
64impl SpatialSource {
65 pub fn set_position(&mut self, position: Vector3<f32>) -> &mut Self {
67 self.position = position;
68 self
69 }
70
71 pub fn position(&self) -> Vector3<f32> {
73 self.position
74 }
75
76 pub fn set_radius(&mut self, radius: f32) -> &mut Self {
78 self.radius = radius;
79 self
80 }
81
82 pub fn radius(&self) -> f32 {
84 self.radius
85 }
86
87 pub fn set_rolloff_factor(&mut self, rolloff_factor: f32) -> &mut Self {
91 self.rolloff_factor = rolloff_factor;
92 self
93 }
94
95 pub fn rolloff_factor(&self) -> f32 {
97 self.rolloff_factor
98 }
99
100 pub fn set_max_distance(&mut self, max_distance: f32) -> &mut Self {
105 self.max_distance = max_distance;
106 self
107 }
108
109 pub fn max_distance(&self) -> f32 {
111 self.max_distance
112 }
113
114 pub fn generic(&self) -> &GenericSource {
116 &self.generic
117 }
118
119 pub fn generic_mut(&mut self) -> &mut GenericSource {
121 &mut self.generic
122 }
123
124 pub(in crate) fn get_distance_gain(
128 &self,
129 listener: &Listener,
130 distance_model: DistanceModel,
131 ) -> f32 {
132 let distance = self
133 .position
134 .metric_distance(&listener.position())
135 .max(self.radius)
136 .min(self.max_distance);
137 match distance_model {
138 DistanceModel::None => 1.0,
139 DistanceModel::InverseDistance => {
140 self.radius / (self.radius + self.rolloff_factor * (distance - self.radius))
141 }
142 DistanceModel::LinearDistance => {
143 1.0 - self.radius * (distance - self.radius) / (self.max_distance - self.radius)
144 }
145 DistanceModel::ExponentDistance => (distance / self.radius).powf(-self.rolloff_factor),
146 }
147 }
148
149 pub(in crate) fn get_panning(&self, listener: &Listener) -> f32 {
150 (self.position - listener.position())
151 .try_normalize(f32::EPSILON)
152 .unwrap_or_else(|| listener.look_axis())
155 .dot(&listener.ear_axis())
156 }
157
158 pub(in crate) fn get_sampling_vector(&self, listener: &Listener) -> Vector3<f32> {
159 let to_self = self.position - listener.position();
160
161 (listener.basis() * to_self)
162 .try_normalize(f32::EPSILON)
163 .unwrap_or_else(|| Vector3::new(0.0, 0.0, 1.0))
166 }
167}
168
169impl Deref for SpatialSource {
170 type Target = GenericSource;
171
172 fn deref(&self) -> &Self::Target {
173 &self.generic
174 }
175}
176
177impl DerefMut for SpatialSource {
178 fn deref_mut(&mut self) -> &mut Self::Target {
179 &mut self.generic
180 }
181}
182
183impl Visit for SpatialSource {
184 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
185 visitor.enter_region(name)?;
186
187 self.radius.visit("Radius", visitor)?;
188 self.position.visit("Position", visitor)?;
189
190 visitor.leave_region()
191 }
192}
193
194impl Default for SpatialSource {
195 fn default() -> Self {
196 Self {
197 generic: Default::default(),
198 radius: 1.0,
199 position: Vector3::new(0.0, 0.0, 0.0),
200 max_distance: f32::MAX,
201 rolloff_factor: 1.0,
202 prev_left_samples: Default::default(),
203 prev_right_samples: Default::default(),
204 prev_sampling_vector: Vector3::new(0.0, 0.0, 1.0),
205 prev_distance_gain: None,
206 }
207 }
208}
209
210pub struct SpatialSourceBuilder {
212 generic: GenericSource,
213 radius: f32,
214 position: Vector3<f32>,
215 max_distance: f32,
216 rolloff_factor: f32,
217}
218
219impl SpatialSourceBuilder {
220 pub fn new(generic: GenericSource) -> Self {
223 Self {
224 generic,
225 radius: 1.0,
226 position: Vector3::new(0.0, 0.0, 0.0),
227 max_distance: f32::MAX,
228 rolloff_factor: 1.0,
229 }
230 }
231
232 pub fn with_position(mut self, position: Vector3<f32>) -> Self {
234 self.position = position;
235 self
236 }
237
238 pub fn with_radius(mut self, radius: f32) -> Self {
240 self.radius = radius;
241 self
242 }
243
244 pub fn with_max_distance(mut self, max_distance: f32) -> Self {
246 self.max_distance = max_distance;
247 self
248 }
249
250 pub fn with_rolloff_factor(mut self, rolloff_factor: f32) -> Self {
252 self.rolloff_factor = rolloff_factor;
253 self
254 }
255
256 pub fn build(self) -> SpatialSource {
258 SpatialSource {
259 generic: self.generic,
260 radius: self.radius,
261 position: self.position,
262 max_distance: self.max_distance,
263 rolloff_factor: self.rolloff_factor,
264 prev_left_samples: Default::default(),
265 prev_right_samples: Default::default(),
266 ..Default::default()
267 }
268 }
269
270 pub fn build_source(self) -> SoundSource {
272 SoundSource::Spatial(self.build())
273 }
274}