Skip to content

Commit

Permalink
Embedded gltf textures
Browse files Browse the repository at this point in the history
Also tweaks blade-asset API to borrow choir's ExecutionContext.
  • Loading branch information
kvark committed Nov 7, 2023
1 parent b70036d commit 43a3aec
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 48 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ log = "0.4"
mint = "0.5"
naga = { version = "0.14", features = ["wgsl-in", "span", "validate"] }
profiling = "1"
slab = "0.4"
strum = { version = "0.25", features = ["derive"] }
web-sys = "0.3.60"

Expand Down
1 change: 1 addition & 0 deletions blade-asset/src/flat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ macro_rules! impl_basic {
impl_basic!(bool);
impl_basic!(u32);
impl_basic!(u64);
impl_basic!(usize);
impl_basic!(f32);

/*
Expand Down
16 changes: 16 additions & 0 deletions blade-asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ impl<T> Default for Slot<T> {
}
}

#[derive(Default)]
struct Inner {
result: Vec<u8>,
dependencies: Vec<PathBuf>,
Expand Down Expand Up @@ -143,6 +144,21 @@ impl<B: Baker> Cooker<B> {
}
}

/// Create a new container with no data, no path, and no hasher.
pub fn new_embedded() -> Self {
Self {
inner: Mutex::new(Inner::default()),
base_path: Default::default(),
_phantom: PhantomData,
}
}

pub fn extract_embedded(&self) -> Vec<u8> {
let mut inner = self.inner.lock().unwrap();
assert!(inner.dependencies.is_empty());
mem::take(&mut inner.result)
}

/// Return the base path of the asset.
pub fn base_path(&self) -> &Path {
&self.base_path
Expand Down
2 changes: 1 addition & 1 deletion blade-graphics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ ash-window = "0.12"
gpu-alloc = "0.6"
gpu-alloc-ash = "0.6"
naga = { workspace = true, features = ["spv-out"] }
slab = "0.4"
slab = { workspace = true }

[target.'cfg(any(gles, target_arch = "wasm32"))'.dependencies]
# Version contains `glGetProgramResource`
Expand Down
3 changes: 2 additions & 1 deletion blade-render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ repository = "https://github.com/kvark/blade"

[features]
default = ["asset"]
asset = ["gltf" , "base64", "exr", "mikktspace", "texpresso", "zune-core", "zune-jpeg", "zune-png", "zune-imageprocs"]
asset = ["gltf" , "base64", "exr", "mikktspace", "slab", "texpresso", "zune-core", "zune-jpeg", "zune-png", "zune-imageprocs"]

[dependencies]
base64 = { workspace = true, optional = true }
Expand All @@ -28,6 +28,7 @@ log = { workspace = true }
mikktspace = { package = "bevy_mikktspace", version = "0.10", optional = true }
mint = { workspace = true }
profiling = { workspace = true }
slab = { workspace = true, optional = true }
strum = { workspace = true }
texpresso = { version = "2.0", optional = true }
#zune-core = { version = "0.2", optional = true }
Expand Down
167 changes: 121 additions & 46 deletions blade-render/src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ use std::{
collections::hash_map::{Entry, HashMap},
fmt, hash, mem,
ops::Range,
path::PathBuf,

Check warning on line 6 in blade-render/src/model/mod.rs

View workflow job for this annotation

GitHub Actions / build (Linux, ubuntu-latest, x86_64-unknown-linux-gnu)

unused imports: `path::PathBuf`, `unimplemented`
ptr, str,
sync::{Arc, Mutex},
unimplemented,
};

const PRELOAD_TEXTURES: bool = false;
Expand Down Expand Up @@ -58,11 +60,19 @@ pub struct Model {
pub acceleration_structure: blade_graphics::AccelerationStructure,
}

#[derive(blade_macros::Flat, Default)]
struct TextureReference<'a> {
path: Cow<'a, [u8]>,
embedded_data: Cow<'a, [u8]>,
//Note: this isn't used for anything during deserialization
source_index: usize,
}

#[derive(blade_macros::Flat)]
struct CookedMaterial<'a> {
base_color_path: Cow<'a, [u8]>,
base_color: TextureReference<'a>,
base_color_factor: [f32; 4],
normal_path: Cow<'a, [u8]>,
normal: TextureReference<'a>,
transparent: bool,
}

Expand Down Expand Up @@ -295,6 +305,28 @@ struct PendingOperations {
blas_constructs: Vec<BlasConstruct>,
}

enum TextureSource {
Path(String, super::texture::Meta),
Embedded(
Option<choir::IdleTask>,
Arc<blade_asset::Cooker<super::texture::Baker>>,
),
}

impl TextureReference<'_> {
fn complete(&mut self, sources: &slab::Slab<TextureSource>) {
match sources.get(self.source_index) {
Some(&TextureSource::Embedded(_task, sub_cooker)) => {
self.embedded_data = Cow::Owned(sub_cooker.extract_embedded());
}
Some(&TextureSource::Path(ref relative, meta)) => {
self.path = Cow::Owned(relative.as_bytes().to_owned());
}
None => {}
}
}
}

pub struct Baker {
gpu_context: Arc<blade_graphics::Context>,
pending_operations: Mutex<PendingOperations>,
Expand Down Expand Up @@ -339,6 +371,54 @@ impl Baker {
}
}
}

fn add_texture(
&self,
texture: gltf::texture::Texture,
meta: super::texture::Meta,
data_buffers: &[Vec<u8>],
) -> TextureSource {
match texture.source().source() {
gltf::image::Source::View { view, mime_type } => {
let sub_cooker = Arc::new(blade_asset::Cooker::new_embedded());
let cooker = Arc::clone(&sub_cooker);
let baker = Arc::clone(&self.asset_textures.baker);
let buffer = &data_buffers[view.buffer().index()];
let data = buffer[view.offset()..view.offset() + view.length()].to_vec();
let (_, extension) = mime_type.split_once('/').unwrap();
let task = self
.asset_textures
.choir
.spawn("embedded cook")
.init(move |ec| {
blade_asset::Baker::cook(
baker.as_ref(),
&data,
extension,
meta,
cooker,
ec,
);
});
TextureSource::Embedded(Some(task), sub_cooker)
}
gltf::image::Source::Uri { uri, mime_type: _ } => {
let relative = if let Some(_rest) = uri.strip_prefix("data:") {
panic!("Data URL isn't supported for textures yet");
} else if let Some(rest) = uri.strip_prefix("file://") {
rest
} else if let Some(rest) = uri.strip_prefix("file:") {
rest
} else {
uri
};
if PRELOAD_TEXTURES {
self.asset_textures.load(relative, meta);
}
TextureSource::Path(relative.to_string(), meta)
}
}
}
}

impl blade_asset::Baker for Baker {
Expand Down Expand Up @@ -384,30 +464,8 @@ impl blade_asset::Baker for Baker {
}
buffers.push(data);
}
let mut texture_paths = Vec::new();
for texture in document.textures() {
let relative = match texture.source().source() {
gltf::image::Source::Uri { uri, .. } => {
if let Some(rest) = uri.strip_prefix("data:") {
let (_before, after) = rest.split_once(";base64,").unwrap();
let _data = ENCODING_ENGINE.decode(after).unwrap();
panic!("Data URL isn't supported here yet");
} else if let Some(rest) = uri.strip_prefix("file://") {
rest
} else if let Some(rest) = uri.strip_prefix("file:") {
rest
} else {
uri
}
}
gltf::image::Source::View { .. } => {
panic!("Embedded images are not supported yet")
}
};
let full = cooker.base_path().join(relative);
texture_paths.push(full.to_str().unwrap().to_string());
}

let mut sources = slab::Slab::new();
let mut model = CookedModel {
name: &[],
materials: Vec::new(),
Expand All @@ -416,30 +474,33 @@ impl blade_asset::Baker for Baker {
for g_material in document.materials() {
let pbr = g_material.pbr_metallic_roughness();
model.materials.push(CookedMaterial {
base_color_path: Cow::Owned(match pbr.base_color_texture() {
Some(info) => {
let path = &texture_paths[info.texture().index()];
if PRELOAD_TEXTURES {
self.asset_textures.load(path, META_BASE_COLOR);
}
path.as_bytes().to_vec()
}
None => Vec::new(),
}),
base_color: TextureReference {
source_index: match pbr.base_color_texture() {
Some(info) => sources.insert(self.add_texture(
info.texture(),
META_BASE_COLOR,
&buffers,
)),
None => !0,
},
..Default::default()
},
base_color_factor: pbr.base_color_factor(),
normal_path: Cow::Owned(match g_material.normal_texture() {
Some(info) => {
let path = &texture_paths[info.texture().index()];
if PRELOAD_TEXTURES {
self.asset_textures.load(path, META_BASE_COLOR);
}
path.as_bytes().to_vec()
}
None => Vec::new(),
}),
normal: TextureReference {
source_index: match pbr.base_color_texture() {
Some(info) => sources.insert(self.add_texture(
info.texture(),
META_NORMAL,
&buffers,
)),
None => !0,
},
..Default::default()
},
transparent: g_material.alpha_mode() != gltf::material::AlphaMode::Opaque,
});
}

let mut flattened_geos = Vec::new();
for g_scene in document.scenes() {
for g_node in g_scene.nodes() {
Expand Down Expand Up @@ -476,11 +537,25 @@ impl blade_asset::Baker for Baker {
geo.indices = Cow::Owned(indices);
},
);

let mut dependencies = vec![gen_tangents];
for (_, source) in sources.iter_mut() {
if let TextureSource::Embedded(ref mut task, _) = *source {
dependencies.push(task.take().unwrap())
}
}

let mut finish = exe_context.fork("finish").init(move |_| {
let model = Arc::into_inner(model_shared).unwrap().into_inner().unwrap();
for material in model.materials.iter_mut() {
material.base_color.complete(&sources);
material.normal.complete(&sources);
}
cooker.finish(model);
});
finish.depend_on(&gen_tangents);
for dependency in dependencies {
finish.depend_on(&dependency);
}
}
other => panic!("Unknown model extension: {}", other),
}
Expand Down

0 comments on commit 43a3aec

Please sign in to comment.