View hbias worlds

This commit is contained in:
Simon Gellis 2025-02-17 13:52:04 -05:00
parent 2a4599756c
commit 3cdc0583a6
3 changed files with 175 additions and 39 deletions

View File

@ -98,8 +98,11 @@ macro_rules! primitive_memory_value_impl {
} }
primitive_memory_value_impl!(u8, 1); primitive_memory_value_impl!(u8, 1);
primitive_memory_value_impl!(i8, 2);
primitive_memory_value_impl!(u16, 2); primitive_memory_value_impl!(u16, 2);
primitive_memory_value_impl!(i16, 2);
primitive_memory_value_impl!(u32, 4); primitive_memory_value_impl!(u32, 4);
primitive_memory_value_impl!(i32, 2);
impl<const N: usize, T: MemoryValue> MemoryValue for [T; N] { impl<const N: usize, T: MemoryValue> MemoryValue for [T; N] {
#[inline] #[inline]

View File

@ -34,10 +34,11 @@ pub trait UiExt {
impl UiExt for Ui { impl UiExt for Ui {
fn section(&mut self, title: impl Into<String>, add_contents: impl FnOnce(&mut Ui)) { fn section(&mut self, title: impl Into<String>, add_contents: impl FnOnce(&mut Ui)) {
let title: String = title.into();
let mut frame = Frame::group(self.style()); let mut frame = Frame::group(self.style());
frame.outer_margin.top += 10.0; frame.outer_margin.top += 10.0;
frame.inner_margin.top += 2.0; frame.inner_margin.top += 2.0;
let res = frame.show(self, add_contents); let res = self.push_id(&title, |ui| frame.show(ui, add_contents));
let text = RichText::new(title).background_color(self.style().visuals.panel_fill); let text = RichText::new(title).background_color(self.style().visuals.panel_fill);
let old_rect = res.response.rect; let old_rect = res.response.rect;
let mut text_rect = old_rect; let mut text_rect = old_rect;
@ -133,12 +134,14 @@ impl<'a, T: Number> NumberEdit<'a, T> {
impl<T: Number> Widget for NumberEdit<'_, T> { impl<T: Number> Widget for NumberEdit<'_, T> {
fn ui(self, ui: &mut Ui) -> Response { fn ui(self, ui: &mut Ui) -> Response {
let id = ui.id();
let (last_value, mut str, focus) = ui.memory(|m| { let (last_value, mut str, focus) = ui.memory(|m| {
let (lv, s) = m let (lv, s) = m
.data .data
.get_temp(ui.id()) .get_temp(id)
.unwrap_or((*self.value, self.value.to_string())); .unwrap_or((*self.value, self.value.to_string()));
let focus = m.has_focus(ui.id()); let focus = m.has_focus(id);
(lv, s, focus) (lv, s, focus)
}); });
let mut stale = false; let mut stale = false;
@ -174,7 +177,7 @@ impl<T: Number> Widget for NumberEdit<'_, T> {
} }
let text = TextEdit::singleline(&mut str) let text = TextEdit::singleline(&mut str)
.horizontal_align(Align::Max) .horizontal_align(Align::Max)
.id(ui.id()) .id(id)
.margin(Margin { .margin(Margin {
left: 4.0, left: 4.0,
right: 20.0, right: 20.0,
@ -242,7 +245,7 @@ impl<T: Number> Widget for NumberEdit<'_, T> {
stale = true; stale = true;
} }
if stale { if stale {
ui.memory_mut(|m| m.data.insert_temp(ui.id(), (*self.value, str))); ui.memory_mut(|m| m.data.insert_temp(id, (*self.value, str)));
} }
res res
} }

View File

@ -24,7 +24,9 @@ pub struct WorldWindow {
sim_id: SimId, sim_id: SimId,
loader: Arc<VramTextureLoader>, loader: Arc<VramTextureLoader>,
worlds: MemoryView, worlds: MemoryView,
bgmaps: MemoryView,
index: usize, index: usize,
param_index: usize,
generic_palette: bool, generic_palette: bool,
params: VramParams<WorldParams>, params: VramParams<WorldParams>,
scale: f32, scale: f32,
@ -45,7 +47,9 @@ impl WorldWindow {
sim_id, sim_id,
loader: Arc::new(loader), loader: Arc::new(loader),
worlds: memory.watch(sim_id, 0x3d800, 0x400), worlds: memory.watch(sim_id, 0x3d800, 0x400),
bgmaps: memory.watch(sim_id, 0x00020000, 0x20000),
index: params.index, index: params.index,
param_index: 0,
generic_palette: params.generic_palette, generic_palette: params.generic_palette,
params, params,
scale: 1.0, scale: 1.0,
@ -262,6 +266,57 @@ impl WorldWindow {
}); });
}); });
}); });
if world.header.mode == WorldMode::HBias {
ui.section("H-bias", |ui| {
TableBuilder::new(ui)
.column(Column::remainder())
.column(Column::remainder())
.body(|mut body| {
body.row(row_height, |mut row| {
row.col(|ui| {
ui.label("Index");
});
row.col(|ui| {
ui.add(NumberEdit::new(&mut self.param_index).range(0..32));
});
});
let base = world.param_base + self.param_index * 2;
let mut param = HBiasParam::load(&self.bgmaps.borrow(), base);
body.row(row_height, |mut row| {
row.col(|ui| {
ui.label("Address");
});
row.col(|ui| {
let address = 0x00020000 + base * 2;
let mut address_str = format!("{address:08x}");
ui.add_enabled(
false,
TextEdit::singleline(&mut address_str)
.horizontal_align(Align::Max),
);
});
});
body.row(row_height, |mut row| {
row.col(|ui| {
ui.label("Left");
});
row.col(|ui| {
ui.add(NumberEdit::new(&mut param.left).range(-4096..4096));
});
});
body.row(row_height, |mut row| {
row.col(|ui| {
ui.label("Right");
});
row.col(|ui| {
ui.add(NumberEdit::new(&mut param.right).range(-4096..4096));
});
});
});
});
} else {
self.param_index = 0;
}
ui.section("Display", |ui| { ui.section("Display", |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("Scale"); ui.label("Scale");
@ -497,48 +552,44 @@ impl WorldRenderer {
] ]
}; };
let mut chars = CharCache::new(self.chardata.borrow()); let chardata = self.chardata.borrow();
let mut cells = CellCache::new(self.bgmaps.borrow()); let bgmaps = self.bgmaps.borrow();
let mut chars = [CharCache::new(&chardata), CharCache::new(&chardata)];
let mut cells = [CellCache::new(&bgmaps), CellCache::new(&bgmaps)];
let mut source = SourceCoordCalculator::new(&bgmaps, &world);
for y in 0..height { for y in 0..height {
let dy = y + world.dst_y; let dy = y + world.dst_y;
if !(0..224).contains(&dy) { if !(0..224).contains(&dy) {
continue; continue;
} }
let sy = y + world.src_y;
// left side
for x in 0..world.width { for x in 0..world.width {
let dx = x + world.dst_x - world.dst_parallax; let dx = x + world.dst_x - world.dst_parallax;
if !(0..384).contains(&dx) { if world.header.lon && (0..384).contains(&dx) {
continue; let (sx, sy) = source.left(x, y);
let cell_index = world.source_cell(sx, sy);
let cell = cells[0].get(cell_index);
let char = chars[0].get(cell.char_index);
let row = (sy & 0x7) as usize;
let col = (sx & 0x7) as usize;
let pixel = utils::read_char_pixel(char, cell.hflip, cell.vflip, row, col);
image.add((dx as usize, dy as usize), colors[0][pixel as usize]);
} }
let sx = x + world.src_x - world.src_parallax;
let cell_index = world.source_cell(sx, sy);
let cell = cells.get(cell_index);
let char = chars.get(cell.char_index);
let row = (sy & 0x7) as usize;
let col = (sx & 0x7) as usize;
let pixel = utils::read_char_pixel(char, cell.hflip, cell.vflip, row, col);
image.add((dx as usize, dy as usize), colors[0][pixel as usize]);
}
// right side
for x in 0..world.width {
let dx = x + world.dst_x + world.dst_parallax; let dx = x + world.dst_x + world.dst_parallax;
if !(0..384).contains(&dx) { if world.header.ron && (0..384).contains(&dx) {
continue; let (sx, sy) = source.right(x, y);
}
let sx = x + world.src_x + world.src_parallax;
let cell_index = world.source_cell(sx, sy); let cell_index = world.source_cell(sx, sy);
let cell = cells.get(cell_index); let cell = cells[1].get(cell_index);
let char = chars.get(cell.char_index); let char = chars[1].get(cell.char_index);
let row = (sy & 0x7) as usize; let row = (sy & 0x7) as usize;
let col = (sx & 0x7) as usize; let col = (sx & 0x7) as usize;
let pixel = utils::read_char_pixel(char, cell.hflip, cell.vflip, row, col); let pixel = utils::read_char_pixel(char, cell.hflip, cell.vflip, row, col);
image.add((dx as usize, dy as usize), colors[1][pixel as usize]); image.add((dx as usize, dy as usize), colors[1][pixel as usize]);
}
} }
} }
} }
@ -556,7 +607,7 @@ impl VramRenderer<1> for WorldRenderer {
let worlds = self.worlds.borrow(); let worlds = self.worlds.borrow();
let header = WorldHeader::parse(worlds.read(params.index * 16)); let header = WorldHeader::parse(worlds.read(params.index * 16));
if header.end || (!header.lon && header.ron) { if header.end || (!header.lon && !header.ron) {
image.clear(); image.clear();
return; return;
} }
@ -693,13 +744,13 @@ impl Display for WorldMode {
} }
struct CellCache<'a> { struct CellCache<'a> {
bgmaps: MemoryRef<'a>, bgmaps: &'a MemoryRef<'a>,
index: usize, index: usize,
cell: CellData, cell: CellData,
} }
impl<'a> CellCache<'a> { impl<'a> CellCache<'a> {
fn new(bgmaps: MemoryRef<'a>) -> Self { fn new(bgmaps: &'a MemoryRef<'a>) -> Self {
Self { Self {
bgmaps, bgmaps,
index: 0x10000, index: 0x10000,
@ -718,13 +769,13 @@ impl<'a> CellCache<'a> {
} }
struct CharCache<'a> { struct CharCache<'a> {
chardata: MemoryRef<'a>, chardata: &'a MemoryRef<'a>,
index: usize, index: usize,
char: [u16; 8], char: [u16; 8],
} }
impl<'a> CharCache<'a> { impl<'a> CharCache<'a> {
fn new(chardata: MemoryRef<'a>) -> Self { fn new(chardata: &'a MemoryRef<'a>) -> Self {
Self { Self {
chardata, chardata,
index: 2048, index: 2048,
@ -740,3 +791,82 @@ impl<'a> CharCache<'a> {
&self.char &self.char
} }
} }
struct SourceCoordCalculator<'a> {
params: &'a MemoryRef<'a>,
world: &'a World,
y: i16,
param: SourceParam,
}
impl<'a> SourceCoordCalculator<'a> {
fn new(params: &'a MemoryRef<'a>, world: &'a World) -> Self {
Self {
params,
world,
y: -1,
param: SourceParam::Normal,
}
}
fn left(&mut self, x: i16, y: i16) -> (i16, i16) {
self.update_param(y);
match &self.param {
SourceParam::HBias(HBiasParam { left, .. }) => {
let sx = x + self.world.src_x - self.world.src_parallax + *left;
let sy = y + self.world.src_y;
(sx, sy)
}
SourceParam::Normal => {
let sx = x + self.world.src_x - self.world.src_parallax;
let sy = y + self.world.src_y;
(sx, sy)
}
}
}
fn right(&mut self, x: i16, y: i16) -> (i16, i16) {
self.update_param(y);
match &self.param {
SourceParam::HBias(HBiasParam { right, .. }) => {
let sx = x + self.world.src_x + self.world.src_parallax + *right;
let sy = y + self.world.src_y;
(sx, sy)
}
SourceParam::Normal => {
let sx = x + self.world.src_x + self.world.src_parallax;
let sy = y + self.world.src_y;
(sx, sy)
}
}
}
fn update_param(&mut self, y: i16) {
if self.y == y {
return;
}
if self.world.header.mode == WorldMode::HBias {
let base = self.world.param_base + (2 * y as usize);
self.param = SourceParam::HBias(HBiasParam::load(self.params, base));
}
self.y = y;
}
}
enum SourceParam {
Normal,
HBias(HBiasParam),
}
struct HBiasParam {
left: i16,
right: i16,
}
impl HBiasParam {
fn load(params: &MemoryRef, index: usize) -> Self {
let left = params.read::<i16>(index) << 3 >> 3;
let right = params.read::<i16>(index | 1) << 3 >> 3;
Self { left, right }
}
}