anvilkit_render/renderer/
line.rs1use glam::{Mat4, Vec3};
22use wgpu::{
23 BindGroup, Buffer, CommandEncoder, RenderPipeline, TextureView,
24};
25
26use crate::renderer::RenderDevice;
27use crate::renderer::buffer::{ColorVertex, Vertex, create_uniform_buffer};
28use crate::renderer::pipeline::RenderPipelineBuilder;
29
30const LINE_SHADER: &str = include_str!("../shaders/line.wgsl");
32
33pub struct LineRenderer {
38 pipeline: RenderPipeline,
39 scene_buffer: Buffer,
40 scene_bind_group: BindGroup,
41 cached_vb: Option<(Buffer, u64)>,
43}
44
45#[repr(C)]
47#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
48struct LineSceneUniform {
49 view_proj: [[f32; 4]; 4],
50}
51
52impl LineRenderer {
53 pub fn new(device: &RenderDevice, format: wgpu::TextureFormat) -> Self {
60 let scene_uniform = LineSceneUniform {
61 view_proj: Mat4::IDENTITY.to_cols_array_2d(),
62 };
63 let scene_buffer = create_uniform_buffer(
64 device,
65 "Line Scene Uniform",
66 bytemuck::bytes_of(&scene_uniform),
67 );
68
69 let scene_bind_group_layout =
70 device
71 .device()
72 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
73 label: Some("Line Scene BGL"),
74 entries: &[wgpu::BindGroupLayoutEntry {
75 binding: 0,
76 visibility: wgpu::ShaderStages::VERTEX,
77 ty: wgpu::BindingType::Buffer {
78 ty: wgpu::BufferBindingType::Uniform,
79 has_dynamic_offset: false,
80 min_binding_size: None,
81 },
82 count: None,
83 }],
84 });
85
86 let scene_bind_group = device.device().create_bind_group(&wgpu::BindGroupDescriptor {
87 label: Some("Line Scene BG"),
88 layout: &scene_bind_group_layout,
89 entries: &[wgpu::BindGroupEntry {
90 binding: 0,
91 resource: scene_buffer.as_entire_binding(),
92 }],
93 });
94
95 let pipeline = RenderPipelineBuilder::new()
97 .with_vertex_shader(LINE_SHADER)
98 .with_fragment_shader(LINE_SHADER)
99 .with_format(format)
100 .with_vertex_layouts(vec![ColorVertex::layout()])
101 .with_bind_group_layouts(vec![scene_bind_group_layout])
102 .with_topology(wgpu::PrimitiveTopology::LineList)
103 .with_label("Line Pipeline")
104 .build(device)
105 .expect("创建 Line 管线失败")
106 .into_pipeline();
107
108 Self {
109 pipeline,
110 scene_buffer,
111 scene_bind_group,
112 cached_vb: None,
113 }
114 }
115
116 pub fn render(
126 &mut self,
127 device: &RenderDevice,
128 encoder: &mut CommandEncoder,
129 target: &TextureView,
130 lines: &[(Vec3, Vec3, Vec3)],
131 view_proj: &Mat4,
132 ) {
133 if lines.is_empty() {
134 return;
135 }
136
137 let uniform = LineSceneUniform {
139 view_proj: view_proj.to_cols_array_2d(),
140 };
141 device
142 .queue()
143 .write_buffer(&self.scene_buffer, 0, bytemuck::bytes_of(&uniform));
144
145 let mut vertices = Vec::with_capacity(lines.len() * 2);
147 for (start, end, color) in lines {
148 vertices.push(ColorVertex {
149 position: [start.x, start.y, start.z],
150 color: [color.x, color.y, color.z],
151 });
152 vertices.push(ColorVertex {
153 position: [end.x, end.y, end.z],
154 color: [color.x, color.y, color.z],
155 });
156 }
157
158 let data: &[u8] = bytemuck::cast_slice(&vertices);
160 let needed = data.len() as u64;
161 let reuse = self.cached_vb.as_ref().map_or(false, |(_, cap)| *cap >= needed);
162 if !reuse {
163 self.cached_vb = Some((
164 device.device().create_buffer(&wgpu::BufferDescriptor {
165 label: Some("Line VB (cached)"),
166 size: needed,
167 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
168 mapped_at_creation: false,
169 }),
170 needed,
171 ));
172 }
173 let vertex_buffer = &self.cached_vb.as_ref().unwrap().0;
174 device.queue().write_buffer(vertex_buffer, 0, data);
175
176 {
177 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
178 label: Some("Line Pass"),
179 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
180 view: target,
181 resolve_target: None,
182 ops: wgpu::Operations {
183 load: wgpu::LoadOp::Load,
184 store: wgpu::StoreOp::Store,
185 },
186 })],
187 depth_stencil_attachment: None,
188 timestamp_writes: None,
189 occlusion_query_set: None,
190 });
191
192 render_pass.set_pipeline(&self.pipeline);
193 render_pass.set_bind_group(0, &self.scene_bind_group, &[]);
194 render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
195 render_pass.draw(0..vertices.len() as u32, 0..1);
196 }
197 }
198}