summaryrefslogtreecommitdiff
path: root/graphics
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2025-10-18 13:21:40 -0400
committerFreya Murphy <freya@freyacat.org>2025-10-18 13:21:40 -0400
commit8e7268c661e8df25224c907ba68eeb5a9cc5ff11 (patch)
tree27db2f024ae85d95ca5ba7ead9e637ac3c9c9ee7 /graphics
parentgraphics: remove anyhow (it was not being used) (diff)
downloadDungeonCrawl-8e7268c661e8df25224c907ba68eeb5a9cc5ff11.tar.gz
DungeonCrawl-8e7268c661e8df25224c907ba68eeb5a9cc5ff11.tar.bz2
DungeonCrawl-8e7268c661e8df25224c907ba68eeb5a9cc5ff11.zip
graphics: add audio/texture subsytem
Diffstat (limited to 'graphics')
-rw-r--r--graphics/src/assets.rs70
-rw-r--r--graphics/src/lib.rs32
-rw-r--r--graphics/src/render.rs28
3 files changed, 110 insertions, 20 deletions
diff --git a/graphics/src/assets.rs b/graphics/src/assets.rs
new file mode 100644
index 0000000..3f2354a
--- /dev/null
+++ b/graphics/src/assets.rs
@@ -0,0 +1,70 @@
+//! The `assets` crate stores all audio and image assets that need to be
+//! loaded during runtime
+
+use std::error::Error;
+
+use raylib::{RaylibHandle, RaylibThread, audio::RaylibAudio};
+
+#[expect(dead_code)]
+type Sound = raylib::audio::Sound<'static>;
+
+/// The `AudioData` container initalizes the audio subsystem
+/// for raylib, leaks it (to gurentee audio is statically loaded),
+/// then loads all needed audio samples
+#[derive(Debug)]
+pub struct AudioData {}
+impl AudioData {
+ pub(crate) fn load() -> Result<Self, Box<dyn Error>> {
+ // Phantom handle to the raylib audio subsystem
+ // Raylib doesnt use a handle, but the rust bindings
+ // have one to ensure memory safety.
+ //
+ // We must leak this handle after allocating it,
+ // if we dont then all audio will be unloaded :(
+ //
+ // NOTE: would this cause issues if `AudioData::load` was
+ // called multiple times?
+ let _handle = Box::leak(Box::new(RaylibAudio::init_audio_device()?));
+
+ // TODO: load audio samples
+
+ //let example = handle.new_sound("example.ogg")?;
+
+ Ok(Self {})
+ }
+}
+
+/// The `ImageData` container loads all game sprites, and other images into memory.
+#[derive(Debug)]
+pub(crate) struct ImageData {}
+impl ImageData {
+ pub(crate) fn load(
+ _handle: &mut RaylibHandle,
+ _thread: &RaylibThread,
+ ) -> Result<Self, Box<dyn Error>> {
+ // TODO: load image data
+
+ //let example = handle.load_texture(&thread, "example.png");
+
+ Ok(Self {})
+ }
+}
+
+#[derive(Debug)]
+pub(crate) struct Assets {
+ /// Audio needs to be accessible outside of the renderer
+ pub(crate) audio: AudioData,
+ /// Images are only needed by the renderer
+ pub(crate) image: ImageData,
+}
+impl Assets {
+ pub(crate) fn load(
+ handle: &mut RaylibHandle,
+ thread: &RaylibThread,
+ ) -> Result<Self, Box<dyn Error>> {
+ let audio = AudioData::load()?;
+ let image = ImageData::load(handle, thread)?;
+
+ Ok(Self { audio, image })
+ }
+}
diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs
index 6e5eb91..1ca03b3 100644
--- a/graphics/src/lib.rs
+++ b/graphics/src/lib.rs
@@ -2,12 +2,15 @@
//! rendering using the `raylib` library.
use std::cell::RefCell;
+use std::error::Error;
use raylib::prelude::*;
+use crate::assets::{Assets, AudioData};
use crate::render::{FrameInfo, Renderer};
-pub mod render;
+mod assets;
+mod render;
/// The `KeyCode` type represents different keys being pressed on the users keyboard
pub use raylib::consts::KeyboardKey as KeyCode;
@@ -15,8 +18,11 @@ pub use raylib::consts::KeyboardKey as KeyCode;
/// The `Window` type represents the game window
#[derive(Debug)]
pub struct Window {
+ // core raylib handles
handle: RefCell<RaylibHandle>,
thread: RaylibThread,
+ // static assets
+ assets: Assets,
}
impl Window {
@@ -27,20 +33,25 @@ impl Window {
/// ```no_run
/// use graphics::Window;
///
- /// let window = Window::new(800, 600, "Dungeon Crawl");
+ /// let window = Window::new(800, 600, "Dungeon Crawl").unwrap();
/// ```
- pub fn new(width: i32, height: i32, title: &str) -> Self {
- let (handle, thread) = raylib::init()
+ pub fn new(width: i32, height: i32, title: &str) -> Result<Self, Box<dyn Error>> {
+ let (mut handle, thread) = raylib::init()
.size(width, height)
.title(title)
.resizable()
.log_level(TraceLogLevel::LOG_WARNING)
.vsync()
.build();
- Self {
+
+ // we will now initalize all assets
+ let assets = Assets::load(&mut handle, &thread)?;
+
+ Ok(Self {
handle: RefCell::new(handle),
thread,
- }
+ assets,
+ })
}
/// Returns if the window should be closed.
@@ -55,11 +66,11 @@ impl Window {
/// ```no_run
/// use graphics::Window;
///
- /// let mut window = Window::new(800, 600, "Dungeon Crawl");
+ /// let mut window = Window::new(800, 600, "Dungeon Crawl").unwrap();
/// let mut renderer = window.renderer();
/// ```
pub fn renderer(&mut self) -> Renderer<'_> {
- let info = FrameInfo::new(self.handle.get_mut());
+ let info = FrameInfo::new(self.handle.get_mut(), &self.assets.image);
let handle = self.handle.get_mut().begin_drawing(&self.thread);
Renderer::new(handle, info)
}
@@ -88,4 +99,9 @@ impl Window {
pub fn get_key_pressed(&self) -> Option<KeyCode> {
self.handle.borrow_mut().get_key_pressed()
}
+
+ /// Get audio data for the window
+ pub fn audio(&self) -> &AudioData {
+ &self.assets.audio
+ }
}
diff --git a/graphics/src/render.rs b/graphics/src/render.rs
index 8bde77d..d9b0473 100644
--- a/graphics/src/render.rs
+++ b/graphics/src/render.rs
@@ -8,21 +8,28 @@ use raylib::{
prelude::{RaylibDraw, RaylibDrawHandle, RaylibHandle},
};
+use crate::assets::ImageData;
+
/// The `FrameInfo` struct contains information about
/// the current frame being rendered.
-pub struct FrameInfo {
+pub struct FrameInfo<'a> {
/// Time in seconds since last frame drawn
pub delta: f32,
/// FPS for last frame drawn
pub fps: u32,
+ /// Loaded texture data
+ /// NOTE: remove `expect` once we start using image data!!
+ #[expect(dead_code)]
+ pub(crate) image: &'a ImageData,
}
-impl FrameInfo {
+impl<'a> FrameInfo<'a> {
/// Creates a new `FrameInfo` from the provided
/// `RaylibHandle`.
- pub fn new(handle: &RaylibHandle) -> Self {
+ pub(crate) fn new(handle: &RaylibHandle, image: &'a ImageData) -> Self {
Self {
delta: handle.get_frame_time(),
fps: handle.get_fps(),
+ image,
}
}
}
@@ -31,16 +38,16 @@ impl FrameInfo {
/// frame of the game. It is created per frame.
pub struct Renderer<'a> {
handle: RaylibDrawHandle<'a>,
- info: FrameInfo,
+ info: FrameInfo<'a>,
}
impl<'a> Renderer<'a> {
/// Creates the renderer for the current frame
- pub(crate) fn new(handle: RaylibDrawHandle<'a>, info: FrameInfo) -> Self {
+ pub(crate) fn new(handle: RaylibDrawHandle<'a>, info: FrameInfo<'a>) -> Self {
Self { handle, info }
}
/// Returns the info struct for the current frame
- pub fn info(&self) -> &FrameInfo {
+ pub fn info(&self) -> &FrameInfo<'a> {
&self.info
}
@@ -70,7 +77,7 @@ impl<'a> Renderer<'a> {
/// ```no_run
/// use dungeon::Dungeon;
/// use graphics::Window;
- /// let mut window = Window::new(800, 600, "Dungeon Crawl");
+ /// let mut window = Window::new(800, 600, "Dungeon Crawl").unwrap();
/// let mut renderer = window.renderer();
/// let dungeon = Dungeon::new();
/// renderer.draw_frame(&dungeon);
@@ -94,22 +101,19 @@ impl<'a> Renderer<'a> {
self.draw_fps();
}
- /// Draw game over screen
- pub fn draw_game_over(&mut self) {
- // TODO:
- }
-
/// Draw the player sprite
pub fn draw_player(&mut self, player: &Player) {
self.draw_entity(&player.entity);
}
/// Draws an entity
+ #[expect(clippy::unused_self)]
pub fn draw_entity(&mut self, _entity: &Entity) {
// TODO:
}
/// Draw dungeon tiles
+ #[expect(clippy::unused_self)]
pub fn draw_tiles(&mut self, _dungeon: &Dungeon) {
// TODO:
}