Skip to main content

Crate banished_gfx

Crate banished_gfx 

Source
Expand description

§banished-gfx

Rendering code that the game (apps/game) and the viewer (apps/viewer) share. It uses wgpu, so it runs on Vulkan, Metal, DirectX 12, OpenGL, and in browsers on WebGPU or WebGL 2.

PartWhat it does
UiRendererDraws the quads of banished-ui in one alpha-blended pipeline, in batches split by texture and clip rectangle
upload_image, upload_rgba, upload_cube, …Upload the game’s textures (ImageBuffer) to the GPU, with their mip levels
decode_levelsDecode every level of a texture to RGBA8, for GPUs without BC texture support
materialWhich ported shader a model’s material uses (MaterialKind), and the atlas of citizens’ outfits
modelThe draw ranges of a mesh: which material, shader, textures and node transform each range uses
skinGPU skinning: the vertex layout of skinned meshes, the bone palette and its WGSL

The game’s own scene renderer (terrain, water, models, shadows, particles) is in apps/game. This crate holds only what both programs need.

§Use it

[dependencies]
banished-gfx = { path = "crates/gfx" }
banished-ui = { path = "crates/ui" }
banished-formats = { path = "crates/formats" }
wgpu = "30"

§Draw the interface

Create the renderer once, with the format of your colour and depth targets. Each frame, pass the quads from Ui::draw. The renderer uploads the sprite sheets that the quads use the first time it sees them.

use banished_gfx::UiRenderer;
use banished_ui::{NodeId, Ui};

fn draw_ui<'p>(
    renderer: &'p mut UiRenderer,
    device: &wgpu::Device,
    queue: &wgpu::Queue,
    pass: &mut wgpu::RenderPass<'p>,
    ui: &Ui,
    root: NodeId,
    size: (u32, u32),
) {
    let quads = ui.draw(root);
    renderer.draw(device, queue, pass, ui, &quads, size);
}

fn make(device: &wgpu::Device) -> UiRenderer {
    UiRenderer::new(device, wgpu::TextureFormat::Bgra8UnormSrgb, wgpu::TextureFormat::Depth32Float)
}

set_external(device, id, view) registers an application texture for quads with Texture::External(id), for example a preview picture in a dialog. set_time drives the animated interface materials.

§Upload a texture

use banished_formats::pkg::Archive;
use banished_formats::types::image_buffer;

fn texture(
    device: &wgpu::Device,
    queue: &wgpu::Queue,
    archive: &Archive,
    reference: &str,
) -> Option<wgpu::TextureView> {
    let image = image_buffer(&archive.read_ref(reference).ok()?).ok()?;
    banished_gfx::upload_image(device, queue, reference, &image)
}

upload_image decodes every mip level to RGBA8 on the CPU (decode_levels), because WebGL 2 often has no support for BC-compressed textures, and uploads the result as an sRGB texture. upload_image_linear uploads without sRGB conversion; upload_cube uploads a cube map.

§Rules for code in this crate

  • It must work on WebGL 2: no storage buffers in vertex shaders, no base-vertex draws, the downlevel_webgl2_defaults limits.
  • In WGSL, do every texture sample before any discard.

Port a shader explains these rules.

§The API

The full API documentation is at https://tanepiper.github.io/unbanished/api/banished_gfx/index.html.

Modules§

material
Model material classification and the character outfit atlas, shared by the viewer and the game so both draw a material the same way.
model
CPU-side description of a GraphicsMesh’s draw ranges, shared by the viewer and the game: which material each range uses, which shader kind that is, its textures and its rigid node transform. GPU upload and skinning strategy stay with each app.
skin
GPU skinning shared by the viewer and the game: the ModelSkinLayout vertex attributes, the bone palette uniform and its WGSL. The original’s NormalSkin_*vs programs blend four bones from a palette of transforms (tc_transform, c41, four rows per bone).

Structs§

UiRenderer
Draws the quads of banished-ui with wgpu: one alpha-blended pipeline (and one for the logo material), in batches split by texture and clip rectangle. It uploads each sprite sheet the first time a quad uses it.

Functions§

decode_levels
Decode an ImageBuffer into RGBA8 levels: faces × mips (face-major, as stored).
upload_cube
Upload a cube-map ImageBuffer (6 faces, D3D order +X −X +Y −Y +Z −Z) as a cube view.
upload_image
Decode and upload an ImageBuffer with all its mip levels (sRGB).
upload_image_linear
Upload a 2D ImageBuffer (all mips) as linear RGBA8, the way the D3D9 renderer samples it.
upload_rgba
Upload RGBA8 mip levels as an sRGB texture.
upload_rgba_as
Upload 4-byte-per-texel mip levels in format.