swamp_render/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/piot/swamp-render
 * Licensed under the MIT License. See LICENSE in the project root for license information.
 */

use int_math::{URect, Vec2, Vec3};
use log::info;
use std::rc::Rc;
use std::sync::Arc;
use swamp_wgpu_sprites::SpriteInfo;
use wgpu::{BindGroup, BindGroupLayout, RenderPass, RenderPipeline};

#[derive(Debug)]
pub struct Render {
    index_buffer: wgpu::Buffer,
    vertex_buffer: wgpu::Buffer,

    device: Arc<wgpu::Device>,
    queue: Arc<wgpu::Queue>, // Queue to talk to device

    sprites: Vec<Sprite>,
    materials: Vec<SpriteMaterialRef>,
    bind_group_layout: BindGroupLayout,
    sampler: wgpu::Sampler,
    pipeline: RenderPipelineRef,
}

impl Render {
    pub fn new(
        device: Arc<wgpu::Device>,
        queue: Arc<wgpu::Queue>, // Queue to talk to device
        surface_texture_format: wgpu::TextureFormat,
        vertex_shader_source: &str,
        fragment_shader_source: &str,
    ) -> Self {
        let sprite_info = SpriteInfo::new(
            &device,
            surface_texture_format,
            vertex_shader_source,
            fragment_shader_source,
        );

        let index_buffer =
            swamp_wgpu_sprites::create_sprite_index_buffer(&device, "sprite quad index buffer");
        let vertex_buffer =
            swamp_wgpu_sprites::create_sprite_vertex_buffer(&device, "sprite quad vertex buffer");

        Self {
            device,
            queue,
            sprites: Vec::new(),
            materials: Vec::new(),
            sampler: sprite_info.sampler,
            pipeline: Rc::new(sprite_info.pipeline),
            bind_group_layout: sprite_info.bind_group_layout,
            index_buffer,
            vertex_buffer,
        }
    }

    pub fn render_sprite(
        &mut self,
        position: Vec3,
        material: &SpriteMaterialRef,
        params: SpriteParams,
    ) {
        self.sprites.push(Sprite {
            position,
            material: Rc::clone(material),
            params,
        })
    }

    pub fn render_sprite_2d(
        &mut self,
        position: Vec2,
        material: &SpriteMaterialRef,
        params: SpriteParams,
    ) {
        self.sprites.push(Sprite {
            position: position.into(),
            material: Rc::clone(material),
            params,
        })
    }

    pub fn render(&mut self, render_pass: &mut RenderPass) {
        sort_sprites_by_z_then_y(&mut self.sprites);

        render_pass.set_pipeline(&self.pipeline);

        render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
        render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
        for sprite in &self.sprites {
            let sprite_texture_bind_group = &sprite.material.bind_group;
            render_pass.set_bind_group(0, sprite_texture_bind_group, &[]); // sets sampler and texture

            let num_indices = swamp_wgpu_sprites::INDICES.len() as u32;

            render_pass.draw_indexed(0..num_indices, 0, 0..1);
            render_pass.draw(0..3, 0..1);
        }
    }

    pub fn create_material_png(&mut self, png: &[u8], label: &str) -> SpriteMaterialRef {
        let texture =
            swamp_wgpu_sprites::load_texture_from_memory(&self.device, &self.queue, png, label);
        info!("loaded texture!");

        let bind_group = swamp_wgpu::create_bind_group(
            &self.device,
            &self.bind_group_layout,
            &self.sampler,
            texture,
            label,
        );

        let material = Rc::new(SpriteMaterial {
            bind_group,
            render_pipeline: Rc::clone(&self.pipeline),
        });
        self.materials.push(Rc::clone(&material));

        material
    }
}

fn sort_sprites_by_z_then_y(sprites: &mut [Sprite]) {
    sprites.sort_by_key(|sprite| (sprite.position.z, sprite.position.y));
}

#[derive(Default, Debug)]
pub struct SpriteParams {
    pub dest_size: Option<Vec2>,
    pub source: Option<URect>,
    pub rotation: u16,
    pub flip_x: bool,
    pub flip_y: bool,
    pub pivot: Option<Vec2>,
}

pub type SpriteMaterialRef = Rc<SpriteMaterial>;

#[derive(Debug)]
pub struct Sprite {
    pub position: Vec3,
    pub material: SpriteMaterialRef,
    pub params: SpriteParams,
}

pub type RenderPipelineRef = Rc<RenderPipeline>;

#[derive(Debug)]
pub struct SpriteMaterial {
    pub bind_group: BindGroup,
    pub render_pipeline: RenderPipelineRef,
}