Skip to main content

goud_engine/sdk/
texture.rs

1//! # SDK Texture API
2//!
3//! Provides methods on [`GoudGame`](super::GoudGame) for texture operations
4//! including loading image files and destroying GPU texture resources.
5//!
6//! # Availability
7//!
8//! This module requires the `native` feature (desktop platform with OpenGL).
9
10use super::GoudGame;
11use crate::libs::graphics::backend::TextureOps;
12
13/// Invalid texture handle sentinel value.
14const INVALID_TEXTURE: u64 = u64::MAX;
15
16// =============================================================================
17// Texture Operations (annotated for FFI generation)
18// =============================================================================
19
20// NOTE: FFI wrappers are hand-written in ffi/texture.rs. The `#[goud_api]`
21// attribute is omitted here to avoid duplicate `#[no_mangle]` symbol conflicts.
22impl GoudGame {
23    /// Loads a texture from an image file and returns a packed u64 handle.
24    ///
25    /// Returns `u64::MAX` on error.
26    pub fn load(&mut self, path: &str) -> u64 {
27        use crate::libs::graphics::backend::types::{TextureFilter, TextureFormat, TextureWrap};
28
29        let backend = match self.render_backend.as_mut() {
30            Some(b) => b,
31            None => return INVALID_TEXTURE,
32        };
33
34        let img = match image::open(path) {
35            Ok(i) => i.to_rgba8(),
36            Err(_) => return INVALID_TEXTURE,
37        };
38
39        let width = img.width();
40        let height = img.height();
41        let data = img.into_raw();
42
43        match backend.create_texture(
44            width,
45            height,
46            TextureFormat::RGBA8,
47            TextureFilter::Linear,
48            TextureWrap::ClampToEdge,
49            &data,
50        ) {
51            Ok(handle) => ((handle.generation() as u64) << 32) | (handle.index() as u64),
52            Err(_) => INVALID_TEXTURE,
53        }
54    }
55
56    /// Destroys a texture and releases its GPU resources.
57    pub fn destroy(&mut self, texture: u64) -> bool {
58        use crate::libs::graphics::backend::types::TextureHandle;
59
60        let backend = match self.render_backend.as_mut() {
61            Some(b) => b,
62            None => return false,
63        };
64
65        let index = (texture & 0xFFFF_FFFF) as u32;
66        let generation = ((texture >> 32) & 0xFFFF_FFFF) as u32;
67        let handle = TextureHandle::new(index, generation);
68        backend.destroy_texture(handle)
69    }
70}
71
72// =============================================================================
73// Tests
74// =============================================================================
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::sdk::GameConfig;
80
81    #[test]
82    fn test_texture_load_headless() {
83        let mut game = GoudGame::new(GameConfig::default()).unwrap();
84        assert_eq!(game.load("nonexistent.png"), u64::MAX);
85    }
86
87    #[test]
88    fn test_texture_destroy_headless() {
89        let mut game = GoudGame::new(GameConfig::default()).unwrap();
90        assert!(!game.destroy(0));
91    }
92}