Compare commits
15 Commits
f64cab8f5c
...
4cb15af8a3
Author | SHA1 | Date |
---|---|---|
|
4cb15af8a3 | |
|
fcb79fa05c | |
|
74d10e08a3 | |
|
15cdf0fa8a | |
|
4bcf743e91 | |
|
bdb14f8f23 | |
|
a6c06fd3ac | |
|
d71be6ddcc | |
|
ac93dcadb5 | |
|
aaca75df26 | |
|
5901a21465 | |
|
a19d4bbc89 | |
|
9f4989cd4c | |
|
ae7ab1e204 | |
|
492c04316d |
|
@ -1763,7 +1763,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||
|
||||
[[package]]
|
||||
name = "lemur"
|
||||
version = "0.3.1"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"atoi",
|
||||
|
|
|
@ -4,7 +4,7 @@ description = "An emulator for the Virtual Boy."
|
|||
repository = "https://git.virtual-boy.com/PVB/lemur"
|
||||
publish = false
|
||||
license = "MIT"
|
||||
version = "0.3.1"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -162,7 +162,8 @@ impl GameWindow {
|
|||
fn show_options_menu(&mut self, ctx: &Context, ui: &mut Ui) {
|
||||
ui.menu_button("Video", |ui| {
|
||||
ui.menu_button("Screen Size", |ui| {
|
||||
let current_dims = self.config.dimensions;
|
||||
let current_dims = ctx.input(|i| i.viewport().inner_rect.unwrap());
|
||||
let current_dims = current_dims.max - current_dims.min;
|
||||
|
||||
for scale in 1..=4 {
|
||||
let label = format!("x{scale}");
|
||||
|
@ -202,7 +203,10 @@ impl GameWindow {
|
|||
return;
|
||||
}
|
||||
|
||||
let current_dims = self.config.dimensions;
|
||||
let current_dims = {
|
||||
let viewport = ctx.input(|i| i.viewport().inner_rect.unwrap());
|
||||
viewport.max - viewport.min
|
||||
};
|
||||
let new_proportions = display_mode.proportions();
|
||||
let scale = new_proportions / old_proportions;
|
||||
if scale != Vec2::new(1.0, 1.0) {
|
||||
|
@ -346,7 +350,7 @@ impl AppWindow for GameWindow {
|
|||
|
||||
fn show(&mut self, ctx: &Context) {
|
||||
let dimensions = {
|
||||
let bounds = ctx.available_rect();
|
||||
let bounds = ctx.input(|i| i.viewport().inner_rect.unwrap());
|
||||
bounds.max - bounds.min
|
||||
};
|
||||
self.update_config(|c| c.dimensions = dimensions);
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
},
|
||||
};
|
||||
|
||||
use super::utils::{self, CellData, CharacterGrid};
|
||||
use super::utils::{self, CharacterGrid};
|
||||
|
||||
pub struct BgMapWindow {
|
||||
sim_id: SimId,
|
||||
|
@ -94,12 +94,7 @@ impl BgMapWindow {
|
|||
ui.add(image);
|
||||
ui.section("Cell", |ui| {
|
||||
let cell = self.bgmaps.borrow().read::<u16>(self.cell_index);
|
||||
let CellData {
|
||||
char_index,
|
||||
mut vflip,
|
||||
mut hflip,
|
||||
palette_index,
|
||||
} = CellData::parse(cell);
|
||||
let (char_index, mut vflip, mut hflip, palette_index) = utils::parse_cell(cell);
|
||||
TableBuilder::new(ui)
|
||||
.column(Column::remainder())
|
||||
.column(Column::remainder())
|
||||
|
@ -250,12 +245,7 @@ impl BgMapRenderer {
|
|||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let CellData {
|
||||
char_index,
|
||||
vflip,
|
||||
hflip,
|
||||
palette_index,
|
||||
} = CellData::parse(*cell);
|
||||
let (char_index, vflip, hflip, palette_index) = utils::parse_cell(*cell);
|
||||
let char = chardata.range::<u16>(char_index * 8, 8);
|
||||
let palette = &colors[palette_index];
|
||||
|
||||
|
@ -279,12 +269,7 @@ impl BgMapRenderer {
|
|||
|
||||
let cell = bgmaps.read::<u16>(index);
|
||||
|
||||
let CellData {
|
||||
char_index,
|
||||
vflip,
|
||||
hflip,
|
||||
palette_index,
|
||||
} = CellData::parse(cell);
|
||||
let (char_index, vflip, hflip, palette_index) = utils::parse_cell(cell);
|
||||
let char = chardata.range::<u16>(char_index * 8, 8);
|
||||
let palette = if generic_palette {
|
||||
utils::generic_palette(Color32::RED)
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
},
|
||||
};
|
||||
|
||||
use super::utils::{self, Object};
|
||||
use super::utils;
|
||||
|
||||
pub struct ObjectWindow {
|
||||
sim_id: SimId,
|
||||
|
@ -31,9 +31,8 @@ pub struct ObjectWindow {
|
|||
impl ObjectWindow {
|
||||
pub fn new(sim_id: SimId, memory: &mut MemoryMonitor, vram: &mut VramProcessor) -> Self {
|
||||
let renderer = ObjectRenderer::new(sim_id, memory);
|
||||
let ([zoom, full], params) = vram.add(renderer);
|
||||
let loader =
|
||||
VramTextureLoader::new([("vram://zoom".into(), zoom), ("vram://full".into(), full)]);
|
||||
let ([object], params) = vram.add(renderer);
|
||||
let loader = VramTextureLoader::new([("vram://object".into(), object)]);
|
||||
Self {
|
||||
sim_id,
|
||||
loader: Arc::new(loader),
|
||||
|
@ -60,27 +59,14 @@ impl ObjectWindow {
|
|||
ui.add(NumberEdit::new(&mut self.index).range(0..1024));
|
||||
});
|
||||
});
|
||||
body.row(row_height, |mut row| {
|
||||
row.col(|ui| {
|
||||
ui.label("Address");
|
||||
});
|
||||
row.col(|ui| {
|
||||
let address = 0x3e000 + self.index * 8;
|
||||
let mut address_str = format!("{address:08x}");
|
||||
ui.add_enabled(
|
||||
false,
|
||||
TextEdit::singleline(&mut address_str).horizontal_align(Align::Max),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
let image = Image::new("vram://zoom")
|
||||
.maintain_aspect_ratio(true)
|
||||
.texture_options(TextureOptions::NEAREST);
|
||||
ui.add(image);
|
||||
ui.section("Properties", |ui| {
|
||||
let object = self.objects.borrow().read::<[u16; 4]>(self.index);
|
||||
let mut obj = Object::parse(object);
|
||||
let mut x = ((object[0] & 0x3ff) << 6 >> 6) as i16;
|
||||
let mut parallax = ((object[1] & 0x3ff) << 6 >> 6) as i16;
|
||||
let mut y = ((object[2] & 0x0ff) << 8 >> 8) as i16;
|
||||
let (mut char_index, mut vflip, mut hflip, palette_index) =
|
||||
utils::parse_cell(object[3]);
|
||||
TableBuilder::new(ui)
|
||||
.column(Column::remainder())
|
||||
.column(Column::remainder())
|
||||
|
@ -92,7 +78,7 @@ impl ObjectWindow {
|
|||
row.col(|ui| {
|
||||
ui.add_enabled(
|
||||
false,
|
||||
NumberEdit::new(&mut obj.data.char_index).range(0..2048),
|
||||
NumberEdit::new(&mut char_index).range(0..2048),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -101,7 +87,7 @@ impl ObjectWindow {
|
|||
ui.label("Palette");
|
||||
});
|
||||
row.col(|ui| {
|
||||
let mut palette = format!("OBJ {}", obj.data.palette_index);
|
||||
let mut palette = format!("OBJ {}", palette_index);
|
||||
ui.add_enabled(
|
||||
false,
|
||||
TextEdit::singleline(&mut palette).horizontal_align(Align::Max),
|
||||
|
@ -113,7 +99,7 @@ impl ObjectWindow {
|
|||
ui.label("X");
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.add_enabled(false, NumberEdit::new(&mut obj.x).range(-512..512));
|
||||
ui.add_enabled(false, NumberEdit::new(&mut x).range(-512..512));
|
||||
});
|
||||
});
|
||||
body.row(row_height, |mut row| {
|
||||
|
@ -121,7 +107,7 @@ impl ObjectWindow {
|
|||
ui.label("Y");
|
||||
});
|
||||
row.col(|ui| {
|
||||
ui.add_enabled(false, NumberEdit::new(&mut obj.y).range(-8..=224));
|
||||
ui.add_enabled(false, NumberEdit::new(&mut y).range(-8..=224));
|
||||
});
|
||||
});
|
||||
body.row(row_height, |mut row| {
|
||||
|
@ -131,27 +117,17 @@ impl ObjectWindow {
|
|||
row.col(|ui| {
|
||||
ui.add_enabled(
|
||||
false,
|
||||
NumberEdit::new(&mut obj.parallax).range(-512..512),
|
||||
NumberEdit::new(&mut parallax).range(-512..512),
|
||||
);
|
||||
});
|
||||
});
|
||||
body.row(row_height, |mut row| {
|
||||
row.col(|ui| {
|
||||
let checkbox = Checkbox::new(&mut obj.data.hflip, "H-flip");
|
||||
let checkbox = Checkbox::new(&mut hflip, "H-flip");
|
||||
ui.add_enabled(false, checkbox);
|
||||
});
|
||||
row.col(|ui| {
|
||||
let checkbox = Checkbox::new(&mut obj.data.vflip, "V-flip");
|
||||
ui.add_enabled(false, checkbox);
|
||||
});
|
||||
});
|
||||
body.row(row_height, |mut row| {
|
||||
row.col(|ui| {
|
||||
let checkbox = Checkbox::new(&mut obj.lon, "Left");
|
||||
ui.add_enabled(false, checkbox);
|
||||
});
|
||||
row.col(|ui| {
|
||||
let checkbox = Checkbox::new(&mut obj.ron, "Right");
|
||||
let checkbox = Checkbox::new(&mut vflip, "V-flip");
|
||||
ui.add_enabled(false, checkbox);
|
||||
});
|
||||
});
|
||||
|
@ -177,7 +153,7 @@ impl ObjectWindow {
|
|||
}
|
||||
|
||||
fn show_object(&mut self, ui: &mut Ui) {
|
||||
let image = Image::new("vram://full")
|
||||
let image = Image::new("vram://object")
|
||||
.fit_to_original_size(self.scale)
|
||||
.texture_options(TextureOptions::NEAREST);
|
||||
ui.add(image);
|
||||
|
@ -196,7 +172,7 @@ impl AppWindow for ObjectWindow {
|
|||
fn initial_viewport(&self) -> ViewportBuilder {
|
||||
ViewportBuilder::default()
|
||||
.with_title(format!("Object Data ({})", self.sim_id))
|
||||
.with_inner_size((640.0, 500.0))
|
||||
.with_inner_size((640.0, 480.0))
|
||||
}
|
||||
|
||||
fn on_init(&mut self, ctx: &Context, _render_state: &egui_wgpu::RenderState) {
|
||||
|
@ -263,55 +239,50 @@ impl ObjectRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
fn render_object(&self, image: &mut VramImage, params: &ObjectParams, use_pos: bool, eye: Eye) {
|
||||
fn render_object(&self, image: &mut VramImage, params: &ObjectParams, eye: Eye) {
|
||||
let chardata = self.chardata.borrow();
|
||||
let objects = self.objects.borrow();
|
||||
let brightness = self.brightness.borrow();
|
||||
let palettes = self.palettes.borrow();
|
||||
|
||||
let object: [u16; 4] = objects.read(params.index);
|
||||
let obj = Object::parse(object);
|
||||
|
||||
let ron = object[1] & 0x4000 != 0;
|
||||
let lon = object[1] & 0x8000 != 0;
|
||||
if match eye {
|
||||
Eye::Left => !obj.lon,
|
||||
Eye::Right => !obj.ron,
|
||||
Eye::Left => !lon,
|
||||
Eye::Right => !ron,
|
||||
} {
|
||||
return;
|
||||
}
|
||||
|
||||
let brts = brightness.range::<u8>(0, 8);
|
||||
let (x, y) = if use_pos {
|
||||
let x = match eye {
|
||||
Eye::Left => obj.x - obj.parallax,
|
||||
Eye::Right => obj.x + obj.parallax,
|
||||
};
|
||||
(x, obj.y)
|
||||
} else {
|
||||
(0, 0)
|
||||
|
||||
let x = ((object[0] & 0x3ff) << 6 >> 6) as i16;
|
||||
let parallax = ((object[1] & 0x3ff) << 6 >> 6) as i16;
|
||||
let y = ((object[2] & 0x0ff) << 8 >> 8) as i16;
|
||||
|
||||
let (x, color) = match eye {
|
||||
Eye::Left => (x - parallax, params.left_color),
|
||||
Eye::Right => (x + parallax, params.right_color),
|
||||
};
|
||||
|
||||
let color = match eye {
|
||||
Eye::Left => params.left_color,
|
||||
Eye::Right => params.right_color,
|
||||
};
|
||||
|
||||
let char = chardata.range::<u16>(obj.data.char_index * 8, 8);
|
||||
let (char_index, vflip, hflip, palette_index) = utils::parse_cell(object[3]);
|
||||
let char = chardata.range::<u16>(char_index * 8, 8);
|
||||
let palette = if params.generic_palette {
|
||||
utils::generic_palette(color)
|
||||
} else {
|
||||
utils::parse_palette(palettes.read(8 + obj.data.palette_index * 2), brts, color)
|
||||
utils::parse_palette(palettes.read(8 + palette_index * 2), brts, color)
|
||||
};
|
||||
|
||||
for row in 0..8 {
|
||||
let real_y = y + row as i16;
|
||||
if !(0..224).contains(&real_y) {
|
||||
if !(0..384).contains(&real_y) {
|
||||
continue;
|
||||
}
|
||||
for (col, pixel) in
|
||||
utils::read_char_row(char, obj.data.hflip, obj.data.vflip, row).enumerate()
|
||||
{
|
||||
for (col, pixel) in utils::read_char_row(char, hflip, vflip, row).enumerate() {
|
||||
let real_x = x + col as i16;
|
||||
if !(0..384).contains(&real_x) {
|
||||
if !(0..224).contains(&real_x) {
|
||||
continue;
|
||||
}
|
||||
image.add((real_x as usize, real_y as usize), palette[pixel as usize]);
|
||||
|
@ -320,19 +291,17 @@ impl ObjectRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
impl VramRenderer<2> for ObjectRenderer {
|
||||
impl VramRenderer<1> for ObjectRenderer {
|
||||
type Params = ObjectParams;
|
||||
|
||||
fn sizes(&self) -> [[usize; 2]; 2] {
|
||||
[[8, 8], [384, 224]]
|
||||
fn sizes(&self) -> [[usize; 2]; 1] {
|
||||
[[384, 224]]
|
||||
}
|
||||
|
||||
fn render(&mut self, params: &Self::Params, images: &mut [VramImage; 2]) {
|
||||
images[0].clear();
|
||||
self.render_object(&mut images[0], params, false, Eye::Left);
|
||||
self.render_object(&mut images[0], params, false, Eye::Right);
|
||||
images[1].clear();
|
||||
self.render_object(&mut images[1], params, true, Eye::Left);
|
||||
self.render_object(&mut images[1], params, true, Eye::Right);
|
||||
fn render(&mut self, params: &Self::Params, images: &mut [VramImage; 1]) {
|
||||
let image = &mut images[0];
|
||||
image.clear();
|
||||
self.render_object(image, params, Eye::Left);
|
||||
self.render_object(image, params, Eye::Right);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,57 +28,12 @@ pub fn parse_palette(palette: u8, brts: &[u8], color: Color32) -> [Color32; 4] {
|
|||
]
|
||||
}
|
||||
|
||||
pub struct Object {
|
||||
pub x: i16,
|
||||
pub lon: bool,
|
||||
pub ron: bool,
|
||||
pub parallax: i16,
|
||||
pub y: i16,
|
||||
pub data: CellData,
|
||||
}
|
||||
|
||||
impl Object {
|
||||
pub fn parse(object: [u16; 4]) -> Self {
|
||||
let x = ((object[0] & 0x3ff) << 6 >> 6) as i16;
|
||||
let parallax = ((object[1] & 0x3ff) << 6 >> 6) as i16;
|
||||
let lon = object[1] & 0x8000 != 0;
|
||||
let ron = object[1] & 0x4000 != 0;
|
||||
let y = (object[2] & 0x0ff) as i16;
|
||||
// Y is stored as the bottom 8 bits of an i16,
|
||||
// so only sign extend if it's out of range.
|
||||
let y = if y > 224 { y << 8 >> 8 } else { y };
|
||||
let data = CellData::parse(object[3]);
|
||||
Self {
|
||||
x,
|
||||
lon,
|
||||
ron,
|
||||
parallax,
|
||||
y,
|
||||
data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CellData {
|
||||
pub palette_index: usize,
|
||||
pub hflip: bool,
|
||||
pub vflip: bool,
|
||||
pub char_index: usize,
|
||||
}
|
||||
|
||||
impl CellData {
|
||||
pub fn parse(cell: u16) -> Self {
|
||||
let char_index = (cell & 0x7ff) as usize;
|
||||
let vflip = cell & 0x1000 != 0;
|
||||
let hflip = cell & 0x2000 != 0;
|
||||
let palette_index = (cell >> 14) as usize;
|
||||
Self {
|
||||
char_index,
|
||||
vflip,
|
||||
hflip,
|
||||
palette_index,
|
||||
}
|
||||
}
|
||||
pub fn parse_cell(cell: u16) -> (usize, bool, bool, usize) {
|
||||
let char_index = (cell & 0x7ff) as usize;
|
||||
let vflip = cell & 0x1000 != 0;
|
||||
let hflip = cell & 0x2000 != 0;
|
||||
let palette_index = (cell >> 14) as usize;
|
||||
(char_index, vflip, hflip, palette_index)
|
||||
}
|
||||
|
||||
pub fn read_char_row(
|
||||
|
|
Loading…
Reference in New Issue