1use super::{
2 super::{Bound, Consts, ExperimentalShader, GlobalsLayouts, Mesh, Model, PipelineModes},
3 AtlasData,
4 terrain::Vertex,
5};
6use crate::mesh::greedy::GreedyMesh;
7use bytemuck::{Pod, Zeroable};
8use vek::*;
9
10#[repr(C)]
11#[derive(Copy, Clone, Debug, Zeroable, Pod)]
12pub struct Locals {
13 model_mat: [[f32; 4]; 4],
14 highlight_col: [f32; 4],
15 model_light: [f32; 4],
16 model_glow: [f32; 4],
17 atlas_offs: [i32; 4],
18 model_pos: [f32; 3],
19 flags: u32,
20}
21
22#[repr(C)]
23#[derive(Copy, Clone, Debug, Zeroable, Pod)]
24pub struct BoneData {
25 bone_mat: [[f32; 4]; 4],
26 normals_mat: [[f32; 4]; 4],
27}
28
29pub type BoundLocals = Bound<(Consts<Locals>, Consts<BoneData>)>;
30
31impl Locals {
32 pub fn new(
33 model_mat: anim::vek::Mat4<f32>,
34 col: Rgb<f32>,
35 pos: anim::vek::Vec3<f32>,
36 atlas_offs: Vec2<i32>,
37 is_player: bool,
38 light: f32,
39 glow: (Vec3<f32>, f32),
40 ) -> Self {
41 let mut flags = 0;
42 flags |= is_player as u32;
43
44 Self {
45 model_mat: model_mat.into_col_arrays(),
46 highlight_col: [col.r, col.g, col.b, 1.0],
47 model_pos: pos.into_array(),
48 atlas_offs: Vec4::from(atlas_offs).into_array(),
49 model_light: [light, 1.0, 1.0, 1.0],
50 model_glow: [glow.0.x, glow.0.y, glow.0.z, glow.1],
51 flags,
52 }
53 }
54}
55
56impl Default for Locals {
57 fn default() -> Self {
58 Self::new(
59 anim::vek::Mat4::identity(),
60 Rgb::broadcast(1.0),
61 anim::vek::Vec3::default(),
62 Vec2::default(),
63 false,
64 1.0,
65 (Vec3::zero(), 0.0),
66 )
67 }
68}
69
70impl BoneData {
71 pub fn new(bone_mat: anim::vek::Mat4<f32>, normals_mat: anim::vek::Mat4<f32>) -> Self {
72 Self {
73 bone_mat: bone_mat.into_col_arrays(),
74 normals_mat: normals_mat.into_col_arrays(),
75 }
76 }
77}
78
79impl Default for BoneData {
80 fn default() -> Self { Self::new(anim::vek::Mat4::identity(), anim::vek::Mat4::identity()) }
81}
82
83pub struct FigureModel {
84 pub opaque: Option<Model<Vertex>>,
85 }
88
89impl FigureModel {
90 pub fn make_greedy<'a>() -> GreedyMesh<'a, FigureSpriteAtlasData> {
92 let max_size = Vec2::new((1 << 15) - 1, (1 << 15) - 1);
97 GreedyMesh::new(max_size, crate::mesh::greedy::general_config())
98 }
99}
100
101pub type BoneMeshes = (Mesh<Vertex>, anim::vek::Aabb<f32>);
102
103pub struct FigureLayout {
104 pub locals: wgpu::BindGroupLayout,
105}
106
107impl FigureLayout {
108 pub fn new(device: &wgpu::Device) -> Self {
109 Self {
110 locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
111 label: None,
112 entries: &[
113 wgpu::BindGroupLayoutEntry {
115 binding: 0,
116 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
117 ty: wgpu::BindingType::Buffer {
118 ty: wgpu::BufferBindingType::Uniform,
119 has_dynamic_offset: false,
120 min_binding_size: None,
121 },
122 count: None,
123 },
124 wgpu::BindGroupLayoutEntry {
126 binding: 1,
127 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
128 ty: wgpu::BindingType::Buffer {
129 ty: wgpu::BufferBindingType::Uniform,
130 has_dynamic_offset: false,
131 min_binding_size: None,
132 },
133 count: None,
134 },
135 ],
136 }),
137 }
138 }
139
140 pub fn bind_locals(
141 &self,
142 device: &wgpu::Device,
143 locals: Consts<Locals>,
144 bone_data: Consts<BoneData>,
145 ) -> BoundLocals {
146 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
147 label: None,
148 layout: &self.locals,
149 entries: &[
150 wgpu::BindGroupEntry {
151 binding: 0,
152 resource: locals.buf().as_entire_binding(),
153 },
154 wgpu::BindGroupEntry {
155 binding: 1,
156 resource: bone_data.buf().as_entire_binding(),
157 },
158 ],
159 });
160
161 BoundLocals {
162 bind_group,
163 with: (locals, bone_data),
164 }
165 }
166}
167
168pub struct FigurePipeline {
169 pub pipeline: wgpu::RenderPipeline,
170}
171
172impl FigurePipeline {
173 pub fn new(
174 device: &wgpu::Device,
175 vs_module: &wgpu::ShaderModule,
176 fs_module: &wgpu::ShaderModule,
177 global_layout: &GlobalsLayouts,
178 layout: &FigureLayout,
179 format: wgpu::TextureFormat,
180 pipeline_modes: &PipelineModes,
181 ) -> Self {
182 common_base::span!(_guard, "FigurePipeline::new");
183 let render_pipeline_layout =
184 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
185 label: Some("Figure pipeline layout"),
186 push_constant_ranges: &[],
187 bind_group_layouts: &[
188 &global_layout.globals,
189 &global_layout.shadow_textures,
190 global_layout.figure_sprite_atlas_layout.layout(),
191 &layout.locals,
192 ],
193 });
194
195 let samples = pipeline_modes.aa.samples();
196
197 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
198 label: Some("Figure pipeline"),
199 layout: Some(&render_pipeline_layout),
200 vertex: wgpu::VertexState {
201 module: vs_module,
202 entry_point: Some("main"),
203 buffers: &[Vertex::desc()],
204 compilation_options: Default::default(),
205 },
206 primitive: wgpu::PrimitiveState {
207 topology: wgpu::PrimitiveTopology::TriangleList,
208 strip_index_format: None,
209 front_face: wgpu::FrontFace::Ccw,
210 cull_mode: Some(wgpu::Face::Back),
211 unclipped_depth: false,
212 polygon_mode: if pipeline_modes
213 .experimental_shaders
214 .contains(&ExperimentalShader::Wireframe)
215 {
216 wgpu::PolygonMode::Line
217 } else {
218 wgpu::PolygonMode::Fill
219 },
220 conservative: false,
221 },
222 depth_stencil: Some(wgpu::DepthStencilState {
223 format: wgpu::TextureFormat::Depth32Float,
224 depth_write_enabled: true,
225 depth_compare: wgpu::CompareFunction::GreaterEqual,
226 stencil: wgpu::StencilState {
227 front: wgpu::StencilFaceState::IGNORE,
228 back: wgpu::StencilFaceState::IGNORE,
229 read_mask: !0,
230 write_mask: 0,
231 },
232 bias: wgpu::DepthBiasState {
233 constant: 0,
234 slope_scale: 0.0,
235 clamp: 0.0,
236 },
237 }),
238 multisample: wgpu::MultisampleState {
239 count: samples,
240 mask: !0,
241 alpha_to_coverage_enabled: false,
242 },
243 fragment: Some(wgpu::FragmentState {
244 module: fs_module,
245 entry_point: Some("main"),
246 targets: &[
247 Some(wgpu::ColorTargetState {
248 format,
249 blend: None,
250 write_mask: wgpu::ColorWrites::ALL,
251 }),
252 Some(wgpu::ColorTargetState {
253 format: wgpu::TextureFormat::Rgba8Uint,
254 blend: None,
255 write_mask: wgpu::ColorWrites::ALL,
256 }),
257 ],
258 compilation_options: Default::default(),
259 }),
260 multiview: None,
261 cache: None,
262 });
263
264 Self {
265 pipeline: render_pipeline,
266 }
267 }
268}
269
270pub struct FigureSpriteAtlasData {
273 pub col_lights: Vec<[u8; 4]>,
274}
275
276impl AtlasData for FigureSpriteAtlasData {
277 type SliceMut<'a> = std::slice::IterMut<'a, [u8; 4]>;
278
279 const TEXTURES: usize = 1;
280
281 fn blank_with_size(sz: Vec2<u16>) -> Self {
282 let col_lights =
283 vec![Vertex::make_col_light(254, 0, Rgb::broadcast(254), true); sz.as_().product()];
284 Self { col_lights }
285 }
286
287 fn as_texture_data(&self) -> [(wgpu::TextureFormat, &[u8]); Self::TEXTURES] {
288 [(
289 wgpu::TextureFormat::Rgba8Unorm,
290 bytemuck::cast_slice(self.col_lights.as_slice()),
291 )]
292 }
293
294 fn layout() -> Vec<wgpu::BindGroupLayoutEntry> {
295 vec![
296 wgpu::BindGroupLayoutEntry {
298 binding: 0,
299 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
300 ty: wgpu::BindingType::Texture {
301 sample_type: wgpu::TextureSampleType::Float { filterable: true },
302 view_dimension: wgpu::TextureViewDimension::D2,
303 multisampled: false,
304 },
305 count: None,
306 },
307 wgpu::BindGroupLayoutEntry {
308 binding: 1,
309 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
310 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
311 count: None,
312 },
313 ]
314 }
315
316 fn slice_mut(&mut self, range: std::ops::Range<usize>) -> Self::SliceMut<'_> {
317 self.col_lights[range].iter_mut()
318 }
319}