fn convert_image_to_rgba8(image: &gltf::image::Data, idx: usize, file_path: &str) -> Vec<u8> {
let (w, h) = (image.width as usize, image.height as usize);
let pixel_count = w * h;
match image.format {
gltf::image::Format::R8G8B8A8 => {
let expected = pixel_count * 4;
if image.pixels.len() >= expected {
image.pixels[..expected].to_vec()
} else {
let mut out = image.pixels.clone();
out.resize(expected, 255);
out
}
}
gltf::image::Format::R8G8B8 => {
let mut out = Vec::with_capacity(pixel_count * 4);
for chunk in image.pixels.chunks_exact(3) {
out.extend_from_slice(&[chunk[0], chunk[1], chunk[2], 255]);
}
out.resize(pixel_count * 4, 255);
out
}
gltf::image::Format::R8G8 => {
let mut out = Vec::with_capacity(pixel_count * 4);
for chunk in image.pixels.chunks_exact(2) {
out.extend_from_slice(&[chunk[0], chunk[1], 0, 255]);
}
out.resize(pixel_count * 4, 255);
out
}
gltf::image::Format::R8 => {
let mut out = Vec::with_capacity(pixel_count * 4);
for &lum in &image.pixels {
out.extend_from_slice(&[lum, lum, lum, 255]);
}
out.resize(pixel_count * 4, 255);
out
}
unknown => {
tracing::error!(
"[GLTF WARN] Unknown pixel format {unknown:?} on image {idx} in '{file_path}'. \
Falling back to RGBA8 with clamped copy."
);
let expected = pixel_count * 4;
let mut out = vec![0u8; expected];
for px in 0..pixel_count {
out[px * 4 + 3] = 255;
}
let copy_len = image.pixels.len().min(expected);
out[..copy_len].copy_from_slice(&image.pixels[..copy_len]);
out
}
}
}
pub(super) struct GpuImage {
#[allow(dead_code)]
texture: wgpu::Texture,
pub(super) view: wgpu::TextureView,
}
pub(super) fn classify_gltf_image_srgb(document: &gltf::Document, num_images: usize) -> Vec<bool> {
let mut is_srgb = vec![false; num_images];
let mut mark = |idx: usize| {
if idx < is_srgb.len() {
is_srgb[idx] = true;
}
};
for material in document.materials() {
let pbr = material.pbr_metallic_roughness();
if let Some(ti) = pbr.base_color_texture() {
mark(ti.texture().source().index());
}
if let Some(ti) = material.emissive_texture() {
mark(ti.texture().source().index());
}
}
is_srgb
}
pub(super) fn upload_gltf_images(
device: &wgpu::Device,
queue: &wgpu::Queue,
file_path: &str,
images: &[gltf::image::Data],
srgb_flags: &[bool],
) -> Vec<GpuImage> {
let mut out = Vec::with_capacity(images.len());
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("glTF mipmap encoder"),
});
let mut srgb_blitter: Option<crate::texture_quality::MipmapBlitter> = None;
let mut linear_blitter: Option<crate::texture_quality::MipmapBlitter> = None;
let mut recorded_any = false;
for (i, image) in images.iter().enumerate() {
let (width, height) = (image.width, image.height);
let rgba: Vec<u8> = convert_image_to_rgba8(image, i, file_path);
let texture_size = wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let format = if srgb_flags.get(i).copied().unwrap_or(true) {
wgpu::TextureFormat::Rgba8UnormSrgb
} else {
wgpu::TextureFormat::Rgba8Unorm
};
let mip_level_count = crate::texture_quality::mip_level_count(width, height);
let texture = device.create_texture(&wgpu::TextureDescriptor {
size: texture_size,
mip_level_count,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: crate::texture_quality::MIPPED_TEXTURE_USAGE,
label: Some(&format!("{file_path}_tex_{i}")),
view_formats: &[],
});
queue.write_texture(
wgpu::TexelCopyTextureInfo {
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
&rgba,
wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(4 * width),
rows_per_image: Some(height),
},
texture_size,
);
if mip_level_count > 1 {
let blitter = if format == wgpu::TextureFormat::Rgba8UnormSrgb {
&mut srgb_blitter
} else {
&mut linear_blitter
};
blitter
.get_or_insert_with(|| crate::texture_quality::MipmapBlitter::new(device, format))
.record(device, &mut encoder, &texture, mip_level_count);
recorded_any = true;
}
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
out.push(GpuImage { texture, view });
}
if recorded_any {
queue.submit(Some(encoder.finish()));
tracing::debug!(
images = out.len(),
"[Asset] glTF doku mip zincirleri üretildi"
);
}
out
}