Skip to content

Commit

Permalink
🐛 fix issue with scripted refs and default distances
Browse files Browse the repository at this point in the history
  • Loading branch information
Roms1383 committed Dec 22, 2024
1 parent 3aaa8ad commit 8fb7a81
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 92 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ glam = "0.29"
# kira = { version = "0.9.6", features = ["serde"] }
kira = { git = "https://github.com/Roms1383/kira", branch = "feat/total-duration", features = ["serde"] }
rayon = "1.10"
red4ext-rs = { git = "https://github.com/Roms1383/red4ext-rs", branch = "chore/solo" }
red4ext-rs = { git = "https://github.com/jac3km4/red4ext-rs", rev = "ae1c7b3cbc247b48fa1a90e7a082bede6909d501" }
# red4ext-rs = { git = "https://github.com/Roms1383/red4ext-rs", branch = "chore/solo" }
# red4ext-rs-bindings = { git = "https://github.com/jac3km4/red4ext-rs-bindings", rev = "v0.5.0" }
serde = "1.0"
snafu = "0.8"
Expand Down
4 changes: 1 addition & 3 deletions crates/audioware/reds/Debug.reds
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,8 @@ public class AutoEmittersSystem extends ScriptableSystem {
let emitterID: EntityID;
let emitterCName: CName = evt.IsShiftDown() ? n"None" : n"DummyTest";
let tween = new LinearTween();
tween.duration = 0.0;
let ext = new AudioSettingsExt();
ext.fadeIn = tween;
ext.fadeIn = LinearTween.Immediate(2.0);
let settings = new EmitterSettings();
settings.persistUntilSoundsFinish = true;
Expand Down
28 changes: 3 additions & 25 deletions crates/audioware/src/abi/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::num::NonZeroU64;

use audioware_manifest::{Locale, PlayerGender, ScnDialogLineType, Validate};
use command::Command;
use crossbeam::channel::bounded;
Expand Down Expand Up @@ -289,38 +287,18 @@ impl SceneLifecycle for AudioSystemExt {
emitter_name: Opt<CName>,
emitter_settings: Ref<EmitterSettings>,
) -> bool {
use crate::engine::ToDistances;
if tag_name.as_str() == "None" || tag_name.as_str().is_empty() {
warns!("invalid tag name: \"{tag_name}\"");
return false;
}
let (sender, receiver) = bounded(0);
let mut emitter_settings_hash = None;
if !emitter_settings.is_null() {
use std::hash::Hash;
use std::hash::Hasher;
let mut hasher = ahash::AHasher::default();
if let Some(x) = unsafe { emitter_settings.fields() }.cloned() {
x.hash(&mut hasher);
emitter_settings_hash = Some(hasher.finish());
} else {
fails!("emitter settings fields should be available when not null");
}
}
let emitter_settings = emitter_settings.into_settings();
let emitter_settings = emitter_settings.into_settings_ext(entity_id.to_distances());
queue::notify(Lifecycle::RegisterEmitter {
tag_name,
entity_id,
emitter_name: emitter_name.into_option(),
emitter_settings: emitter_settings_hash.map(|x| {
(
// SAFETY: if emitter_settings_hash is Some, then emitter_settings is Some
emitter_settings.unwrap(),
unsafe {
// SAFETY: Hash implementation already makes sure that the hash is non-zero
NonZeroU64::new_unchecked(x)
},
)
}),
emitter_settings,
sender,
});
if let Ok(registered) = receiver.recv() {
Expand Down
197 changes: 136 additions & 61 deletions crates/audioware/src/abi/types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use std::time::Duration;
use std::{
hash::{Hash, Hasher},
ops::Not,
time::Duration,
};

use audioware_manifest::{Interpolation, Locale, LocaleExt, PlayerGender, Region, Settings};
use kira::{manager::backend::cpal::CpalBackend, tween::Easing};
Expand Down Expand Up @@ -78,10 +82,63 @@ pub trait ToSettings {
fn into_settings(self) -> Option<Self::Settings>;
}

impl<T> ToSettings for Ref<T>
where
T: ScriptClass + Clone + ToSettings,
Ref<T>: Clone,
{
type Settings = <T as ToSettings>::Settings;

fn into_settings(self) -> Option<Self::Settings> {
if self.is_null() {
return None;
}
unsafe { self.fields() }.cloned().unwrap().into_settings()
}
}

pub trait ToSettingsExt {
type Settings;
type Defaults;
fn into_settings_ext(self, defaults: Self::Defaults) -> Option<Self::Settings>;
}

impl<T> ToSettingsExt for Ref<T>
where
T: ScriptClass + Clone + ToSettingsExt,
Ref<T>: Clone,
{
type Settings = <T as ToSettingsExt>::Settings;
type Defaults = <T as ToSettingsExt>::Defaults;

fn into_settings_ext(self, defaults: Self::Defaults) -> Option<Self::Settings> {
if self.is_null() {
return None;
}
unsafe { self.fields() }
.cloned()
.unwrap()
.into_settings_ext(defaults)
}
}

pub trait ToRegion {
fn into_region(self) -> Option<Region>;
}

impl<T> ToRegion for Ref<T>
where
T: ScriptClass + Clone + ToRegion,
Ref<T>: Clone,
{
fn into_region(self) -> Option<Region> {
if self.is_null() {
return None;
}
unsafe { self.fields() }.cloned().unwrap().into_region()
}
}

pub trait ToInterpolation {
fn into_interpolation(self) -> Option<Interpolation>;
}
Expand Down Expand Up @@ -125,91 +182,109 @@ impl ToInterpolation for Ref<Tween> {
}
}

impl ToRegion for Ref<AudioRegion> {
impl ToRegion for AudioRegion {
fn into_region(self) -> Option<Region> {
if self.is_null() {
return None;
}
let AudioRegion { starts, ends } = unsafe { self.fields() }?.clone();
if starts == 0. && ends == 0. {
return None;
}
Some(Region {
starts: starts.ne(&0.).then_some(Duration::from_secs_f32(starts)),
ends: ends.ne(&0.).then_some(Duration::from_secs_f32(ends)),
starts: self
.starts
.ne(&0.)
.then_some(Duration::from_secs_f32(self.starts)),
ends: self
.ends
.ne(&0.)
.then_some(Duration::from_secs_f32(self.ends)),
})
}
}

impl ToSettings for Ref<AudioSettingsExt> {
impl ToSettings for AudioSettingsExt {
type Settings = Settings;
fn into_settings(self) -> Option<Self::Settings> {
if self.is_null() {
return None;
}
let AudioSettingsExt {
start_position,
region,
r#loop,
volume,
fade_in,
panning,
playback_rate,
affected_by_time_dilation,
} = unsafe { self.fields() }?.clone();
if let Err(e) = Duration::try_from_secs_f32(start_position) {
if let Err(e) = Duration::try_from_secs_f32(self.start_position) {
fails!("invalid start position: {e}");
return None;
}
Some(Settings {
start_time: Default::default(),
start_position: Some(Duration::from_secs_f32(start_position)),
region: region.into_region(),
r#loop: Some(r#loop),
volume: Some(volume as f64),
fade_in_tween: fade_in.into_interpolation(),
panning: Some(panning as f64),
playback_rate: Some(kira::sound::PlaybackRate::Factor(playback_rate as f64)),
affected_by_time_dilation: Some(affected_by_time_dilation),
start_position: Some(Duration::from_secs_f32(self.start_position)),
region: self.region.into_region(),
r#loop: Some(self.r#loop),
volume: Some(self.volume as f64),
fade_in_tween: self.fade_in.into_interpolation(),
panning: Some(self.panning as f64),
playback_rate: Some(kira::sound::PlaybackRate::Factor(self.playback_rate as f64)),
affected_by_time_dilation: Some(self.affected_by_time_dilation),
})
}
}

impl ToSettings for Ref<EmitterSettings> {
type Settings = kira::spatial::emitter::EmitterSettings;
fn into_settings(self) -> Option<Self::Settings> {
if self.is_null() {
impl ToSettingsExt for EmitterSettings {
type Settings = (
kira::spatial::emitter::EmitterSettings,
std::num::NonZero<u64>,
);
type Defaults = Option<kira::spatial::emitter::EmitterDistances>;
fn into_settings_ext(self, defaults: Self::Defaults) -> Option<Self::Settings> {
let mut state = ahash::AHasher::default();
let d = self
.distances
.is_null()
.not()
.then_some(unsafe { self.distances.fields() }.cloned())
.flatten();
d.hash(&mut state);
if !self.attenuation_function.is_null() {
if self.attenuation_function.is_a::<LinearTween>() {
let x = unsafe {
std::mem::transmute::<&Ref<Tween>, &Ref<LinearTween>>(
&self.attenuation_function,
)
};
let ty = unsafe { x.fields() }.unwrap();
Some(ty).hash(&mut state);
} else if self.attenuation_function.is_a::<ElasticTween>() {
let x = unsafe {
std::mem::transmute::<&Ref<Tween>, &Ref<ElasticTween>>(
&self.attenuation_function,
)
};
let ty = unsafe { x.fields() }.unwrap();
Some(ty).hash(&mut state);
} else {
fails!("invalid attenuation function");
None::<Tween>.hash(&mut state);
}
} else {
None::<Tween>.hash(&mut state);
}
let distances = self.distances.into_settings();
let attenuation_function = self.attenuation_function.into_easing();
self.enable_spatialization.hash(&mut state);
self.persist_until_sounds_finish.hash(&mut state);
let hash = state.finish();
if hash == 0 {
fails!("emitter settings hash should not be 0");
return None;
}
let EmitterSettings {
distances,
attenuation_function,
enable_spatialization,
persist_until_sounds_finish,
..
} = unsafe { self.fields() }?.clone();
Some(kira::spatial::emitter::EmitterSettings {
distances: distances.into_settings().unwrap_or_default(),
attenuation_function: attenuation_function.into_easing(),
enable_spatialization,
persist_until_sounds_finish,
})
Some((
kira::spatial::emitter::EmitterSettings {
distances: distances.unwrap_or(defaults.unwrap_or_default()),
attenuation_function,
enable_spatialization: self.enable_spatialization,
persist_until_sounds_finish: self.persist_until_sounds_finish,
},
// SAFETY: checked above
unsafe { std::num::NonZeroU64::new_unchecked(hash) },
))
}
}

impl ToSettings for Ref<EmitterDistances> {
impl ToSettings for EmitterDistances {
type Settings = kira::spatial::emitter::EmitterDistances;
fn into_settings(self) -> Option<Self::Settings> {
if self.is_null() {
return None;
}
let EmitterDistances {
min_distance,
max_distance,
} = unsafe { self.fields() }?.clone();
Some(kira::spatial::emitter::EmitterDistances {
min_distance,
max_distance,
min_distance: self.min_distance,
max_distance: self.max_distance,
})
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/audioware/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ use crate::{
AsAudioSystem, AsGameInstance, AsGameObjectExt, GameObject,
};

pub use scene::ToDistances;

pub mod eq;
pub mod queue;
pub mod state;
Expand Down
16 changes: 16 additions & 0 deletions crates/audioware/src/engine/scene/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ pub trait AsEntityExt {

impl AsEntityExt for Ref<Entity> {
fn get_emitter_distances(&self) -> Option<EmitterDistances> {
if self.is_null() {
return None;
}
if let Some(puppet) = self.clone().cast::<ScriptedPuppet>().as_ref() {
let s = match puppet.get_npc_type() {
GamedataNpcType::Device | GamedataNpcType::Drone | GamedataNpcType::Spiderbot => {
Expand Down Expand Up @@ -350,3 +353,16 @@ impl AsEntityExt for Ref<Entity> {
None
}
}

pub trait ToDistances {
fn to_distances(&self) -> Option<EmitterDistances>;
}

impl ToDistances for EntityId {
fn to_distances(&self) -> Option<EmitterDistances> {
use crate::types::AsGameInstance;
let game = GameInstance::new();
let entity = GameInstance::find_entity_by_id(game, *self);
entity.get_emitter_distances()
}
}
4 changes: 2 additions & 2 deletions crates/audioware/src/types/audioware/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ unsafe impl ScriptClass for EmitterDistances {

impl Hash for EmitterDistances {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
((self.min_distance * 100.) as u64).hash(state);
((self.max_distance * 100.) as u64).hash(state);
((self.min_distance * 100.).clamp(0., u64::MAX as f32) as u64).hash(state);
((self.max_distance * 100.).clamp(0., u64::MAX as f32) as u64).hash(state);
}
}

0 comments on commit 8fb7a81

Please sign in to comment.