//! Building render pipeline use super::common::{Vertex, ColoredVertex}; pub fn create_building_pipeline( device: &wgpu::Device, format: &wgpu::TextureFormat, bind_group_layout: &wgpu::BindGroupLayout ) -> wgpu::RenderPipeline { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(r#" struct CameraUniform { params: vec4, theme: vec4, }; @group(0) @binding(0) var camera: CameraUniform; struct VertexInput { @location(0) position: vec2, }; struct VertexOutput { @builtin(position) clip_position: vec4, }; @vertex fn vs_main( model: VertexInput, ) -> VertexOutput { var out: VertexOutput; let world_pos = model.position; let x = world_pos.x * camera.params.x + camera.params.z; let y = world_pos.y * camera.params.y + camera.params.w; out.clip_position = vec4(x, y, 0.0, 1.0); return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { // Buildings: Light: #d9d9d9 (0.85), Dark: #333333 (0.2) let is_dark = camera.theme.x; let color = mix(vec3(0.92, 0.91, 0.90), vec3(0.2, 0.2, 0.2), is_dark); return vec4(color, 1.0); } "#)), }); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Building Pipeline Layout"), bind_group_layouts: &[bind_group_layout], push_constant_ranges: &[], }); device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: None, layout: Some(&pipeline_layout), vertex: wgpu::VertexState { module: &shader, entry_point: "vs_main", buffers: &[ Vertex::desc(), ], }, fragment: Some(wgpu::FragmentState { module: &shader, entry_point: "fs_main", targets: &[Some(wgpu::ColorTargetState { format: *format, blend: Some(wgpu::BlendState::REPLACE), write_mask: wgpu::ColorWrites::ALL, })], }), primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, strip_index_format: None, front_face: wgpu::FrontFace::Ccw, cull_mode: None, unclipped_depth: false, polygon_mode: wgpu::PolygonMode::Fill, conservative: false, }, depth_stencil: None, multisample: wgpu::MultisampleState { count: 4, mask: !0, alpha_to_coverage_enabled: false, }, multiview: None, }) } /// Colored building pipeline - uses per-vertex color for different building types pub fn create_colored_building_pipeline( device: &wgpu::Device, format: &wgpu::TextureFormat, bind_group_layout: &wgpu::BindGroupLayout ) -> wgpu::RenderPipeline { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(r#" struct CameraUniform { params: vec4, theme: vec4, }; @group(0) @binding(0) var camera: CameraUniform; struct VertexInput { @location(0) position: vec2, @location(1) color: vec3, // Per-vertex color (light theme) }; struct VertexOutput { @builtin(position) clip_position: vec4, @location(0) v_color: vec3, }; @vertex fn vs_main(model: VertexInput) -> VertexOutput { var out: VertexOutput; let x = model.position.x * camera.params.x + camera.params.z; let y = model.position.y * camera.params.y + camera.params.w; out.clip_position = vec4(x, y, 0.0, 1.0); out.v_color = model.color; return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { let is_dark = camera.theme.x; // Dark theme: darken the light theme color let dark_color = in.v_color * 0.5; let color = mix(in.v_color, dark_color, is_dark); return vec4(color, 1.0); } "#)), }); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Colored Building Pipeline Layout"), bind_group_layouts: &[bind_group_layout], push_constant_ranges: &[], }); device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Colored Building Pipeline"), layout: Some(&pipeline_layout), vertex: wgpu::VertexState { module: &shader, entry_point: "vs_main", buffers: &[ColoredVertex::desc()], }, fragment: Some(wgpu::FragmentState { module: &shader, entry_point: "fs_main", targets: &[Some(wgpu::ColorTargetState { format: *format, blend: Some(wgpu::BlendState::REPLACE), write_mask: wgpu::ColorWrites::ALL, })], }), primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, strip_index_format: None, front_face: wgpu::FrontFace::Ccw, cull_mode: None, unclipped_depth: false, polygon_mode: wgpu::PolygonMode::Fill, conservative: false, }, depth_stencil: None, multisample: wgpu::MultisampleState { count: 4, mask: !0, alpha_to_coverage_enabled: false, }, multiview: None, }) }