Integrate hand gateway and spatial force rendering
This commit is contained in:
@@ -13,11 +13,13 @@ use std::ops::Range;
|
||||
pub const PRESSURE_CELL_COUNT: usize =
|
||||
(crate::matrix::MATRIX_ROWS * crate::matrix::MATRIX_COLS) as usize;
|
||||
pub type PressureFrame = [[f32; 2]; PRESSURE_CELL_COUNT];
|
||||
pub type PressureSamples = Vec<[f32; 2]>;
|
||||
|
||||
pub struct WgpuBackgroundCallback {
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
pub pressure: PressureFrame,
|
||||
pub hand_pressure: PressureSamples,
|
||||
pub active_mode: ActiveMode,
|
||||
}
|
||||
|
||||
@@ -85,6 +87,10 @@ const HAND_PALM_CHIPS: [HandPalmChip; 2] = [
|
||||
},
|
||||
];
|
||||
|
||||
const HAND_FINGER_SENSOR_CELLS: usize = 12 * 7;
|
||||
const HAND_PALM_HORIZONTAL_OFFSET: usize = HAND_FINGER_SENSOR_CELLS * 5;
|
||||
const HAND_PALM_VERTICAL_OFFSET: usize = HAND_PALM_HORIZONTAL_OFFSET + 5 * 14;
|
||||
|
||||
// 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.
|
||||
@@ -114,7 +120,13 @@ impl egui_wgpu::CallbackTrait for WgpuBackgroundCallback {
|
||||
resources: &mut egui_wgpu::CallbackResources,
|
||||
) -> Vec<wgpu::CommandBuffer> {
|
||||
let resources: &mut BackgroundRenderResources = resources.get_mut().unwrap();
|
||||
resources.prepare(queue, self.width, self.height, &self.pressure);
|
||||
resources.prepare(
|
||||
queue,
|
||||
self.width,
|
||||
self.height,
|
||||
&self.pressure,
|
||||
&self.hand_pressure,
|
||||
);
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
@@ -596,7 +608,14 @@ impl BackgroundRenderResources {
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare(&mut self, queue: &wgpu::Queue, width: f32, height: f32, pressure: &PressureFrame) {
|
||||
fn prepare(
|
||||
&mut self,
|
||||
queue: &wgpu::Queue,
|
||||
width: f32,
|
||||
height: f32,
|
||||
pressure: &PressureFrame,
|
||||
hand_pressure: &[[f32; 2]],
|
||||
) {
|
||||
let aspect = width / height.max(1.0);
|
||||
self.uniform = MatrixUniform::new(
|
||||
width,
|
||||
@@ -628,14 +647,20 @@ impl BackgroundRenderResources {
|
||||
bytemuck::cast_slice(&self.glyph_instances),
|
||||
);
|
||||
|
||||
// Hand mode uses five UV-anchored fingertip matrices over hand.png.
|
||||
let hand_pressure = if hand_pressure.is_empty() {
|
||||
pressure.as_slice()
|
||||
} else {
|
||||
hand_pressure
|
||||
};
|
||||
|
||||
// Hand mode uses 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,
|
||||
hand_pressure,
|
||||
);
|
||||
queue.write_buffer(
|
||||
&self.hand_dot_instance_buffer,
|
||||
@@ -650,7 +675,7 @@ impl BackgroundRenderResources {
|
||||
self.cols,
|
||||
self.hand_image_texture.width as f32,
|
||||
self.hand_image_texture.height as f32,
|
||||
pressure,
|
||||
hand_pressure,
|
||||
);
|
||||
queue.write_buffer(
|
||||
&self.hand_palm_dot_instance_buffer,
|
||||
@@ -1158,19 +1183,22 @@ fn build_hand_dot_instances(
|
||||
cols: u32,
|
||||
image_width: f32,
|
||||
image_height: f32,
|
||||
pressure: &PressureFrame,
|
||||
pressure: &[[f32; 2]],
|
||||
) -> Vec<GlyphInstance> {
|
||||
let mut instances = Vec::with_capacity(HAND_TIP_MATRICES.len() * rows as usize * cols as usize);
|
||||
|
||||
for tip in HAND_TIP_MATRICES {
|
||||
for (tip_index, tip) in HAND_TIP_MATRICES.into_iter().enumerate() {
|
||||
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]);
|
||||
let [normalized, display_value] = sample_pressure_at(
|
||||
pressure,
|
||||
tip_index * HAND_FINGER_SENSOR_CELLS + index,
|
||||
index,
|
||||
);
|
||||
|
||||
// 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];
|
||||
@@ -1195,11 +1223,11 @@ fn build_hand_dot_instances(
|
||||
}
|
||||
|
||||
fn build_hand_palm_dot_instances(
|
||||
rows: u32,
|
||||
cols: u32,
|
||||
_rows: u32,
|
||||
_cols: u32,
|
||||
image_width: f32,
|
||||
image_height: f32,
|
||||
pressure: &PressureFrame,
|
||||
pressure: &[[f32; 2]],
|
||||
) -> Vec<GlyphInstance> {
|
||||
let chip_dot_count: usize = HAND_PALM_CHIPS
|
||||
.iter()
|
||||
@@ -1207,7 +1235,7 @@ fn build_hand_palm_dot_instances(
|
||||
.sum();
|
||||
let mut instances = Vec::with_capacity(chip_dot_count);
|
||||
|
||||
for chip in HAND_PALM_CHIPS {
|
||||
for (chip_index, chip) in HAND_PALM_CHIPS.into_iter().enumerate() {
|
||||
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.
|
||||
@@ -1215,13 +1243,13 @@ fn build_hand_palm_dot_instances(
|
||||
|
||||
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 index = (row * chip.cols + col) as usize;
|
||||
let offset = match chip_index {
|
||||
0 => HAND_PALM_HORIZONTAL_OFFSET,
|
||||
_ => HAND_PALM_VERTICAL_OFFSET,
|
||||
};
|
||||
let [normalized, display_value] =
|
||||
pressure.get(index).copied().unwrap_or([0.0, 0.0]);
|
||||
sample_pressure_at(pressure, offset + index, index);
|
||||
|
||||
let local_x =
|
||||
(col as f32 - chip.cols as f32 / 2.0 + 0.5) / chip.cols as f32 * active_size[0];
|
||||
@@ -1247,13 +1275,12 @@ fn build_hand_palm_dot_instances(
|
||||
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 sample_pressure_at(pressure: &[[f32; 2]], index: usize, fallback_index: usize) -> [f32; 2] {
|
||||
pressure
|
||||
.get(index)
|
||||
.or_else(|| pressure.get(fallback_index))
|
||||
.copied()
|
||||
.unwrap_or([0.0, 0.0])
|
||||
}
|
||||
|
||||
fn build_glyph_instances(
|
||||
|
||||
Reference in New Issue
Block a user