gmod-rs/crates/render/src/texture.rs
Niko Marmeladkov b8b5a4c328 engine/render: M3 v0 — рендер мира BSP со скайбоксом
- crates/engine: сборка мира (браши, displacement'ы, лайтмапы в вершинах,
  материалы через VMT/patch/include, кэш текстур, пропуск прозрачных материалов),
  загрузка скайбокса из skyname
- crates/render: wgpu-рендерер мира (VB/IB, батчи, глубина), скайбокс-куб,
  камера-полёт, capture PNG с учётом BGRA, debug-режимы материалов
- client: команда map (диск/VPK, pakfile карты, WASD+мышь, --pos/--angles/--screenshot)
- тесты: gm_construct/gm_flatgrass собираются, 0 заглушек, 41 324 треугольника
- docs: прогресс M3 и заметки (BGRA, pak-VMT, z=w клиппинг)
2026-10-12 00:06:26 +03:00

157 lines
5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Загрузка текстур мира на GPU.
use engine::TextureData;
/// Создаёт 1x1 текстуру заданного цвета (debug-режим материалов).
pub fn upload_solid(
device: &wgpu::Device,
queue: &wgpu::Queue,
layout: &wgpu::BindGroupLayout,
sampler: &wgpu::Sampler,
color: [u8; 4],
) -> wgpu::BindGroup {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("debug material"),
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
});
queue.write_texture(
wgpu::TexelCopyTextureInfo {
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
&color,
wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(4),
rows_per_image: Some(1),
},
wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("debug material bind group"),
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(sampler),
},
],
})
}
/// Цвет палитры для индекса материала (debug-режим).
pub fn debug_palette_color(index: usize) -> [u8; 4] {
let hue = (index as f32 * 0.618_034) % 1.0;
let (r, g, b) = hsv_to_rgb(hue, 0.85, 1.0);
[r, g, b, 255]
}
fn hsv_to_rgb(h: f32, s: f32, v: f32) -> (u8, u8, u8) {
let i = (h * 6.0).floor();
let f = h * 6.0 - i;
let p = v * (1.0 - s);
let q = v * (1.0 - f * s);
let t = v * (1.0 - (1.0 - f) * s);
let (r, g, b) = match (i as i32) % 6 {
0 => (v, t, p),
1 => (q, v, p),
2 => (p, v, t),
3 => (p, q, v),
4 => (t, p, v),
_ => (v, p, q),
};
((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8)
}
/// Загружает текстуру (с мипами) и создаёт bind group материала.
pub fn upload_texture(
device: &wgpu::Device,
queue: &wgpu::Queue,
layout: &wgpu::BindGroupLayout,
sampler: &wgpu::Sampler,
texture: &TextureData,
) -> wgpu::BindGroup {
let width = texture.width.max(1);
let height = texture.height.max(1);
let mip_levels = texture.mips.len().max(1) as u32;
let gpu_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some(&texture.name),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: mip_levels,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
});
for (level, data) in texture.mips.iter().enumerate() {
let level = level as u32;
let mip_width = (width >> level).max(1);
let mip_height = (height >> level).max(1);
let expected = (mip_width * mip_height * 4) as usize;
if data.len() < expected {
break;
}
queue.write_texture(
wgpu::TexelCopyTextureInfo {
texture: &gpu_texture,
mip_level: level,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
&data[..expected],
wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(mip_width * 4),
rows_per_image: Some(mip_height),
},
wgpu::Extent3d {
width: mip_width,
height: mip_height,
depth_or_array_layers: 1,
},
);
}
let view = gpu_texture.create_view(&wgpu::TextureViewDescriptor::default());
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some(&texture.name),
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(sampler),
},
],
})
}