summaryrefslogtreecommitdiff
path: root/client/src/scene_render.rs
blob: 8703c9f66ff3c0ac23813b2e445f7738a86f4bba (plain)
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
use glam::{EulerRot, Mat3, Mat4, Vec3, vec3};
use std::sync::Arc;
use weareshared::{packets::Resource, tree::SceneTree};
use wgpu::{
    BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState,
    Color, ColorTargetState, ColorWrites, CommandEncoder, CompareFunction, DepthBiasState,
    DepthStencilState, Device, Extent3d, FragmentState, FrontFace, IndexFormat, LoadOp,
    MultisampleState, Operations, PipelineCompilationOptions, PipelineLayoutDescriptor,
    PolygonMode, PrimitiveState, PrimitiveTopology, PushConstantRange, RenderPassColorAttachment,
    RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPipeline,
    RenderPipelineDescriptor, SamplerBindingType, ShaderStages, StencilState, StoreOp,
    TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages,
    TextureView, TextureViewDescriptor, TextureViewDimension, VertexAttribute, VertexBufferLayout,
    VertexFormat, VertexState, VertexStepMode, include_wgsl,
};

use crate::scene_prepare::{DemandMap, RPrefab};

pub struct ScenePipeline {
    pipeline: RenderPipeline,
    depth: TextureView,
}

macro_rules! v_attr {
    ($($n:literal),*) => {
        [$(VertexBufferLayout {
            step_mode: VertexStepMode::Vertex,
            array_stride: 4,
            attributes: &[VertexAttribute {
                format: VertexFormat::Float32,
                offset: 0,
                shader_location: $n,
            }],
        }),*]
    };
}

