Add pressure visual modes and breakout game
This commit is contained in:
241
src/render.rs
241
src/render.rs
@@ -68,6 +68,23 @@ const HAND_TIP_MATRICES: [HandTipMatrix; 5] = [
|
||||
},
|
||||
];
|
||||
|
||||
const HAND_PALM_CHIPS: [HandPalmChip; 2] = [
|
||||
HandPalmChip {
|
||||
center_px: [538.0, 608.0],
|
||||
size_px: [248.0, 82.0],
|
||||
angle_rad: 0.06,
|
||||
rows: 5,
|
||||
cols: 14,
|
||||
},
|
||||
HandPalmChip {
|
||||
center_px: [606.0, 780.0],
|
||||
size_px: [72.0, 214.0],
|
||||
angle_rad: 0.05,
|
||||
rows: 11,
|
||||
cols: 4,
|
||||
},
|
||||
];
|
||||
|
||||
// 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.
|
||||
@@ -77,6 +94,16 @@ struct HandTipMatrix {
|
||||
angle_rad: f32,
|
||||
}
|
||||
|
||||
// Palm chips follow the hand layout: one horizontal 5x14 matrix and one vertical
|
||||
// 11x4 matrix, rendered as dark inset chip tiles on the palm.
|
||||
struct HandPalmChip {
|
||||
center_px: [f32; 2],
|
||||
size_px: [f32; 2],
|
||||
angle_rad: f32,
|
||||
rows: u32,
|
||||
cols: u32,
|
||||
}
|
||||
|
||||
impl egui_wgpu::CallbackTrait for WgpuBackgroundCallback {
|
||||
fn prepare(
|
||||
&self,
|
||||
@@ -117,6 +144,8 @@ pub struct BackgroundRenderResources {
|
||||
dot_pipeline: wgpu::RenderPipeline,
|
||||
hand_membrane_pipeline: wgpu::RenderPipeline,
|
||||
hand_dot_pipeline: wgpu::RenderPipeline,
|
||||
hand_palm_chip_pipeline: wgpu::RenderPipeline,
|
||||
hand_palm_dot_pipeline: wgpu::RenderPipeline,
|
||||
hand_image_bind_group: wgpu::BindGroup,
|
||||
hand_image_texture: texture::Texture,
|
||||
|
||||
@@ -127,6 +156,10 @@ pub struct BackgroundRenderResources {
|
||||
hand_membrane_instances: Vec<GlyphInstance>,
|
||||
hand_dot_instance_buffer: wgpu::Buffer,
|
||||
hand_dot_instances: Vec<GlyphInstance>,
|
||||
hand_palm_chip_instance_buffer: wgpu::Buffer,
|
||||
hand_palm_chip_instances: Vec<GlyphInstance>,
|
||||
hand_palm_dot_instance_buffer: wgpu::Buffer,
|
||||
hand_palm_dot_instances: Vec<GlyphInstance>,
|
||||
render_options: RenderOptions,
|
||||
}
|
||||
|
||||
@@ -452,6 +485,10 @@ impl BackgroundRenderResources {
|
||||
create_hand_membrane_pipeline(device, target_format, &shader, &pipeline_layout);
|
||||
let hand_dot_pipeline =
|
||||
create_hand_dot_pipeline(device, target_format, &shader, &pipeline_layout);
|
||||
let hand_palm_chip_pipeline =
|
||||
create_hand_palm_chip_pipeline(device, target_format, &shader, &pipeline_layout);
|
||||
let hand_palm_dot_pipeline =
|
||||
create_hand_palm_dot_pipeline(device, target_format, &shader, &pipeline_layout);
|
||||
|
||||
let _model_pipelines =
|
||||
create_model_pipelines(device, target_format, &shader, &model_pipeline_layout);
|
||||
@@ -502,6 +539,29 @@ impl BackgroundRenderResources {
|
||||
contents: bytemuck::cast_slice(&hand_dot_instances),
|
||||
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
let hand_palm_chip_instances = build_hand_palm_chip_instances(
|
||||
hand_image_texture.width as f32,
|
||||
hand_image_texture.height as f32,
|
||||
);
|
||||
let hand_palm_chip_instance_buffer =
|
||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Hand Palm Chip Instance Buffer"),
|
||||
contents: bytemuck::cast_slice(&hand_palm_chip_instances),
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
});
|
||||
let hand_palm_dot_instances = build_hand_palm_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_palm_dot_instance_buffer =
|
||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Hand Palm Chip Dot Instance Buffer"),
|
||||
contents: bytemuck::cast_slice(&hand_palm_dot_instances),
|
||||
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
Self {
|
||||
layout,
|
||||
@@ -517,6 +577,8 @@ impl BackgroundRenderResources {
|
||||
dot_pipeline,
|
||||
hand_membrane_pipeline,
|
||||
hand_dot_pipeline,
|
||||
hand_palm_chip_pipeline,
|
||||
hand_palm_dot_pipeline,
|
||||
hand_image_bind_group,
|
||||
hand_image_texture,
|
||||
glyph_vertex_buffer,
|
||||
@@ -526,6 +588,10 @@ impl BackgroundRenderResources {
|
||||
hand_membrane_instances,
|
||||
hand_dot_instance_buffer,
|
||||
hand_dot_instances,
|
||||
hand_palm_chip_instance_buffer,
|
||||
hand_palm_chip_instances,
|
||||
hand_palm_dot_instance_buffer,
|
||||
hand_palm_dot_instances,
|
||||
render_options,
|
||||
}
|
||||
}
|
||||
@@ -576,6 +642,21 @@ impl BackgroundRenderResources {
|
||||
0,
|
||||
bytemuck::cast_slice(&self.hand_dot_instances),
|
||||
);
|
||||
|
||||
// Palm chips reuse the same live 12x7 pressure frame, but draw it as
|
||||
// embedded micro-pixels inside dark chip tiles.
|
||||
self.hand_palm_dot_instances = build_hand_palm_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_palm_dot_instance_buffer,
|
||||
0,
|
||||
bytemuck::cast_slice(&self.hand_palm_dot_instances),
|
||||
);
|
||||
}
|
||||
|
||||
fn paint(&self, render_pass: &mut wgpu::RenderPass<'_>, active_mode: &ActiveMode) {
|
||||
@@ -623,6 +704,16 @@ impl BackgroundRenderResources {
|
||||
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);
|
||||
|
||||
render_pass.set_pipeline(&self.hand_palm_chip_pipeline);
|
||||
render_pass.set_vertex_buffer(0, self.glyph_vertex_buffer.slice(..));
|
||||
render_pass.set_vertex_buffer(1, self.hand_palm_chip_instance_buffer.slice(..));
|
||||
render_pass.draw(0..6, 0..self.hand_palm_chip_instances.len() as u32);
|
||||
|
||||
render_pass.set_pipeline(&self.hand_palm_dot_pipeline);
|
||||
render_pass.set_vertex_buffer(0, self.glyph_vertex_buffer.slice(..));
|
||||
render_pass.set_vertex_buffer(1, self.hand_palm_dot_instance_buffer.slice(..));
|
||||
render_pass.draw(0..6, 0..self.hand_palm_dot_instances.len() as u32);
|
||||
}
|
||||
|
||||
fn visible_instance_count(&self, rows: u32, cols: u32) -> u32 {
|
||||
@@ -956,6 +1047,72 @@ fn create_hand_dot_pipeline(
|
||||
})
|
||||
}
|
||||
|
||||
fn create_hand_palm_chip_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 Palm Embedded Chip Pipeline"),
|
||||
layout: Some(layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: shader,
|
||||
entry_point: Some("vs_hand_palm_chip"),
|
||||
compilation_options: Default::default(),
|
||||
buffers: &[GlyphVertex::desc(), GlyphInstance::desc()],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: shader,
|
||||
entry_point: Some("fs_hand_palm_chip"),
|
||||
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 create_hand_palm_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 Palm Embedded Chip Dot Pipeline"),
|
||||
layout: Some(layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: shader,
|
||||
entry_point: Some("vs_hand_palm_dot"),
|
||||
compilation_options: Default::default(),
|
||||
buffers: &[GlyphVertex::desc(), GlyphInstance::desc()],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: shader,
|
||||
entry_point: Some("fs_hand_palm_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_membrane_instances(image_width: f32, image_height: f32) -> Vec<GlyphInstance> {
|
||||
HAND_TIP_MATRICES
|
||||
.iter()
|
||||
@@ -974,6 +1131,28 @@ fn build_hand_membrane_instances(image_width: f32, image_height: f32) -> Vec<Gly
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn build_hand_palm_chip_instances(image_width: f32, image_height: f32) -> Vec<GlyphInstance> {
|
||||
HAND_PALM_CHIPS
|
||||
.iter()
|
||||
.map(|chip| {
|
||||
let uv_x = chip.center_px[0] / image_width.max(1.0);
|
||||
let uv_y = chip.center_px[1] / image_height.max(1.0);
|
||||
|
||||
GlyphInstance {
|
||||
// Palm chip shaders read xy as hand.png UV.
|
||||
world_position: [uv_x, uv_y, 0.0, 1.0],
|
||||
// Store chip angle, source-image pixel size, and matrix shape for the shader grid.
|
||||
style: [
|
||||
chip.angle_rad,
|
||||
chip.size_px[0],
|
||||
chip.size_px[1],
|
||||
(chip.rows * 100 + chip.cols) as f32,
|
||||
],
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn build_hand_dot_instances(
|
||||
rows: u32,
|
||||
cols: u32,
|
||||
@@ -1015,6 +1194,68 @@ fn build_hand_dot_instances(
|
||||
instances
|
||||
}
|
||||
|
||||
fn build_hand_palm_dot_instances(
|
||||
rows: u32,
|
||||
cols: u32,
|
||||
image_width: f32,
|
||||
image_height: f32,
|
||||
pressure: &PressureFrame,
|
||||
) -> Vec<GlyphInstance> {
|
||||
let chip_dot_count: usize = HAND_PALM_CHIPS
|
||||
.iter()
|
||||
.map(|chip| (chip.rows * chip.cols) as usize)
|
||||
.sum();
|
||||
let mut instances = Vec::with_capacity(chip_dot_count);
|
||||
|
||||
for chip in HAND_PALM_CHIPS {
|
||||
let cos = chip.angle_rad.cos();
|
||||
let sin = chip.angle_rad.sin();
|
||||
// Leave a bevel around the chip so the matrix reads as embedded pixels.
|
||||
let active_size = [chip.size_px[0] * 0.72, chip.size_px[1] * 0.76];
|
||||
|
||||
for row in 0..chip.rows {
|
||||
for col in 0..chip.cols {
|
||||
// Current live data is still the device pressure frame. Sample it by
|
||||
// proportion so the palm's 5x14 and 11x4 layouts get coherent values.
|
||||
let source_row = resample_index(row, chip.rows, rows);
|
||||
let source_col = resample_index(col, chip.cols, cols);
|
||||
let index = (source_row * cols + source_col) as usize;
|
||||
let [normalized, display_value] =
|
||||
pressure.get(index).copied().unwrap_or([0.0, 0.0]);
|
||||
|
||||
let local_x =
|
||||
(col as f32 - chip.cols as f32 / 2.0 + 0.5) / chip.cols as f32 * active_size[0];
|
||||
let local_y =
|
||||
(row as f32 - chip.rows as f32 / 2.0 + 0.5) / chip.rows as f32 * active_size[1];
|
||||
|
||||
let x = chip.center_px[0] + local_x * cos - local_y * sin;
|
||||
let y = chip.center_px[1] + local_x * sin + local_y * cos;
|
||||
|
||||
instances.push(GlyphInstance {
|
||||
world_position: [
|
||||
x / image_width.max(1.0),
|
||||
y / image_height.max(1.0),
|
||||
0.0,
|
||||
1.0,
|
||||
],
|
||||
style: [normalized, display_value, 0.0, 0.0],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instances
|
||||
}
|
||||
|
||||
fn resample_index(index: u32, source_count: u32, target_count: u32) -> u32 {
|
||||
if source_count <= 1 || target_count <= 1 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mapped = index as f32 / (source_count - 1) as f32 * (target_count - 1) as f32;
|
||||
mapped.round().clamp(0.0, (target_count - 1) as f32) as u32
|
||||
}
|
||||
|
||||
fn build_glyph_instances(
|
||||
rows: u32,
|
||||
cols: u32,
|
||||
|
||||
Reference in New Issue
Block a user