#[cfg(feature = "XPLM210")]
use std::ffi::c_void;
use std::ffi::CString;
use std::os::raw::c_int;
use xplm_sys::{
xplm_ProbeHitTerrain, xplm_ProbeMissed, xplm_ProbeY, XPLMCreateProbe, XPLMDestroyProbe,
XPLMLoadObject, XPLMObjectRef, XPLMProbeInfo_t, XPLMProbeRef, XPLMProbeTerrainXYZ,
XPLMUnloadObject,
};
#[cfg(feature = "XPLM210")]
use xplm_sys::XPLMLoadObjectAsync;
#[cfg(feature = "XPLM300")]
use xplm_sys::{XPLMDegMagneticToDegTrue, XPLMDegTrueToDegMagnetic, XPLMGetMagneticVariation};
pub type Vec3 = (f32, f32, f32);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ProbeHit {
pub location: Vec3,
pub normal: Vec3,
pub velocity: Vec3,
pub is_wet: bool,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ProbeOutcome {
Hit(ProbeHit),
Missed,
Error,
}
pub struct TerrainProbe {
raw: XPLMProbeRef,
}
unsafe impl Send for TerrainProbe {}
impl TerrainProbe {
pub fn new() -> Self {
let raw = unsafe { XPLMCreateProbe(xplm_ProbeY) };
Self { raw }
}
pub fn probe_terrain(&self, x: f32, y: f32, z: f32) -> ProbeOutcome {
let mut info = XPLMProbeInfo_t {
structSize: std::mem::size_of::<XPLMProbeInfo_t>() as c_int,
locationX: 0.0,
locationY: 0.0,
locationZ: 0.0,
normalX: 0.0,
normalY: 0.0,
normalZ: 0.0,
velocityX: 0.0,
velocityY: 0.0,
velocityZ: 0.0,
is_wet: 0,
};
let result = unsafe { XPLMProbeTerrainXYZ(self.raw, x, y, z, &mut info) };
if result == xplm_ProbeHitTerrain {
ProbeOutcome::Hit(ProbeHit {
location: (info.locationX, info.locationY, info.locationZ),
normal: (info.normalX, info.normalY, info.normalZ),
velocity: (info.velocityX, info.velocityY, info.velocityZ),
is_wet: info.is_wet != 0,
})
} else if result == xplm_ProbeMissed {
ProbeOutcome::Missed
} else {
ProbeOutcome::Error
}
}
}
impl Default for TerrainProbe {
fn default() -> Self {
Self::new()
}
}
impl Drop for TerrainProbe {
fn drop(&mut self) {
unsafe { XPLMDestroyProbe(self.raw) }
}
}
#[cfg(feature = "XPLM300")]
pub fn magnetic_variation(latitude: f64, longitude: f64) -> f32 {
unsafe { XPLMGetMagneticVariation(latitude, longitude) }
}
#[cfg(feature = "XPLM300")]
pub fn true_to_magnetic(heading_degrees_true: f32) -> f32 {
unsafe { XPLMDegTrueToDegMagnetic(heading_degrees_true) }
}
#[cfg(feature = "XPLM300")]
pub fn magnetic_to_true(heading_degrees_magnetic: f32) -> f32 {
unsafe { XPLMDegMagneticToDegTrue(heading_degrees_magnetic) }
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct DrawInfo {
pub x: f32,
pub y: f32,
pub z: f32,
pub pitch: f32,
pub heading: f32,
pub roll: f32,
}
impl From<DrawInfo> for xplm_sys::XPLMDrawInfo_t {
fn from(d: DrawInfo) -> Self {
Self {
structSize: std::mem::size_of::<xplm_sys::XPLMDrawInfo_t>() as c_int,
x: d.x,
y: d.y,
z: d.z,
pitch: d.pitch,
heading: d.heading,
roll: d.roll,
}
}
}
pub struct Object {
pub(crate) raw: XPLMObjectRef,
}
unsafe impl Send for Object {}
impl Object {
pub fn load(path: &str) -> Option<Self> {
let c_path = CString::new(path).ok()?;
let raw = unsafe { XPLMLoadObject(c_path.as_ptr()) };
(!raw.is_null()).then_some(Self { raw })
}
#[cfg(feature = "XPLM210")]
pub fn load_async(path: &str, callback: impl FnOnce(Option<Self>) + 'static) -> bool {
let Ok(c_path) = CString::new(path) else {
return false;
};
let boxed: Box<AsyncCallback> = Box::new(callback);
let refcon = Box::into_raw(Box::new(boxed));
unsafe {
XPLMLoadObjectAsync(
c_path.as_ptr(),
Some(load_object_trampoline),
refcon as *mut c_void,
);
}
true
}
pub fn new_instance(&self, datarefs: &[&str]) -> Option<crate::instance::Instance> {
crate::instance::Instance::new(self, datarefs)
}
}
impl Drop for Object {
fn drop(&mut self) {
unsafe { XPLMUnloadObject(self.raw) }
}
}
#[cfg(feature = "XPLM210")]
type AsyncCallback = dyn FnOnce(Option<Object>) + 'static;
#[cfg(feature = "XPLM210")]
unsafe extern "C" fn load_object_trampoline(object: XPLMObjectRef, refcon: *mut c_void) {
crate::guard(|| {
let callback: Box<AsyncCallback> =
*unsafe { Box::from_raw(refcon as *mut Box<AsyncCallback>) };
let loaded = (!object.is_null()).then_some(Object { raw: object });
callback(loaded);
});
}