impl ScenePipeline {
    pub fn new(device: &Device, format: TextureFormat) -> (Self, BindGroupLayout) {
        let module = device.create_shader_module(include_wgsl!("shader.wgsl"));

        let depth = device.create_texture(&TextureDescriptor {
            label: None,
            size: Extent3d {
                height: 256,
                width: 256,
                depth_or_array_layers: 1,
            },
            mip_level_count: 1,
            sample_count: 1,
            dimension: TextureDimension::D2,
            format: TextureFormat::Depth32Float,
            usage: TextureUsages::RENDER_ATTACHMENT,
            view_formats: &[],
        });
        let depth = depth.create_view(&TextureViewDescriptor::default());

        let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
            entries: &[
                BindGroupLayoutEntry {
                    binding: 0,
                    count: None,
                    visibility: ShaderStages::FRAGMENT,
                    ty: BindingType::Texture {
                        sample_type: TextureSampleType::Float { filterable: true },
                        view_dimension: TextureViewDimension::D2,
                        multisampled: false,
                    },
                },
                BindGroupLayoutEntry {
                    binding: 1,
                    count: None,
                    visibility: ShaderStages::FRAGMENT,
                    ty: BindingType::Sampler(SamplerBindingType::Filtering),
                },
            ],
            label: None,
        });
        let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
            label: None,
            bind_group_layouts: &[&bind_group_layout],
            push_constant_ranges: &[PushConstantRange {
                range: 0..(4 * 4 * size_of::<f32>() as u32),
                stages: ShaderStages::VERTEX,
            }],
        });
        let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
            label: None,
            layout: Some(&pipeline_layout),
            fragment: Some(FragmentState {
                module: &module,
                entry_point: Some("fs_main"),
                targets: &[Some(ColorTargetState {
                    blend: Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING),
                    format,
                    write_mask: ColorWrites::all(),
                })],
                compilation_options: PipelineCompilationOptions::default(),
            }),
            vertex: VertexState {
                module: &module,
                entry_point: Some("vs_main"),
                buffers: &v_attr!(0, 1, 2, 3, 4, 5, 6, 7),
                compilation_options: PipelineCompilationOptions::default(),
            },
            primitive: PrimitiveState {
                topology: PrimitiveTopology::TriangleList,
                front_face: FrontFace::Ccw,
                cull_mode: None, //Some(Face::Back),
                polygon_mode: PolygonMode::Fill,
                ..Default::default()
            },
            depth_stencil: Some(DepthStencilState {
                depth_compare: CompareFunction::Greater,
                depth_write_enabled: true,
                format: TextureFormat::Depth32Float,
                bias: DepthBiasState::default(),
                stencil: StencilState::default(),
            }),
            multisample: MultisampleState::default(),
            multiview: None,
            cache: None,
        });
        (Self { pipeline, depth }, bind_group_layout)
    }

    pub fn resize(&mut self, device: &Device, width: u32, height: u32) {
        self.depth = device
            .create_texture(&TextureDescriptor {
                label: None,
                size: Extent3d {
                    height,
                    width,
                    depth_or_array_layers: 1,
                },
                mip_level_count: 1,
                sample_count: 1,
                dimension: TextureDimension::D2,
                format: TextureFormat::Depth32Float,
                usage: TextureUsages::RENDER_ATTACHMENT,
                view_formats: &[],
            })
            .create_view(&TextureViewDescriptor::default());
    }

    pub fn draw(
        &mut self,
        commands: &mut CommandEncoder,
        target: &TextureView,
        scene: &SceneTree,
        prefabs: &mut DemandMap<Resource, Arc<RPrefab>>,
    ) {
        let mut rpass = commands.begin_render_pass(&RenderPassDescriptor {
            label: None,
            color_attachments: &[Some(RenderPassColorAttachment {
                view: target,
                resolve_target: None,
                ops: Operations {
                    store: StoreOp::Store,
                    load: LoadOp::Clear(Color {
                        r: 0.01,
                        g: 0.01,
                        b: 0.01,
                        a: 1.,
                    }),
                },
            })],
            depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
                view: &self.depth,
                depth_ops: Some(Operations {
                    load: LoadOp::Clear(0.),
                    store: StoreOp::Store,
                }),
                stencil_ops: None,
            }),
            ..Default::default()
        });

        let camera = Mat4::perspective_infinite_reverse_rh(1., 1., 0.1)
            * Mat4::look_at_rh(vec3(-5., 0., 0.), vec3(0., 0., 0.), Vec3::Y);

        for ob in scene.objects.values() {
            let prefab_projection = camera
                * Mat4::from_mat3(Mat3::from_euler(
                    EulerRot::YXZ,
                    ob.rot.x,
                    ob.rot.y,
                    ob.rot.z,
                ))
                * Mat4::from_translation(ob.pos.into());
            if let Some(prefab) = prefabs.try_get(ob.res) {
                for (affine, part) in &prefab.0 {
                    let part_projection = prefab_projection
                        * Mat4::from_mat3a(affine.matrix3)
                        * Mat4::from_translation(affine.translation.into());
                    let projection = part_projection.to_cols_array().map(|v| v.to_le_bytes());

                    rpass.set_bind_group(0, &*part.texture, &[]);
                    rpass.set_pipeline(&self.pipeline);
                    rpass.set_push_constants(ShaderStages::VERTEX, 0, projection.as_flattened());
                    rpass.set_index_buffer(part.index.slice(..), IndexFormat::Uint16);
                    rpass.set_vertex_buffer(0, part.position[0].slice(..));
                    rpass.set_vertex_buffer(1, part.position[1].slice(..));
                    rpass.set_vertex_buffer(2, part.position[2].slice(..));
                    rpass.set_vertex_buffer(3, part.normal[0].slice(..));
                    rpass.set_vertex_buffer(4, part.normal[1].slice(..));
                    rpass.set_vertex_buffer(5, part.normal[2].slice(..));
                    rpass.set_vertex_buffer(6, part.texcoord[0].slice(..));
                    rpass.set_vertex_buffer(7, part.texcoord[1].slice(..));
                    rpass.draw_indexed(0..part.index_count, 0, 0..1);
                }
            }
        }
    }
}