1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
use std::os::raw::*;
use xplm_sys;
/// A callback that can be called while X-Plane draws graphics
pub trait DrawCallback: 'static {
/// Draws
fn draw(&mut self);
}
impl<F> DrawCallback for F
where
F: 'static + FnMut(),
{
fn draw(&mut self) {
self()
}
}
/// Sets up a draw callback
pub struct Draw {
/// The callback to execute
_callback: Box<dyn DrawCallback>,
/// The draw phase (used when unregistering)
phase: Phase,
/// The callback pointer (used when unregistering)
callback_ptr: *mut c_void,
/// The C callback (used when unregistering)
c_callback: xplm_sys::XPLMDrawCallback_f,
}
impl Draw {
/// Creates a new drawing callback
pub fn new<C: DrawCallback>(phase: Phase, callback: C) -> Result<Self, Error> {
let xplm_phase = phase.to_xplm();
let callback_box = Box::new(callback);
let callback_ptr: *const _ = &*callback_box;
let status = unsafe {
xplm_sys::XPLMRegisterDrawCallback(
Some(draw_callback::<C>),
xplm_phase,
0,
callback_ptr as *mut _,
)
};
if status == 1 {
Ok(Draw {
_callback: callback_box,
phase,
callback_ptr: callback_ptr as *mut _,
c_callback: Some(draw_callback::<C>),
})
} else {
Err(Error::UnsupportedPhase(phase))
}
}
}
impl Drop for Draw {
/// Unregisters this draw callback
fn drop(&mut self) {
let phase = self.phase.to_xplm();
unsafe {
xplm_sys::XPLMUnregisterDrawCallback(self.c_callback, phase, 0, self.callback_ptr);
}
}
}
/// The draw callback provided to X-Plane
///
/// This is instantiated separately for each callback type.
unsafe extern "C" fn draw_callback<C: DrawCallback>(
_phase: xplm_sys::XPLMDrawingPhase,
_before: c_int,
refcon: *mut c_void,
) -> c_int {
let callback_ptr = refcon as *mut C;
(*callback_ptr).draw();
// Always allow X-Plane to draw
1
}
/// Phases in which drawing can occur
#[derive(Debug, Copy, Clone)]
pub enum Phase {
// TODO: Some phases have been removed because they were removed from the upstream X-Plane SDK.
// The replacements should be added back in.
AfterPanel,
/// After X-Plane draws panel gauges
AfterGauges,
/// After X-Plane draws user interface windows
AfterWindows,
/// After X-Plane draws 3D content in the local map window
AfterLocalMap3D,
/// After X-Plane draws 2D content in the local map window
AfterLocalMap2D,
/// After X-Plane draws 2D content in the local map profile view
AfterLocalMapProfile,
}
impl Phase {
/// Converts this phase into an XPLMDrawingPhase and a 0 for after or 1 for before
fn to_xplm(&self) -> xplm_sys::XPLMDrawingPhase {
use self::Phase::*;
let phase = match *self {
AfterPanel => xplm_sys::xplm_Phase_Panel,
AfterGauges => xplm_sys::xplm_Phase_Gauges,
AfterWindows => xplm_sys::xplm_Phase_Window,
AfterLocalMap2D => xplm_sys::xplm_Phase_LocalMap2D,
AfterLocalMap3D => xplm_sys::xplm_Phase_LocalMap3D,
AfterLocalMapProfile => xplm_sys::xplm_Phase_LocalMapProfile,
};
phase as xplm_sys::XPLMDrawingPhase
}
}
/// Errors that can occur when creating a draw callback
#[derive(thiserror::Error, Debug)]
pub enum Error {
/// X-Plane does not support the provided phase
#[error("Unsupported draw phase: {0:?}")]
UnsupportedPhase(Phase),
}
/// Stores various flags that can be enabled or disabled
#[derive(Debug, Clone)]
pub struct GraphicsState {
/// Enable status of fog
///
/// During 3-d rendering fog is set up to cause a fade-to-fog effect at the visibility limit.
pub fog: bool,
/// Enable status of 3D lighting
pub lighting: bool,
/// Enable status of alpha testing
///
/// Alpha testing stops pixels from being rendered to the frame buffer if their alpha is zero.
pub alpha_testing: bool,
/// Enable status of alpha blending
pub alpha_blending: bool,
/// Enable status of depth testing
pub depth_testing: bool,
/// Enable status of depth writing
pub depth_writing: bool,
/// The number of textures that are enabled for use
pub textures: i32,
}
/// Sets the graphics state
pub fn set_state(state: &GraphicsState) {
unsafe {
xplm_sys::XPLMSetGraphicsState(
state.fog as i32,
state.textures,
state.lighting as i32,
state.alpha_testing as i32,
state.alpha_blending as i32,
state.depth_testing as i32,
state.depth_writing as i32,
);
}
}
/// Binds a texture ID to a texture number
///
/// This function should be used instead of glBindTexture
pub fn bind_texture(texture_number: i32, texture_id: i32) {
unsafe {
xplm_sys::XPLMBindTexture2d(texture_number, texture_id);
}
}
/// Generates texture numbers in a range not reserved for X-Plane.
///
/// This function should be used instead of glGenTextures.
///
/// Texture IDs are placed in the provided slice. If the slice contains more than i32::max_value()
/// elements, no more than i32::max_value() texture IDs will be generated.
pub fn generate_texture_numbers(numbers: &mut [i32]) {
let count = if numbers.len() < (i32::max_value() as usize) {
numbers.len() as i32
} else {
i32::max_value()
};
unsafe {
xplm_sys::XPLMGenerateTextureNumbers(numbers.as_mut_ptr(), count);
}
}
///
/// Generates a single texture number
///
/// See generate_texture_numbers for more detail.
///
pub fn generate_texture_number() -> i32 {
let number = 0;
generate_texture_numbers(&mut [number]);
number
}