//! Water render pipelines use super::common::{Vertex, create_simple_pipeline}; pub fn create_water_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; // Globe Effect: Spherize // let r2 = x*x + y*y; // let w = 1.0 + r2 * 0.5; out.clip_position = vec4(x, y, 0.0, 1.0); return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { // Water: Light: #9ecaff, Dark: #1a2639 let is_dark = camera.theme.x; let color = mix(vec3(0.62, 0.79, 1.0), vec3(0.1, 0.15, 0.22), is_dark); return vec4(color, 1.0); } "#)), }); let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Water Pipeline Layout"), bind_group_layouts: &[bind_group_layout], push_constant_ranges: &[], }); device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Water Pipeline"), layout: Some(&render_pipeline_layout), vertex: wgpu::VertexState { module: &shader, entry_point: "vs_main", buffers: &[ wgpu::VertexBufferLayout { array_stride: std::mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, attributes: &[ wgpu::VertexAttribute { offset: 0, shader_location: 0, format: wgpu::VertexFormat::Float32x2, } ], } ], }, 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::default(), multiview: None, }) } pub fn create_water_line_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 { let is_dark = camera.theme.x; // Light: #a5bfdd (same/similar to water), Dark: #4a6fa5 let color = mix(vec3(0.66, 0.82, 0.96), vec3(0.29, 0.44, 0.65), is_dark); return vec4(color, 1.0); } "#)), }); create_simple_pipeline(device, format, bind_group_layout, &shader, "Water Line Pipeline", wgpu::PrimitiveTopology::LineList) }