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