1use crate::{AssetHub, CameraParams, DummyResources, Object, Shaders, Vertex};
2use blade_graphics as gpu;
3use std::mem;
4
5#[derive(Clone, Copy, Debug)]
6pub struct RasterConfig {
7 pub clear_color: gpu::TextureColor,
8 pub light_dir: mint::Vector3<f32>,
9 pub light_color: mint::Vector3<f32>,
10 pub ambient_color: mint::Vector3<f32>,
11 pub roughness: f32,
12 pub metallic: f32,
13 pub space_sky: bool,
15}
16
17impl Default for RasterConfig {
18 fn default() -> Self {
19 Self {
20 clear_color: gpu::TextureColor::OpaqueBlack,
21 light_dir: mint::Vector3 {
22 x: -0.3,
23 y: -1.0,
24 z: -0.2,
25 },
26 light_color: mint::Vector3 {
27 x: 3.0,
28 y: 3.0,
29 z: 3.0,
30 },
31 ambient_color: mint::Vector3 {
32 x: 0.05,
33 y: 0.05,
34 z: 0.05,
35 },
36 roughness: 0.4,
37 metallic: 0.0,
38 space_sky: false,
39 }
40 }
41}
42
43#[repr(C)]
44#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
45struct RasterFrameParams {
46 view_proj: [f32; 16],
47 inv_view_proj: [f32; 16],
48 camera_pos: [f32; 4],
49 light_dir: [f32; 4],
50 light_color: [f32; 4],
51 ambient_color: [f32; 4],
52 material: [f32; 4],
53}
54
55#[repr(C)]
56#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
57struct RasterDrawParams {
58 model: [f32; 16],
59 normal_quat: [f32; 4],
60 base_color_factor: [f32; 4],
61 material: [f32; 4],
62}
63
64#[derive(blade_macros::ShaderData)]
65struct RasterMainData {
66 frame_params: RasterFrameParams,
67 draw_params: RasterDrawParams,
68 vertices: gpu::BufferPiece,
69 samp: gpu::Sampler,
70 base_color_tex: gpu::TextureView,
71 normal_tex: gpu::TextureView,
72}
73
74#[derive(blade_macros::ShaderData)]
75struct RasterSkyData {
76 sky_params: RasterFrameParams,
77 samp: gpu::Sampler,
78 env_map: gpu::TextureView,
79}
80
81struct RasterPipelines {
82 main: gpu::RenderPipeline,
83 sky: gpu::RenderPipeline,
84}
85
86impl RasterPipelines {
87 fn create_main(
88 shader: &gpu::Shader,
89 info: gpu::SurfaceInfo,
90 gpu: &gpu::Context,
91 ) -> gpu::RenderPipeline {
92 shader.check_struct_size::<RasterFrameParams>();
93 shader.check_struct_size::<RasterDrawParams>();
94 let main_layout = <RasterMainData as gpu::ShaderData>::layout();
95 gpu.create_render_pipeline(gpu::RenderPipelineDesc {
96 name: "raster",
97 data_layouts: &[&main_layout],
98 vertex: shader.at("raster_vs"),
99 vertex_fetches: &[],
100 primitive: gpu::PrimitiveState {
101 topology: gpu::PrimitiveTopology::TriangleList,
102 ..Default::default()
103 },
104 depth_stencil: Some(gpu::DepthStencilState {
105 format: gpu::TextureFormat::Depth32Float,
106 depth_write_enabled: true,
107 depth_compare: gpu::CompareFunction::Less,
108 stencil: gpu::StencilState::default(),
109 bias: gpu::DepthBiasState::default(),
110 }),
111 fragment: Some(shader.at("raster_fs")),
112 color_targets: &[info.format.into()],
113 multisample_state: gpu::MultisampleState::default(),
114 })
115 }
116
117 fn create_sky(
118 shader: &gpu::Shader,
119 info: gpu::SurfaceInfo,
120 gpu: &gpu::Context,
121 ) -> gpu::RenderPipeline {
122 shader.check_struct_size::<RasterFrameParams>();
123 let sky_layout = <RasterSkyData as gpu::ShaderData>::layout();
124 gpu.create_render_pipeline(gpu::RenderPipelineDesc {
125 name: "raster-sky",
126 data_layouts: &[&sky_layout],
127 vertex: shader.at("raster_sky_vs"),
128 vertex_fetches: &[],
129 primitive: gpu::PrimitiveState {
130 topology: gpu::PrimitiveTopology::TriangleList,
131 ..Default::default()
132 },
133 depth_stencil: Some(gpu::DepthStencilState {
134 format: gpu::TextureFormat::Depth32Float,
135 depth_write_enabled: false,
136 depth_compare: gpu::CompareFunction::LessEqual,
137 stencil: gpu::StencilState::default(),
138 bias: gpu::DepthBiasState::default(),
139 }),
140 fragment: Some(shader.at("raster_sky_fs")),
141 color_targets: &[info.format.into()],
142 multisample_state: gpu::MultisampleState::default(),
143 })
144 }
145
146 fn init(
147 shaders: &Shaders,
148 config: &crate::render::RenderConfig,
149 gpu: &gpu::Context,
150 shader_man: &blade_asset::AssetManager<crate::shader::Baker>,
151 ) -> Result<Self, &'static str> {
152 let shader = shader_man[shaders.raster].raw.as_ref().unwrap();
153 Ok(Self {
154 main: Self::create_main(shader, config.surface_info, gpu),
155 sky: Self::create_sky(shader, config.surface_info, gpu),
156 })
157 }
158}
159
160pub struct Rasterizer {
161 shaders: Shaders,
162 pipelines: RasterPipelines,
163 sampler_linear: gpu::Sampler,
164 debug: Option<crate::render::DebugRender>,
165 dummy: DummyResources,
166 depth_texture: gpu::Texture,
167 depth_view: gpu::TextureView,
168 surface_size: gpu::Extent,
169 surface_info: gpu::SurfaceInfo,
170}
171
172impl Rasterizer {
173 #[profiling::function]
174 pub fn new(
175 encoder: &mut gpu::CommandEncoder,
176 gpu: &gpu::Context,
177 shaders: Shaders,
178 shader_man: &blade_asset::AssetManager<crate::shader::Baker>,
179 config: &crate::render::RenderConfig,
180 ) -> Self {
181 let pipelines = RasterPipelines::init(&shaders, config, gpu, shader_man).unwrap();
182 #[cfg(target_os = "android")]
183 let debug = None;
184 #[cfg(not(target_os = "android"))]
185 let debug = {
186 let sh_draw = shader_man[shaders.debug_draw].raw.as_ref().unwrap();
187 let sh_blit = shader_man[shaders.debug_blit].raw.as_ref().unwrap();
188 Some(crate::render::DebugRender::init(
189 encoder,
190 gpu,
191 sh_draw,
192 sh_blit,
193 config.max_debug_lines,
194 config.surface_info,
195 ))
196 };
197 let dummy = DummyResources::new(encoder, gpu);
198 let sampler_linear = gpu.create_sampler(gpu::SamplerDesc {
199 name: "raster-linear",
200 address_modes: [gpu::AddressMode::Repeat; 3],
201 mag_filter: gpu::FilterMode::Linear,
202 min_filter: gpu::FilterMode::Linear,
203 mipmap_filter: gpu::FilterMode::Linear,
204 ..Default::default()
205 });
206 let (depth_texture, depth_view) = Self::create_depth_target(config.surface_size, gpu);
207
208 Self {
209 shaders,
210 pipelines,
211 sampler_linear,
212 debug,
213 dummy,
214 depth_texture,
215 depth_view,
216 surface_size: config.surface_size,
217 surface_info: config.surface_info,
218 }
219 }
220
221 pub fn destroy(&mut self, gpu: &gpu::Context) {
222 if let Some(debug) = self.debug.as_mut() {
223 debug.destroy(gpu);
224 }
225 self.dummy.destroy(gpu);
226 gpu.destroy_texture_view(self.depth_view);
227 gpu.destroy_texture(self.depth_texture);
228 gpu.destroy_sampler(self.sampler_linear);
229 gpu.destroy_render_pipeline(&mut self.pipelines.main);
230 gpu.destroy_render_pipeline(&mut self.pipelines.sky);
231 }
232
233 #[profiling::function]
234 pub fn hot_reload(
235 &mut self,
236 asset_hub: &AssetHub,
237 gpu: &gpu::Context,
238 sync_point: &gpu::SyncPoint,
239 ) -> bool {
240 let mut tasks = Vec::new();
241 let old = self.shaders.clone();
242
243 tasks.extend(asset_hub.shaders.hot_reload(&mut self.shaders.raster));
244
245 if tasks.is_empty() {
246 return false;
247 }
248
249 log::info!("Hot reloading raster shaders");
250 let _ = gpu.wait_for(sync_point, !0);
251 for task in tasks {
252 let _ = task.join();
253 }
254
255 if self.shaders.raster != old.raster
256 && let Ok(ref shader) = asset_hub.shaders[self.shaders.raster].raw
257 {
258 self.pipelines.main = RasterPipelines::create_main(shader, self.surface_info, gpu);
259 self.pipelines.sky = RasterPipelines::create_sky(shader, self.surface_info, gpu);
260 }
261
262 true
263 }
264
265 pub fn get_surface_size(&self) -> gpu::Extent {
266 self.surface_size
267 }
268
269 pub fn depth_view(&self) -> gpu::TextureView {
270 self.depth_view
271 }
272
273 pub fn depth_texture(&self) -> gpu::Texture {
274 self.depth_texture
275 }
276
277 pub fn resize_screen(
278 &mut self,
279 size: gpu::Extent,
280 _encoder: &mut gpu::CommandEncoder,
281 gpu: &gpu::Context,
282 ) {
283 if size == self.surface_size {
284 return;
285 }
286 gpu.destroy_texture_view(self.depth_view);
287 gpu.destroy_texture(self.depth_texture);
288 let (depth_texture, depth_view) = Self::create_depth_target(size, gpu);
289 self.depth_texture = depth_texture;
290 self.depth_view = depth_view;
291 self.surface_size = size;
292 }
293
294 #[profiling::function]
295 pub fn render(
296 &mut self,
297 pass: &mut gpu::RenderCommandEncoder,
298 camera: &crate::Camera,
299 objects: &[Object],
300 asset_hub: &AssetHub,
301 environment_map: Option<blade_asset::Handle<crate::Texture>>,
302 config: RasterConfig,
303 ) {
304 let env_map_enabled = environment_map.is_some();
305 let frame_params = self.make_frame_params(camera, config, env_map_enabled);
306 if let mut pc = pass.with(&self.pipelines.main) {
307 for object in objects.iter() {
308 let model = &asset_hub.models[object.model];
309 let object_transform = mat4_transform(&object.transform);
310 let object_normal = object_transform.inverse().transpose();
311
312 for geometry in model.geometries.iter() {
313 let geometry_transform = mat4_transform(&geometry.transform);
314 let world_transform = object_transform * geometry_transform;
315 let normal_transform = object_normal * geometry_transform.inverse().transpose();
316 let normal_basis = glam::Mat3::from_cols(
317 normal_transform.x_axis.truncate().normalize_or_zero(),
318 normal_transform.y_axis.truncate().normalize_or_zero(),
319 normal_transform.z_axis.truncate().normalize_or_zero(),
320 );
321 let normal_quat = glam::Quat::from_mat3(&normal_basis).normalize();
322 let material = &model.materials[geometry.material_index];
323
324 let (normal_tex, normal_scale) = match material.normal_texture {
325 Some(handle) => {
326 let texture = &asset_hub.textures[handle];
327 (texture.view, material.normal_scale)
328 }
329 None => (self.dummy.white_view, 0.0),
330 };
331 let base_color_tex = match material.base_color_texture {
332 Some(handle) => asset_hub.textures[handle].view,
333 None => self.dummy.white_view,
334 };
335
336 pc.bind(
337 0,
338 &RasterMainData {
339 frame_params,
340 draw_params: RasterDrawParams {
341 model: world_transform.to_cols_array(),
342 normal_quat: normal_quat.to_array(),
343 base_color_factor: [
344 material.base_color_factor[0] * object.color_tint[0],
345 material.base_color_factor[1] * object.color_tint[1],
346 material.base_color_factor[2] * object.color_tint[2],
347 material.base_color_factor[3] * object.color_tint[3],
348 ],
349 material: [normal_scale, 0.0, 0.0, 0.0],
350 },
351 vertices: model.vertex_buffer.at(0),
352 samp: self.sampler_linear,
353 base_color_tex,
354 normal_tex,
355 },
356 );
357
358 let vertex_count = geometry.vertex_range.end - geometry.vertex_range.start;
359 let index_count = geometry.triangle_count * 3;
360 match geometry.index_type {
361 Some(index_type) => {
362 pc.draw_indexed(
363 model.index_buffer.at(geometry.index_offset),
364 index_type,
365 index_count,
366 geometry.vertex_range.start as i32,
367 0,
368 1,
369 );
370 }
371 None => {
372 pc.draw(geometry.vertex_range.start, vertex_count, 0, 1);
373 }
374 }
375 }
376 }
377 }
378
379 let env_map = environment_map
380 .map(|handle| asset_hub.textures[handle].view)
381 .unwrap_or(self.dummy.black_view);
382 self.render_sky(pass, frame_params, env_map);
383 }
384
385 pub fn render_debug_lines(
386 &self,
387 pass: &mut gpu::RenderCommandEncoder,
388 camera: &crate::Camera,
389 debug_lines: &[crate::DebugLine],
390 ) {
391 let Some(debug) = self.debug.as_ref() else {
392 return;
393 };
394 if debug_lines.is_empty() {
395 return;
396 }
397 let camera_params = self.make_camera_params(camera);
398 debug.render_lines(debug_lines, camera_params, self.depth_view, pass);
399 }
400
401 pub fn render_sky_only(
403 &self,
404 pass: &mut gpu::RenderCommandEncoder,
405 camera: &crate::Camera,
406 environment_map: Option<blade_asset::Handle<crate::Texture>>,
407 asset_hub: &AssetHub,
408 config: RasterConfig,
409 ) {
410 let env_map_enabled = environment_map.is_some();
411 let frame_params = self.make_frame_params(camera, config, env_map_enabled);
412 let env_map = environment_map
413 .map(|handle| asset_hub.textures[handle].view)
414 .unwrap_or(self.dummy.black_view);
415 self.render_sky(pass, frame_params, env_map);
416 }
417
418 fn render_sky(
419 &self,
420 pass: &mut gpu::RenderCommandEncoder,
421 frame_params: RasterFrameParams,
422 env_map: gpu::TextureView,
423 ) {
424 let mut pc = pass.with(&self.pipelines.sky);
425 pc.bind(
426 0,
427 &RasterSkyData {
428 sky_params: frame_params,
429 samp: self.sampler_linear,
430 env_map,
431 },
432 );
433 pc.draw(0, 3, 0, 1);
434 }
435
436 fn create_depth_target(
437 size: gpu::Extent,
438 gpu: &gpu::Context,
439 ) -> (gpu::Texture, gpu::TextureView) {
440 let texture = gpu.create_texture(gpu::TextureDesc {
441 name: "raster depth",
442 size,
443 format: gpu::TextureFormat::Depth32Float,
444 array_layer_count: 1,
445 mip_level_count: 1,
446 sample_count: 1,
447 dimension: gpu::TextureDimension::D2,
448 usage: gpu::TextureUsage::TARGET | gpu::TextureUsage::RESOURCE,
449 external: None,
450 });
451 let view = gpu.create_texture_view(
452 texture,
453 gpu::TextureViewDesc {
454 name: "raster depth",
455 format: gpu::TextureFormat::Depth32Float,
456 dimension: gpu::ViewDimension::D2,
457 subresources: &gpu::TextureSubresources::default(),
458 },
459 );
460 (texture, view)
461 }
462
463 fn make_camera_params(&self, camera: &crate::Camera) -> CameraParams {
464 let fov_x = 2.0
465 * ((camera.fov_y * 0.5).tan() * self.surface_size.width as f32
466 / self.surface_size.height as f32)
467 .atan();
468 CameraParams {
469 position: camera.pos.into(),
470 depth: camera.depth,
471 orientation: camera.rot.into(),
472 fov: [fov_x, camera.fov_y],
473 target_size: [self.surface_size.width, self.surface_size.height],
474 }
475 }
476
477 fn make_frame_params(
478 &self,
479 camera: &crate::Camera,
480 config: RasterConfig,
481 env_map_enabled: bool,
482 ) -> RasterFrameParams {
483 let pos = glam::Vec3::from(camera.pos);
484 let rot = glam::Quat::from(camera.rot);
485 let view = glam::Mat4::from_rotation_translation(rot, pos).inverse();
486 let near = 0.01;
487 let far = camera.depth;
488 let proj = if let Some(fov) = camera.fov {
489 let left = -fov.left.tan() * near;
491 let right = fov.right.tan() * near;
492 let bottom = -fov.down.tan() * near;
493 let top = fov.up.tan() * near;
494 let w = right - left;
495 let h = top - bottom;
496 glam::Mat4::from_cols(
497 glam::Vec4::new(2.0 * near / w, 0.0, 0.0, 0.0),
498 glam::Vec4::new(0.0, 2.0 * near / h, 0.0, 0.0),
499 glam::Vec4::new(
500 (right + left) / w,
501 (top + bottom) / h,
502 far / (near - far),
503 -1.0,
504 ),
505 glam::Vec4::new(0.0, 0.0, far * near / (near - far), 0.0),
506 )
507 } else {
508 let aspect = self.surface_size.width as f32 / self.surface_size.height.max(1) as f32;
509 glam::Mat4::perspective_rh(camera.fov_y, aspect, near, far)
510 };
511 let view_proj = proj * view;
512 let inv_view_proj = view_proj.inverse();
513 let light_dir = glam::Vec3::from(config.light_dir).normalize_or_zero();
514 RasterFrameParams {
515 view_proj: view_proj.to_cols_array(),
516 inv_view_proj: inv_view_proj.to_cols_array(),
517 camera_pos: [pos.x, pos.y, pos.z, 1.0],
518 light_dir: [light_dir.x, light_dir.y, light_dir.z, 0.0],
519 light_color: {
520 let c = config.light_color;
521 [c.x, c.y, c.z, 0.0]
522 },
523 ambient_color: {
524 let c = config.ambient_color;
525 [c.x, c.y, c.z, config.space_sky as u32 as f32]
526 },
527 material: [
528 config.roughness,
529 config.metallic,
530 env_map_enabled as u32 as f32,
531 0.0,
532 ],
533 }
534 }
535}
536
537impl gpu::Vertex for Vertex {
538 fn layout() -> gpu::VertexLayout {
539 gpu::VertexLayout {
540 attributes: vec![
541 (
542 "position",
543 gpu::VertexAttribute {
544 offset: 0,
545 format: gpu::VertexFormat::F32Vec3,
546 },
547 ),
548 (
549 "bitangent_sign",
550 gpu::VertexAttribute {
551 offset: 12,
552 format: gpu::VertexFormat::F32,
553 },
554 ),
555 (
556 "tex_coords",
557 gpu::VertexAttribute {
558 offset: 16,
559 format: gpu::VertexFormat::F32Vec2,
560 },
561 ),
562 (
563 "normal",
564 gpu::VertexAttribute {
565 offset: 24,
566 format: gpu::VertexFormat::U32,
567 },
568 ),
569 (
570 "tangent",
571 gpu::VertexAttribute {
572 offset: 28,
573 format: gpu::VertexFormat::U32,
574 },
575 ),
576 ],
577 stride: mem::size_of::<Vertex>() as u32,
578 }
579 }
580}
581
582fn mat4_transform(t: &gpu::Transform) -> glam::Mat4 {
583 glam::Mat4 {
584 x_axis: t.x.into(),
585 y_axis: t.y.into(),
586 z_axis: t.z.into(),
587 w_axis: glam::Vec4::W,
588 }
589 .transpose()
590}