use crate::base::{Error, Result};
use crate::gfx::bitmap::Bitmap;
use crate::three::doc::Doc;
pub(super) enum TextureOutcome {
Decoded(Bitmap),
Skipped(String),
}
pub(super) fn decode_texture(
doc: &Doc,
tex_idx: usize,
bin: Option<&[u8]>,
) -> Result<TextureOutcome> {
let tex = doc
.textures
.get(tex_idx)
.ok_or_else(|| Error::Parse(format!("gltf: texture {tex_idx} out of range")))?;
let Some(src) = tex.source else {
return Ok(TextureOutcome::Skipped(format!(
"texture {tex_idx} has no source image; using baseColorFactor"
)));
};
let image = doc
.images
.get(src)
.ok_or_else(|| Error::Parse(format!("gltf: image {src} out of range")))?;
if let Some(uri) = &image.uri {
return Ok(TextureOutcome::Skipped(format!(
"external image uri {uri:?} not fetched (standalone engine); using baseColorFactor"
)));
}
match image.mime_type.as_deref() {
Some("image/png") | Some("image/jpeg") | None => {}
Some(other) => {
return Ok(TextureOutcome::Skipped(format!(
"{other} texture not decoded (PNG/JPEG only); using baseColorFactor"
)))
}
}
let Some(bv_idx) = image.buffer_view else {
return Ok(TextureOutcome::Skipped(format!(
"image {src} has neither bufferView nor uri; using baseColorFactor"
)));
};
let view = doc
.buffer_views
.get(bv_idx)
.ok_or_else(|| Error::Parse(format!("gltf: image bufferView {bv_idx} out of range")))?;
if view.buffer != 0 {
return Ok(TextureOutcome::Skipped(format!(
"image buffer {} is external; using baseColorFactor",
view.buffer
)));
}
let bin = bin.ok_or_else(|| {
Error::Parse("gltf: image references BIN but the GLB has no BIN chunk".into())
})?;
let start = view.byte_offset as u64;
let end = start
.checked_add(view.byte_length as u64)
.ok_or_else(|| Error::Parse("gltf: image bufferView range overflows".into()))?;
if end > bin.len() as u64 {
return Err(Error::Parse(format!(
"gltf: image bufferView runs past BIN ({} bytes)",
bin.len()
)));
}
let data = &bin[start as usize..end as usize];
let bmp = crate::gfx::decode_image(data)
.map_err(|e| Error::Parse(format!("gltf: embedded texture corrupt: {e}")))?;
Ok(TextureOutcome::Decoded(bmp))
}