1use super::{
2 super::{
3 ExperimentalShader, GlobalsLayouts, Mesh, PipelineModes, TerrainLayout,
4 Vertex as VertexTrait, buffer::Buffer,
5 },
6 GlobalModel, Texture, lod_terrain,
7};
8use bytemuck::{Pod, Zeroable};
9use std::mem;
10use vek::*;
11
12pub const VERT_PAGE_SIZE: u32 = 256;
13
14#[repr(C)]
15#[derive(Copy, Clone, Debug, Zeroable, Pod)]
16pub struct Vertex {
17 pos_norm: u32,
18 atlas_pos: u32,
21 }
28
29impl Vertex {
43 pub fn new(atlas_pos: Vec2<u16>, pos: Vec3<f32>, norm: Vec3<f32>) -> Self {
45 const VERT_EXTRA_NEG_XY: i32 = 128;
46 const VERT_EXTRA_NEG_Z: i32 = 128; #[expect(clippy::bool_to_int_with_if)]
49 let norm_bits = if norm.x != 0.0 {
50 if norm.x < 0.0 { 0 } else { 1 }
51 } else if norm.y != 0.0 {
52 if norm.y < 0.0 { 2 } else { 3 }
53 } else {
54 if norm.z < 0.0 { 4 } else { 5 }
55 };
56
57 Self {
58 pos_norm: (((pos.x as i32 + VERT_EXTRA_NEG_XY) & 0x00FF) as u32) | ((((pos.y as i32 + VERT_EXTRA_NEG_XY) & 0x00FF) as u32) << 8)
65 | ((((pos.z as i32 + VERT_EXTRA_NEG_Z).clamp(0, 1 << 12) as u32) & 0x0FFF) << 16)
66 | ((norm_bits & 0x7) << 29),
67 atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) | (((atlas_pos.y as u32) & 0xFFFF) << 16),
68 }
69 }
70}
71
72impl Default for Vertex {
73 fn default() -> Self { Self::new(Vec2::zero(), Vec3::zero(), Vec3::zero()) }
74}
75
76impl VertexTrait for Vertex {
77 const QUADS_INDEX: Option<wgpu::IndexFormat> = Some(wgpu::IndexFormat::Uint16);
78 const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
79}
80
81pub struct SpriteVerts(Buffer<Vertex>);
82
83pub(in super::super) fn create_verts_buffer(
84 device: &wgpu::Device,
85 mesh: Mesh<Vertex>,
86) -> SpriteVerts {
87 SpriteVerts(Buffer::new(
89 device,
90 wgpu::BufferUsages::STORAGE,
91 mesh.vertices(),
92 ))
93}
94
95#[repr(C)]
96#[derive(Copy, Clone, Debug, Zeroable, Pod)]
97pub struct Instance {
98 inst_mat0: [f32; 4],
99 inst_mat1: [f32; 4],
100 inst_mat2: [f32; 4],
101 inst_mat3: [f32; 4],
102 inst_glow: [f32; 4],
103 pos_meta: u32,
104 inst_vert_page: u32,
105 inst_light: f32,
106 model_wind_sway: f32,
107 model_z_scale: f32,
108}
109
110impl Instance {
111 pub fn new(
112 mat: Mat4<f32>,
113 wind_sway: f32,
114 z_scale: f32,
115 pos: Vec3<i32>,
116 light: f32,
117 glow: (Vec3<f32>, f32),
118 vert_page: u32,
119 is_door: bool,
120 is_mirrored: bool,
121 ) -> Self {
122 const EXTRA_NEG_Z: i32 = 32768;
123
124 let mat_arr = mat.into_col_arrays();
125 Self {
126 inst_mat0: mat_arr[0],
127 inst_mat1: mat_arr[1],
128 inst_mat2: mat_arr[2],
129 inst_mat3: mat_arr[3],
130 inst_glow: [glow.0.x, glow.0.y, glow.0.z, glow.1],
131 pos_meta: ((pos.x as u32) & 0x003F)
132 | (((pos.y as u32) & 0x003F) << 6)
133 | ((((pos.z + EXTRA_NEG_Z).clamp(0, 1 << 16) as u32) & 0xFFFF) << 12)
134 | ((u32::from(is_door) & 1) << 28)
135 | ((u32::from(is_mirrored) & 1) << 29),
136 inst_vert_page: vert_page,
137 inst_light: light,
138 model_wind_sway: wind_sway,
139 model_z_scale: z_scale,
140 }
141 }
142
143 fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
144 const ATTRIBUTES: [wgpu::VertexAttribute; 10] = wgpu::vertex_attr_array![
145 0 => Float32x4,
146 1 => Float32x4,
147 2 => Float32x4,
148 3 => Float32x4,
149 4 => Float32x4,
150 5 => Uint32,
151 6 => Uint32,
152 7 => Float32,
153 8 => Float32,
154 9 => Float32,
155 ];
156 wgpu::VertexBufferLayout {
157 array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
158 step_mode: wgpu::VertexStepMode::Instance,
159 attributes: &ATTRIBUTES,
160 }
161 }
162}
163
164impl Default for Instance {
165 fn default() -> Self {
166 Self::new(
167 Mat4::identity(),
168 0.0,
169 0.0,
170 Vec3::zero(),
171 1.0,
172 (Vec3::zero(), 0.0),
173 0,
174 false,
175 false,
176 )
177 }
178}
179
180pub struct Locals;
182
183pub struct SpriteGlobalsBindGroup {
184 pub(in super::super) bind_group: wgpu::BindGroup,
185}
186
187pub struct SpriteLayout {
188 pub globals: wgpu::BindGroupLayout,
189}
190
191impl SpriteLayout {
192 pub fn new(device: &wgpu::Device) -> Self {
193 let mut entries = GlobalsLayouts::base_globals_layout();
194 debug_assert_eq!(15, entries.len()); entries.extend_from_slice(&[
196 wgpu::BindGroupLayoutEntry {
198 binding: 15,
199 visibility: wgpu::ShaderStages::VERTEX,
200 ty: wgpu::BindingType::Buffer {
201 ty: wgpu::BufferBindingType::Storage { read_only: true },
202 has_dynamic_offset: false,
203 min_binding_size: core::num::NonZeroU64::new(mem::size_of::<Vertex>() as u64),
204 },
205 count: None,
206 },
207 ]);
208
209 Self {
210 globals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
211 label: None,
212 entries: &entries,
213 }),
214 }
215 }
216
217 fn bind_globals_inner(
218 &self,
219 device: &wgpu::Device,
220 global_model: &GlobalModel,
221 lod_data: &lod_terrain::LodData,
222 noise: &Texture,
223 sprite_verts: &SpriteVerts,
224 ) -> wgpu::BindGroup {
225 let mut entries = GlobalsLayouts::bind_base_globals(global_model, lod_data, noise);
226
227 entries.extend_from_slice(&[
228 wgpu::BindGroupEntry {
230 binding: 15,
231 resource: sprite_verts.0.buf.as_entire_binding(),
232 },
233 ]);
234
235 device.create_bind_group(&wgpu::BindGroupDescriptor {
236 label: None,
237 layout: &self.globals,
238 entries: &entries,
239 })
240 }
241
242 pub fn bind_globals(
243 &self,
244 device: &wgpu::Device,
245 global_model: &GlobalModel,
246 lod_data: &lod_terrain::LodData,
247 noise: &Texture,
248 sprite_verts: &SpriteVerts,
249 ) -> SpriteGlobalsBindGroup {
250 let bind_group =
251 self.bind_globals_inner(device, global_model, lod_data, noise, sprite_verts);
252
253 SpriteGlobalsBindGroup { bind_group }
254 }
255}
256
257pub struct SpritePipeline {
258 pub pipeline: wgpu::RenderPipeline,
259}
260
261impl SpritePipeline {
262 pub fn new(
263 device: &wgpu::Device,
264 vs_module: &wgpu::ShaderModule,
265 fs_module: &wgpu::ShaderModule,
266 global_layout: &GlobalsLayouts,
267 layout: &SpriteLayout,
268 terrain_layout: &TerrainLayout,
269 format: wgpu::TextureFormat,
270 pipeline_modes: &PipelineModes,
271 ) -> Self {
272 common_base::span!(_guard, "SpritePipeline::new");
273 let render_pipeline_layout =
274 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
275 label: Some("Sprite pipeline layout"),
276 push_constant_ranges: &[],
277 bind_group_layouts: &[
278 &layout.globals,
279 &global_layout.shadow_textures,
280 global_layout.figure_sprite_atlas_layout.layout(),
282 &terrain_layout.locals,
283 ],
284 });
285
286 let samples = pipeline_modes.aa.samples();
287
288 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
289 label: Some("Sprite pipeline"),
290 layout: Some(&render_pipeline_layout),
291 vertex: wgpu::VertexState {
292 module: vs_module,
293 entry_point: Some("main"),
294 buffers: &[Instance::desc()],
295 compilation_options: Default::default(),
296 },
297 primitive: wgpu::PrimitiveState {
298 topology: wgpu::PrimitiveTopology::TriangleList,
299 strip_index_format: None,
300 front_face: wgpu::FrontFace::Ccw,
301 cull_mode: Some(wgpu::Face::Back),
302 unclipped_depth: false,
303 polygon_mode: if pipeline_modes
304 .experimental_shaders
305 .contains(&ExperimentalShader::Wireframe)
306 {
307 wgpu::PolygonMode::Line
308 } else {
309 wgpu::PolygonMode::Fill
310 },
311 conservative: false,
312 },
313 depth_stencil: Some(wgpu::DepthStencilState {
314 format: wgpu::TextureFormat::Depth32Float,
315 depth_write_enabled: true,
316 depth_compare: wgpu::CompareFunction::GreaterEqual,
317 stencil: wgpu::StencilState {
318 front: wgpu::StencilFaceState::IGNORE,
319 back: wgpu::StencilFaceState::IGNORE,
320 read_mask: !0,
321 write_mask: 0,
322 },
323 bias: wgpu::DepthBiasState {
324 constant: 0,
325 slope_scale: 0.0,
326 clamp: 0.0,
327 },
328 }),
329 multisample: wgpu::MultisampleState {
330 count: samples,
331 mask: !0,
332 alpha_to_coverage_enabled: false,
333 },
334 fragment: Some(wgpu::FragmentState {
335 module: fs_module,
336 entry_point: Some("main"),
337 targets: &[
338 Some(wgpu::ColorTargetState {
339 format,
340 blend: None, write_mask: wgpu::ColorWrites::ALL,
354 }),
355 Some(wgpu::ColorTargetState {
356 format: wgpu::TextureFormat::Rgba8Uint,
357 blend: None,
358 write_mask: wgpu::ColorWrites::ALL,
359 }),
360 ],
361 compilation_options: Default::default(),
362 }),
363 multiview: None,
364 cache: None,
365 });
366
367 Self {
368 pipeline: render_pipeline,
369 }
370 }
371}