daily update

This commit is contained in:
lennlouisgeek
2026-06-28 23:07:10 +08:00
parent b3fa3c8341
commit f74d8e5013
14 changed files with 623 additions and 357 deletions

View File

@@ -40,6 +40,43 @@ pub struct HandGatewayMode {
pub range: Range<u32>,
}
const HAND_TIP_MATRICES: [HandTipMatrix; 5] = [
HandTipMatrix {
center_px: [260.0, 490.0],
size_px: [70.0, 120.0],
angle_rad: -0.45,
},
HandTipMatrix {
center_px: [360.0, 255.0],
size_px: [70.0, 120.0],
angle_rad: -0.05,
},
HandTipMatrix {
center_px: [485.0, 225.0],
size_px: [70.0, 120.0],
angle_rad: 0.0,
},
HandTipMatrix {
center_px: [595.0, 260.0],
size_px: [70.0, 120.0],
angle_rad: 0.12,
},
HandTipMatrix {
center_px: [705.0, 385.0],
size_px: [70.0, 120.0],
angle_rad: 0.28,
},
];
// Each entry pins one miniature matrix to a fingertip in hand.png.
// Coordinates are authored in source-image pixels so they are easy to tune by eye.
// size_px keeps the same 7:12 aspect as the Finger-mode 7 columns x 12 rows matrix.
struct HandTipMatrix {
center_px: [f32; 2],
size_px: [f32; 2],
angle_rad: f32,
}
impl egui_wgpu::CallbackTrait for WgpuBackgroundCallback {
fn prepare(
&self,
@@ -78,12 +115,15 @@ pub struct BackgroundRenderResources {
hand_image_pipeline: wgpu::RenderPipeline,
glyph_pipeline: wgpu::RenderPipeline,
dot_pipeline: wgpu::RenderPipeline,
hand_dot_pipeline: wgpu::RenderPipeline,
hand_image_bind_group: wgpu::BindGroup,
hand_image_texture: texture::Texture,
glyph_vertex_buffer: wgpu::Buffer,
glyph_instance_buffer: wgpu::Buffer,
glyph_instances: Vec<GlyphInstance>,
hand_dot_instance_buffer: wgpu::Buffer,
hand_dot_instances: Vec<GlyphInstance>,
render_options: RenderOptions,
}
@@ -405,6 +445,8 @@ impl BackgroundRenderResources {
let glyph_pipeline =
create_glyph_pipeline(device, target_format, &shader, &pipeline_layout);
let dot_pipeline = create_dot_pipeline(device, target_format, &shader, &pipeline_layout);
let hand_dot_pipeline =
create_hand_dot_pipeline(device, target_format, &shader, &pipeline_layout);
let _model_pipelines =
create_model_pipelines(device, target_format, &shader, &model_pipeline_layout);
@@ -432,6 +474,19 @@ impl BackgroundRenderResources {
contents: bytemuck::cast_slice(&glyph_instances),
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
});
let hand_dot_instances = build_hand_dot_instances(
rows,
cols,
hand_image_texture.width as f32,
hand_image_texture.height as f32,
&[[0.0, 0.0]; PRESSURE_CELL_COUNT],
);
let hand_dot_instance_buffer =
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Hand Fingertip Dot Matrix Instance Buffer"),
contents: bytemuck::cast_slice(&hand_dot_instances),
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
});
Self {
layout,
@@ -445,11 +500,14 @@ impl BackgroundRenderResources {
hand_image_pipeline,
glyph_pipeline,
dot_pipeline,
hand_dot_pipeline,
hand_image_bind_group,
hand_image_texture,
glyph_vertex_buffer,
glyph_instance_buffer,
glyph_instances,
hand_dot_instance_buffer,
hand_dot_instances,
render_options,
}
}
@@ -485,6 +543,21 @@ impl BackgroundRenderResources {
0,
bytemuck::cast_slice(&self.glyph_instances),
);
// Hand mode uses five UV-anchored fingertip matrices over hand.png.
// Rebuild their instance positions here so pressure colors update every frame.
self.hand_dot_instances = build_hand_dot_instances(
self.rows,
self.cols,
self.hand_image_texture.width as f32,
self.hand_image_texture.height as f32,
pressure,
);
queue.write_buffer(
&self.hand_dot_instance_buffer,
0,
bytemuck::cast_slice(&self.hand_dot_instances),
);
}
fn paint(&self, render_pass: &mut wgpu::RenderPass<'_>, active_mode: &ActiveMode) {
@@ -493,13 +566,14 @@ impl BackgroundRenderResources {
render_pass.set_pipeline(&self.background_pipeline);
render_pass.draw(0..3, 0..1);
render_pass.set_pipeline(&self.hand_image_pipeline);
render_pass.set_bind_group(1, &self.hand_image_bind_group, &[]);
render_pass.draw(0..6, 0..1);
match active_mode {
ActiveMode::Finger(mode) => self.paint_finger(render_pass, mode),
ActiveMode::Hand(mode) => self.paint_hand(render_pass, mode),
ActiveMode::Hand(mode) => {
render_pass.set_pipeline(&self.hand_image_pipeline);
render_pass.set_bind_group(1, &self.hand_image_bind_group, &[]);
render_pass.draw(0..6, 0..1);
self.paint_hand(render_pass, mode);
}
}
}
@@ -520,7 +594,12 @@ impl BackgroundRenderResources {
fn paint_hand(&self, render_pass: &mut wgpu::RenderPass<'_>, mode: &HandGatewayMode) {
let _range = mode.range.clone();
let _ = render_pass;
// Draw the five fingertip dot matrices after hand.png, so they sit on top of the texture.
render_pass.set_pipeline(&self.hand_dot_pipeline);
render_pass.set_vertex_buffer(0, self.glyph_vertex_buffer.slice(..));
render_pass.set_vertex_buffer(1, self.hand_dot_instance_buffer.slice(..));
render_pass.draw(0..6, 0..self.hand_dot_instances.len() as u32);
}
fn visible_instance_count(&self, rows: u32, cols: u32) -> u32 {
@@ -788,6 +867,80 @@ fn create_dot_pipeline(
})
}
fn create_hand_dot_pipeline(
device: &wgpu::Device,
target_format: &wgpu::TextureFormat,
shader: &wgpu::ShaderModule,
layout: &wgpu::PipelineLayout,
) -> wgpu::RenderPipeline {
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Hand Fingertip Dot Matrix Pipeline"),
layout: Some(layout),
vertex: wgpu::VertexState {
module: shader,
entry_point: Some("vs_hand_dot"),
compilation_options: Default::default(),
buffers: &[GlyphVertex::desc(), GlyphInstance::desc()],
},
fragment: Some(wgpu::FragmentState {
module: shader,
entry_point: Some("fs_hand_dot"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: *target_format,
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
})
}
fn build_hand_dot_instances(
rows: u32,
cols: u32,
image_width: f32,
image_height: f32,
pressure: &PressureFrame,
) -> Vec<GlyphInstance> {
let mut instances = Vec::with_capacity(HAND_TIP_MATRICES.len() * rows as usize * cols as usize);
for tip in HAND_TIP_MATRICES {
let cos = tip.angle_rad.cos();
let sin = tip.angle_rad.sin();
for row in 0..rows {
for col in 0..cols {
let index = (row * cols + col) as usize;
let [normalized, display_value] =
pressure.get(index).copied().unwrap_or([0.0, 0.0]);
// Lay out a rows x cols matrix in fingertip-local pixel space.
let local_x = (col as f32 - cols as f32 / 2.0 + 0.5) / cols as f32 * tip.size_px[0];
let local_y = (row as f32 - rows as f32 / 2.0 + 0.5) / rows as f32 * tip.size_px[1];
// Rotate the local matrix so it follows the direction of the finger.
let x = tip.center_px[0] + local_x * cos - local_y * sin;
let y = tip.center_px[1] + local_x * sin + local_y * cos;
let uv_x = x / image_width.max(1.0);
let uv_y = y / image_height.max(1.0);
instances.push(GlyphInstance {
world_position: [uv_x, uv_y, 0.0, 1.0],
style: [normalized, display_value, 0.0, 0.0],
});
}
}
}
instances
}
fn build_glyph_instances(
rows: u32,
cols: u32,