1use super::{
2 super::{AaMode, Bound, Consts, GlobalsLayouts, Mesh, Model},
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 aa_mode: AaMode,
180 format: wgpu::TextureFormat,
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 = aa_mode.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: "main",
203 buffers: &[Vertex::desc()],
204 },
205 primitive: wgpu::PrimitiveState {
206 topology: wgpu::PrimitiveTopology::TriangleList,
207 strip_index_format: None,
208 front_face: wgpu::FrontFace::Ccw,
209 cull_mode: Some(wgpu::Face::Back),
210 unclipped_depth: false,
211 polygon_mode: wgpu::PolygonMode::Fill,
212 conservative: false,
213 },
214 depth_stencil: Some(wgpu::DepthStencilState {
215 format: wgpu::TextureFormat::Depth32Float,
216 depth_write_enabled: true,
217 depth_compare: wgpu::CompareFunction::GreaterEqual,
218 stencil: wgpu::StencilState {
219 front: wgpu::StencilFaceState::IGNORE,
220 back: wgpu::StencilFaceState::IGNORE,
221 read_mask: !0,
222 write_mask: 0,
223 },
224 bias: wgpu::DepthBiasState {
225 constant: 0,
226 slope_scale: 0.0,
227 clamp: 0.0,
228 },
229 }),
230 multisample: wgpu::MultisampleState {
231 count: samples,
232 mask: !0,
233 alpha_to_coverage_enabled: false,
234 },
235 fragment: Some(wgpu::FragmentState {
236 module: fs_module,
237 entry_point: "main",
238 targets: &[
239 Some(wgpu::ColorTargetState {
240 format,
241 blend: None,
242 write_mask: wgpu::ColorWrites::ALL,
243 }),
244 Some(wgpu::ColorTargetState {
245 format: wgpu::TextureFormat::Rgba8Uint,
246 blend: None,
247 write_mask: wgpu::ColorWrites::ALL,
248 }),
249 ],
250 }),
251 multiview: None,
252 });
253
254 Self {
255 pipeline: render_pipeline,
256 }
257 }
258}
259
260pub struct FigureSpriteAtlasData {
263 pub col_lights: Vec<[u8; 4]>,
264}
265
266impl AtlasData for FigureSpriteAtlasData {
267 type SliceMut<'a> = std::slice::IterMut<'a, [u8; 4]>;
268
269 const TEXTURES: usize = 1;
270
271 fn blank_with_size(sz: Vec2<u16>) -> Self {
272 let col_lights =
273 vec![Vertex::make_col_light(254, 0, Rgb::broadcast(254), true); sz.as_().product()];
274 Self { col_lights }
275 }
276
277 fn as_texture_data(&self) -> [(wgpu::TextureFormat, &[u8]); Self::TEXTURES] {
278 [(
279 wgpu::TextureFormat::Rgba8Unorm,
280 bytemuck::cast_slice(self.col_lights.as_slice()),
281 )]
282 }
283
284 fn layout() -> Vec<wgpu::BindGroupLayoutEntry> {
285 vec![
286 wgpu::BindGroupLayoutEntry {
288 binding: 0,
289 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
290 ty: wgpu::BindingType::Texture {
291 sample_type: wgpu::TextureSampleType::Float { filterable: true },
292 view_dimension: wgpu::TextureViewDimension::D2,
293 multisampled: false,
294 },
295 count: None,
296 },
297 wgpu::BindGroupLayoutEntry {
298 binding: 1,
299 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
300 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
301 count: None,
302 },
303 ]
304 }
305
306 fn slice_mut(&mut self, range: std::ops::Range<usize>) -> Self::SliceMut<'_> {
307 self.col_lights[range].iter_mut()
308 }
309}