use crate::sys;
use std::ffi::CString;
use std::os::raw::c_int;
use std::ptr;
const FSVFXID_NULL: sys::FsVfxId = 0xFFFF_FFFF_0000_0000_u64 as sys::FsVfxId;
pub type Vec3d = sys::FsVec3d;
pub type SimObjId = sys::FsSimObjId;
#[inline]
pub const fn vec3d(x: f64, y: f64, z: f64) -> Vec3d {
sys::FsVec3d { x, y, z }
}
#[derive(Debug, Clone)]
pub struct VfxParam {
pub name: String,
pub rpn: String,
}
impl VfxParam {
pub fn new(name: impl Into<String>, rpn: impl Into<String>) -> Self {
Self {
name: name.into(),
rpn: rpn.into(),
}
}
}
struct ParamBuffer {
_strings: Vec<(CString, CString)>,
entries: Vec<sys::FsVfxGraphParam>,
}
impl ParamBuffer {
fn new(params: &[VfxParam]) -> Option<Self> {
let mut strings = Vec::with_capacity(params.len());
let mut entries = Vec::with_capacity(params.len());
for p in params {
let name = CString::new(p.name.as_str()).ok()?;
let rpn = CString::new(p.rpn.as_str()).ok()?;
entries.push(sys::FsVfxGraphParam {
paramName: name.as_ptr(),
RPNExpression: rpn.as_ptr(),
});
strings.push((name, rpn));
}
Some(Self {
_strings: strings,
entries,
})
}
fn as_ffi(&mut self) -> (*mut sys::FsVfxGraphParam, c_int) {
if self.entries.is_empty() {
(ptr::null_mut(), 0)
} else {
(self.entries.as_mut_ptr(), self.entries.len() as c_int)
}
}
}
pub struct VfxInstance {
id: sys::FsVfxId,
}
unsafe impl Send for VfxInstance {}
impl VfxInstance {
pub fn spawn_in_world(
guid: &str,
lla: Vec3d,
pbh: Vec3d,
min_emission_time: Option<f32>,
params: &[VfxParam],
) -> Option<Self> {
let guid_c = CString::new(guid).ok()?;
let mut buf = ParamBuffer::new(params)?;
let (ptr_, len) = buf.as_ffi();
let id = unsafe {
sys::fsVfxSpawnInWorld(
guid_c.as_ptr(),
lla,
pbh,
min_emission_time.unwrap_or(-1.0),
ptr_,
len,
)
};
Self::from_raw(id)
}
pub fn spawn_on_sim_object(
guid: &str,
sim_obj: SimObjId,
node_name: Option<&str>,
offset: Vec3d,
pbh_offset: Vec3d,
min_emission_time: Option<f32>,
params: &[VfxParam],
) -> Option<Self> {
let guid_c = CString::new(guid).ok()?;
let node_c = match node_name {
Some(n) => Some(CString::new(n).ok()?),
None => None,
};
let node_ptr = node_c.as_ref().map_or(ptr::null(), |c| c.as_ptr());
let mut buf = ParamBuffer::new(params)?;
let (ptr_, len) = buf.as_ffi();
let id = unsafe {
sys::fsVfxSpawnOnSimObject(
guid_c.as_ptr(),
sim_obj,
node_ptr,
offset,
pbh_offset,
min_emission_time.unwrap_or(-1.0),
ptr_,
len,
)
};
Self::from_raw(id)
}
fn from_raw(id: sys::FsVfxId) -> Option<Self> {
if id == FSVFXID_NULL {
None
} else {
Some(Self { id })
}
}
#[inline]
pub fn id(&self) -> sys::FsVfxId {
self.id
}
pub fn play(&self) -> bool {
unsafe { sys::fsVfxPlayInstance(self.id) }
}
pub fn stop(&self) -> bool {
unsafe { sys::fsVfxStopInstance(self.id) }
}
pub fn is_playing(&self) -> bool {
unsafe { sys::fsVfxIsInstancePlaying(self.id) }
}
pub fn is_min_time_passed(&self) -> bool {
unsafe { sys::fsVfxIsMinTimePassed(self.id) }
}
pub fn is_valid(&self) -> bool {
unsafe { sys::fsVfxIsValid(self.id) }
}
pub fn set_offset(&self, offset: Vec3d) -> bool {
unsafe { sys::fsVfxSetOffset(self.id, offset) }
}
pub fn set_world_position(&self, lla: Vec3d) -> bool {
unsafe { sys::fsVfxSetWorldPosition(self.id, lla) }
}
pub fn set_rotation(&self, pbh: Vec3d) -> bool {
unsafe { sys::fsVfxSetRotation(self.id, pbh) }
}
pub fn leak(self) -> sys::FsVfxId {
let id = self.id;
std::mem::forget(self);
id
}
}
impl Drop for VfxInstance {
fn drop(&mut self) {
if self.id != FSVFXID_NULL {
unsafe { sys::fsVfxDestroyInstance(self.id) };
self.id = FSVFXID_NULL;
}
}
}