summary refs log tree commit diff
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2026-08-09 20:09:35 -0700
committerIrene Knapp <ireneista@irenes.space>2026-08-09 20:23:13 -0700
commitbbe9d5993bb922ad6b33a21c3557143cecaf960d (patch)
tree387488a0a6b35aa5ba1fef1adcdc400d0f4935b6
parent677b697eb18b9abb252eacff320aa48314075a0e (diff)
abstract over bundled assets
yay build script stuff :)

Force-Push: yes
Change-Id: I694651febb02f42dac904f0dfbf4089c33ef937e
-rw-r--r--build.rs128
-rw-r--r--src/files/obj.rs4
-rw-r--r--src/files/png.rs4
-rw-r--r--src/graphics/render.rs7
-rw-r--r--src/main.rs9
5 files changed, 129 insertions, 23 deletions
diff --git a/build.rs b/build.rs
index ce74854..a6c5756 100644
--- a/build.rs
+++ b/build.rs
@@ -1,8 +1,31 @@
+use std::collections::BTreeSet;
 use std::env;
-use std::fs;
-use std::path::Path;
+use std::fs::{ self, File };
+use std::io::Write;
+use std::path::{ Path, PathBuf };
 use std::process::Command;
 
+struct Error(String);
+impl std::fmt::Display for Error {
+  fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>)
+      -> std::result::Result<(), std::fmt::Error>
+  {
+    fmt.write_str(&self.0)
+  }
+}
+impl From<std::io::Error> for Error {
+  fn from(e: std::io::Error) -> Self {
+    Error(format!("System I/O error: {}", e))
+  }
+}
+impl From<std::path::StripPrefixError> for Error {
+  fn from(e: std::path::StripPrefixError) -> Self {
+    Error(format!("Can't strip path prefix: {}", e))
+  }
+}
+type Result<T> = std::result::Result<T, Error>;
+
+
 fn main() {
   match process_all() {
     Err(error) => println!("cargo-error={}", error),
@@ -11,18 +34,25 @@ fn main() {
 }
 
 
-fn process_all() -> std::io::Result<()> {
-  process_shaders()?;
-  process_textures()?;
-  process_models()?;
+fn process_all() -> Result<()> {
+  let mut asset_paths: BTreeSet<PathBuf> = BTreeSet::new();
+
+  process_shaders(&mut asset_paths)?;
+  process_textures(&mut asset_paths)?;
+  process_models(&mut asset_paths)?;
+
+  generate_asset_module(asset_paths)?;
 
   Ok(())
 }
 
 
-fn process_shaders() -> std::io::Result<()> {
+fn process_shaders(asset_paths: &mut BTreeSet<PathBuf>) -> Result<()>
+{
   let out_dir = env::var_os("OUT_DIR").unwrap();
 
+  fs::create_dir_all(Path::new(&out_dir).join("shaders/"))?;
+
   for input in fs::read_dir("shaders")? {
     let input_path = input?.path();
     if !input_path.is_file() {
@@ -38,14 +68,14 @@ fn process_shaders() -> std::io::Result<()> {
 
     let output_path
             = Path::new(&out_dir)
-                  .join(format!("{}.spv",
+                  .join(format!("shaders/{}.spv",
                         input_path.file_name().unwrap().to_str().unwrap()));
 
     let result = Command::new("glslang")
             .arg("-V")
             .arg(&input_path)
             .arg("-o")
-            .arg(output_path)
+            .arg(&output_path)
             .output()?;
     if !result.status.success() {
       println!("cargo::error=Failed to compile shader {}; details follow.",
@@ -56,13 +86,15 @@ fn process_shaders() -> std::io::Result<()> {
     }
 
     println!("cargo::rerun-if-changed={}", input_path.display());
+
+    asset_paths.insert(output_path.to_path_buf());
   }
 
   Ok(())
 }
 
 
-fn process_textures() -> std::io::Result<()> {
+fn process_textures(asset_paths: &mut BTreeSet<PathBuf>) -> Result<()> {
   for input in fs::read_dir("textures")? {
     let input_path = input?.path();
     if !input_path.is_file() {
@@ -76,14 +108,26 @@ fn process_textures() -> std::io::Result<()> {
       continue;
     }
 
+    if let Some(Some(extension)) = input_path.extension()
+                                             .map(|a| a.to_str())
+    {
+      if extension != "png" {
+        continue;
+      }
+    } else {
+      continue;
+    }
+
     println!("cargo::rerun-if-changed={}", input_path.display());
+
+    asset_paths.insert(input_path.to_path_buf());
   }
 
   Ok(())
 }
 
 
-fn process_models() -> std::io::Result<()> {
+fn process_models(asset_paths: &mut BTreeSet<PathBuf>) -> Result<()> {
   for input in fs::read_dir("models")? {
     let input_path = input?.path();
     if !input_path.is_file() {
@@ -97,9 +141,71 @@ fn process_models() -> std::io::Result<()> {
       continue;
     }
 
+    if let Some(Some(extension)) = input_path.extension()
+                                             .map(|a| a.to_str())
+    {
+      if extension != "obj" {
+        continue;
+      }
+    } else {
+      continue;
+    }
+
     println!("cargo::rerun-if-changed={}", input_path.display());
+
+    asset_paths.insert(input_path.to_path_buf());
   }
 
   Ok(())
 }
 
+
+fn generate_asset_module(asset_paths: BTreeSet<PathBuf>) -> Result<()> {
+  let out_dir = env::var_os("OUT_DIR")
+                    .ok_or(Error("Don't have $OUT_DIR.".to_string()))?;
+  let output_path = Path::new(&out_dir).join("assets.rs");
+  let mut file = File::create(output_path)?;
+
+  let source_dir = env::current_dir()?;
+
+  println!("cargo::warning=assets {:?}", asset_paths);
+  writeln!(file, "#[deny(unsafe_code)]")?;
+  writeln!(file, "pub fn asset(name: &str) -> crate::error::Result<&[u8]> {{");
+  writeln!(file, "  match name {{");
+
+  for asset_path in asset_paths {
+    let name = if asset_path.starts_with(&out_dir) {
+      asset_path.strip_prefix(&out_dir)?
+    } else {
+      &asset_path
+    }.to_str()
+     .ok_or(Error("Can't stringify path - weird filesystem?".to_string()))?;
+
+    let include_path = if asset_path.is_absolute() {
+      asset_path.to_path_buf()
+    } else {
+      Path::new(&source_dir).join(&asset_path).to_path_buf()
+    };
+    let mut include_path = include_path.to_path_buf();
+    include_path = include_path.strip_prefix(&source_dir)?.to_path_buf();
+    for _ in Path::new(&out_dir).strip_prefix(&source_dir)?.components() {
+      include_path = Path::new("../").join(include_path).to_path_buf();
+    }
+    let include_path = include_path
+            .to_str()
+            .ok_or(Error("Can't stringify path - weird filesystem?"
+                             .to_string()))?;
+
+    writeln!(file, "    \"{}\" => Ok(include_bytes!(\"{}\")),",
+             name.escape_default(),
+             include_path.escape_default());
+  }
+
+  writeln!(file, "    _ => Err(crate::error::Error {{");
+  writeln!(file, "      message: format!(\"No such asset {{:?}}\", name)");
+  writeln!(file, "    }})");
+  writeln!(file, "  }}");
+  writeln!(file, "}}");
+
+  Ok(())
+}
diff --git a/src/files/obj.rs b/src/files/obj.rs
index af2ce03..3ccc2ec 100644
--- a/src/files/obj.rs
+++ b/src/files/obj.rs
@@ -9,9 +9,7 @@ use std::io::Cursor;
 use tobj::{ load_obj_buf, LoadOptions };
 
 
-pub fn load_obj(permanent: &Permanent) -> Result<Model> {
-  let obj = include_bytes!("../../models/teapot.obj");
-
+pub fn load_obj(obj: &[u8], permanent: &Permanent) -> Result<Model> {
   let load_options = LoadOptions {
     single_index: true,
     triangulate: true,
diff --git a/src/files/png.rs b/src/files/png.rs
index c164613..ce24292 100644
--- a/src/files/png.rs
+++ b/src/files/png.rs
@@ -7,9 +7,7 @@ use std::io::Cursor;
 use png::Decoder;
 
 
-pub fn load_png(permanent: &Permanent) -> Result<(Texture, u32)> {
-  let png = include_bytes!("../../textures/forest_leaves_04_diff.png");
-
+pub fn load_png(png: &[u8], permanent: &Permanent) -> Result<(Texture, u32)> {
   let decoder = Decoder::new(Cursor::new(png));
   let mut reader = decoder.read_info()?;
 
diff --git a/src/graphics/render.rs b/src/graphics/render.rs
index 0b4a12a..7318574 100644
--- a/src/graphics/render.rs
+++ b/src/graphics/render.rs
@@ -1,5 +1,6 @@
 #![deny(unsafe_code)]
 use crate::error::*;
+use crate::assets::asset;
 use crate::graphics::{ Permanent, ForReinit, WindowDressing, Model, Texture };
 use crate::shader_data::{ Vertex, UniformBlock, VertexPushBlock };
 
@@ -260,10 +261,8 @@ fn init_pipeline(device: &Device,
                  render_pass: &vk::RenderPass)
     -> Result<(vk::PipelineLayout, vk::Pipeline)>
 {
-  let vertex_binary = include_bytes!(
-          concat!(env!("OUT_DIR"), "/shader.vert.spv"));
-  let fragment_binary = include_bytes!(
-          concat!(env!("OUT_DIR"), "/shader.frag.spv"));
+  let vertex_binary = asset("shaders/shader.vert.spv")?;
+  let fragment_binary = asset("shaders/shader.frag.spv")?;
 
   let vertex_module = Permanent::load_spirv_shader_module(
           device, vertex_binary)?;
diff --git a/src/main.rs b/src/main.rs
index b4f7970..607b0d8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,6 @@
 #![deny(unsafe_code)]
 use crate::error::*;
+use crate::assets::asset;
 use crate::graphics::{ Permanent, ForReinit, Render, Model, Texture };
 use crate::graphics::scene::generate_scene_commands;
 use crate::graphics::window_dressing::{
@@ -22,6 +23,7 @@ use winit::event::WindowEvent;
 use winit::event_loop::{ ActiveEventLoop, EventLoop };
 use winit::window::WindowId;
 
+mod assets { include!(concat!(env!("OUT_DIR"), "/assets.rs")); }
 mod error;
 mod files;
 mod graphics;
@@ -68,14 +70,17 @@ impl Surreality {
     let (permanent, for_reinit, enable_anisotropy, enable_swapchain)
         = Permanent::new(event_loop)?;
 
-    let (texture, mip_count) = load_png(&permanent)?;
+    let png = asset("textures/forest_leaves_04_diff.png")?;
+    let (texture, mip_count) = load_png(png, &permanent)?;
 
     if enable_swapchain.0 {
       let window_dressing = WindowDressing::new(&permanent, &for_reinit,
                                                 enable_anisotropy, mip_count)?;
       let mut render = Render::new(&permanent, &for_reinit, &window_dressing,
                                    &texture)?;
-      let model = load_obj(&permanent)?;
+
+      let obj = asset("models/teapot.obj")?;
+      let model = load_obj(obj, &permanent)?;
       render.set_model(model);
 
       *self.window_dressing.get_mut() = Some(window_dressing);