new rendering system

This commit is contained in:
Able 2024-10-01 08:12:09 -05:00
parent 357ef6c8e2
commit bf70ab9154
7 changed files with 323 additions and 81 deletions

View file

@ -1,18 +1,42 @@
#[derive(Debug, Clone, Copy, PartialEq)] use std::ops::BitAnd;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum CellType { pub enum CellType {
Empty = 0, Empty = 0,
Sand, Sand,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum MovementType { pub enum MovementType {
None = 0b0000_0000, None = 0b0000_0000,
Down = 0b0000_0001, Down = 0b0000_0001,
// 000
// 1X1
// 000
Side = 0b0000_0010, Side = 0b0000_0010,
// 000
// 0X0
// 111
DownSide = 0b0000_0011, DownSide = 0b0000_0011,
} }
impl BitAnd for MovementType {
type Output = Self;
#[derive(Debug, Clone, Copy)] // rhs is the "right-hand side" of the expression `a & b`
fn bitand(self, rhs: Self) -> Self::Output {
let a = self as u8 & rhs as u8;
use MovementType::*;
match a {
0 => None,
1 => Down,
2 => Side,
3 => DownSide,
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Cell { pub struct Cell {
pub cell_type: CellType, pub cell_type: CellType,
pub movement_type: MovementType, pub movement_type: MovementType,

View file

@ -1,23 +1,48 @@
use std::io::Empty;
use minifb::{Key, Window, WindowOptions}; use minifb::{Key, Window, WindowOptions};
const WIDTH: usize = 800; const WIDTH: usize = 800;
const HEIGHT: usize = 600; const HEIGHT: usize = 600;
use crate::{materials::*, rand::Random, world::World}; use crate::{
cell::{Cell, CellType, MovementType},
materials::*,
rand::Random,
tile::{Tile, PLAYER},
world::{in_bounds, xy_to_i, World},
};
struct Inventory {
current_selected: usize,
left_hand_side: [Cell; 10],
right_hand_side: [Cell; 10],
}
pub struct Engine { pub struct Engine {
pub window: Window, pub window: Window,
pub front_buffer: Vec<u32>, pub front_buffer: Vec<u32>,
pub brush_size: usize, pub brush_size: usize,
pub random: Random,
inventory: Inventory,
pub world: World, pub world: World,
tiles: Vec<Tile>,
} }
impl Engine { impl Engine {
pub fn new() -> Self { pub fn new() -> Self {
let inventory = Inventory {
current_selected: 0,
left_hand_side: [
SAND_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL,
EMPTY_CELL, EMPTY_CELL, EMPTY_CELL,
],
right_hand_side: [
EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL, EMPTY_CELL,
EMPTY_CELL, EMPTY_CELL, EMPTY_CELL,
],
};
Self { Self {
window: Window::new( window: Window::new(
"Rising Sands :P | Hit ESC to exit", "Rising Sands :P | Hit ESC to exit",
@ -30,14 +55,13 @@ impl Engine {
}), }),
front_buffer: vec![0; WIDTH * HEIGHT], front_buffer: vec![0; WIDTH * HEIGHT],
brush_size: 1, brush_size: 1,
random: Random::new(0),
world: World::new(), world: World::new(),
inventory,
tiles: vec![PLAYER],
} }
} }
pub fn run(&mut self) { pub fn run(&mut self) {
self.clear();
self.window.set_target_fps(60); self.window.set_target_fps(60);
while self.window.is_open() { while self.window.is_open() {
@ -52,33 +76,34 @@ impl Engine {
self.brush_size = self.brush_size.saturating_sub(1); self.brush_size = self.brush_size.saturating_sub(1);
} }
// TODO: something wrong here? self.handle_mouse();
if self.window.get_mouse_down(minifb::MouseButton::Left) {
// TODO: Swap to mm::Discard and handle the mouse outside of the window
let pos = self.window.get_mouse_pos(minifb::MouseMode::Clamp).unwrap();
let half = self.brush_size / 2;
let mouse_x = pos.0 as usize - half; // TODO: make a self.handle_player_movement() to handle the player movement stuff
let mouse_y = pos.1 as usize - half; // BUG: panics when player moves partially off screen.
if self.window.is_key_down(Key::A) {
for x in 0..self.brush_size { self.tiles[0].x -= 1;
for y in 0..self.brush_size {
self.set_cell(mouse_x + x, mouse_y + y, SAND);
} }
if self.window.is_key_down(Key::D) {
self.tiles[0].x += 1;
} }
if self.window.is_key_down(Key::W) {
self.tiles[0].y -= 1;
}
if self.window.is_key_down(Key::S) {
self.tiles[0].y += 1;
} }
if self.window.get_mouse_down(minifb::MouseButton::Right) { for tile in &self.tiles {
// TODO: Swap to mm::Discard and handle the mouse outside of the window for x in 0..8 {
let pos = self.window.get_mouse_pos(minifb::MouseMode::Clamp).unwrap(); for y in 0..8 {
let half = self.brush_size / 2; if in_bounds(x, y) {
// what happens if the cell is already full?
let material = tile.values[y][x];
let mouse_x = pos.0 as usize - half; if material != EMPTY_CELL {
let mouse_y = pos.1 as usize - half; self.world.set_xy(x + tile.x, tile.y + y, material);
}
for x in 0..self.brush_size { }
for y in 0..self.brush_size {
self.set_cell(mouse_x + x, mouse_y + y, EMPTY);
} }
} }
} }
@ -86,64 +111,120 @@ impl Engine {
for x in 0..WIDTH { for x in 0..WIDTH {
for y in 0..HEIGHT { for y in 0..HEIGHT {
let index = xy_to_i(x, y); let index = xy_to_i(x, y);
let cell = self.front_buffer[index]; let cell = self.world.cells[index];
let cell_move = cell.movement_type;
use MovementType::*;
match cell_move {
None => {}
Down => self.world.move_down(x, y),
Side => todo!(),
DownSide => self.world.move_down_side(x, y),
}
}
}
self.world.commit_cells();
if cell == SAND { for x in 0..WIDTH {
let down = self.is_empty(x, y + 1); for y in 0..HEIGHT {
let random = self.random.random_bool(); let index = xy_to_i(x, y);
let left = self.is_empty(x - 1, y + 1) && random; let cell = self.world.cells[index];
let right = self.is_empty(x + 1, y + 1) ^ left;
if down { self.front_buffer[index] = cell.color;
self.set_cell(x, y + 1, SAND) }
} else if left {
self.set_cell(x - 1, y + 1, SAND)
} else if right {
self.set_cell(x + 1, y + 1, SAND)
} }
if down || left || right { let pos = self.window.get_mouse_pos(minifb::MouseMode::Discard);
self.set_cell(x, y, EMPTY); if pos.is_some() {
let pos = pos.unwrap();
let half = self.brush_size / 2;
let mouse_x = pos.0 as usize - half;
let mouse_y = pos.1 as usize - half;
for x in 0..self.brush_size {
if in_bounds(mouse_x + x, mouse_y) {
let index = xy_to_i(mouse_x + x, mouse_y);
self.front_buffer[index] = 0x00_ff_ff_ff;
} }
} }
for x in 0..self.brush_size {
if in_bounds(mouse_x + x, mouse_y + self.brush_size) {
let index = xy_to_i(mouse_x + x, mouse_y + self.brush_size);
self.front_buffer[index] = 0x00_ff_ff_ff;
} }
} }
for y in 0..self.brush_size {
if in_bounds(mouse_x, mouse_y + y) {
let index = xy_to_i(mouse_x, mouse_y + y);
self.front_buffer[index] = 0x00_ff_ff_ff;
}
}
for y in 0..self.brush_size {
if in_bounds(mouse_x + self.brush_size, mouse_y + y) {
let index = xy_to_i(mouse_x + self.brush_size, mouse_y + y);
self.front_buffer[index] = 0x00_ff_ff_ff;
}
}
// for x in 0..self.brush_size {
// for y in 0..self.brush_size {
// if in_bounds(mouse_x + x, mouse_y + y) {
// let index = xy_to_i(mouse_x + x, mouse_y + y);
// self.front_buffer[index] = 0x00_ff_ff_ff;
// }
// }
// }
}
// TODO Handle this. // TODO Handle this.
self.window self.window
.update_with_buffer(&self.front_buffer, WIDTH, HEIGHT) .update_with_buffer(&self.front_buffer, WIDTH, HEIGHT)
.unwrap(); .unwrap();
}
}
}
impl Engine { for tile in &self.tiles {
fn is_empty(&mut self, x: usize, y: usize) -> bool { for x in 0..8 {
let index = xy_to_i(x, y); for y in 0..8 {
if index >= self.front_buffer.len() { // x += tile.x;
false // y += tile.y;
if in_bounds(x, y) {
// what happens if the cell is already full?
self.world.set_xy(x + tile.x, tile.y + y, EMPTY_CELL);
}
}
}
}
}
}
pub fn handle_mouse(&mut self) {
let material = if self.window.get_mouse_down(minifb::MouseButton::Left) {
self.inventory.left_hand_side[self.inventory.current_selected]
} else if self.window.get_mouse_down(minifb::MouseButton::Right) {
self.inventory.right_hand_side[self.inventory.current_selected]
} else { } else {
self.front_buffer[index] == EMPTY return;
} };
}
fn set_cell(&mut self, x: usize, y: usize, mat: u32) { // TODO: Swap to MouseMode::Discard and handle the mouse outside of the window
let index = xy_to_i(x, y); let pos = self.window.get_mouse_pos(minifb::MouseMode::Discard);
if index >= self.front_buffer.len() { if pos.is_some() {
} else { let pos = pos.unwrap();
self.front_buffer[index] = mat; let half = self.brush_size / 2;
}
}
fn clear(&mut self) { let mouse_x = pos.0 as usize - half;
for x in 0..WIDTH { let mouse_y = pos.1 as usize - half;
for y in 0..HEIGHT {
let index = xy_to_i(x, y); for x in 0..self.brush_size {
self.front_buffer[index] = EMPTY; for y in 0..self.brush_size {
if in_bounds(mouse_x + x, mouse_y + y) {
self.world.set_xy(mouse_x + x, mouse_y + y, material);
}
}
} }
} }
} }
} }
pub fn xy_to_i(x: usize, y: usize) -> usize {
x + WIDTH * y
}

View file

@ -2,6 +2,7 @@ mod cell;
mod engine; mod engine;
mod materials; mod materials;
mod rand; mod rand;
mod tile;
mod world; mod world;
fn main() { fn main() {

View file

@ -1,2 +1,22 @@
use crate::cell::Cell;
use crate::cell::CellType::*;
use crate::cell::MovementType::*;
pub const EMPTY: u32 = 0x00_00_00; pub const EMPTY: u32 = 0x00_00_00;
pub const SAND: u32 = 0xFF_99_50; pub const SAND: u32 = 0xFF_99_50;
pub const EMPTY_CELL: Cell = Cell {
cell_type: Empty,
movement_type: None,
color: 0x0,
};
pub const SAND_CELL: Cell = Cell {
cell_type: Sand,
movement_type: DownSide,
color: 0xFF_99_50,
};
//SAND_START_COLOR_RANGE = 0x00_f6_D7_B0
//SAND_END_COLOR_RANGE = 0x00_e1_bf_92

View file

@ -18,4 +18,12 @@ impl Random {
pub fn random_bool(&mut self) -> bool { pub fn random_bool(&mut self) -> bool {
self.rng.gen_bool(0.5) self.rng.gen_bool(0.5)
} }
pub fn color_range(&mut self, start: u32, end: u32) -> u32 {
if start == end {
0
} else {
self.rng.gen_range(start..=end)
}
}
} }

33
src/tile.rs Normal file
View file

@ -0,0 +1,33 @@
use crate::{cell::Cell, materials::EMPTY_CELL};
pub struct Tile {
pub x: usize,
pub y: usize,
pub values: [[Cell; 8]; 8],
}
const SKIN: Cell = Cell {
cell_type: crate::cell::CellType::Sand,
movement_type: crate::cell::MovementType::None,
color: 0x00_Fe_9f_13,
};
const EYE: Cell = Cell {
cell_type: crate::cell::CellType::Sand,
movement_type: crate::cell::MovementType::None,
color: 0x00_00_5e_be,
};
const NORMAL_ROW: [Cell; 8] = [
EMPTY_CELL, EMPTY_CELL, SKIN, SKIN, SKIN, SKIN, EMPTY_CELL, EMPTY_CELL,
];
const EYE_ROW: [Cell; 8] = [
EMPTY_CELL, EMPTY_CELL, EYE, SKIN, SKIN, EYE, EMPTY_CELL, EMPTY_CELL,
];
pub const PLAYER: Tile = Tile {
x: 100,
y: 100,
values: [
NORMAL_ROW, EYE_ROW, NORMAL_ROW, NORMAL_ROW, NORMAL_ROW, NORMAL_ROW, NORMAL_ROW, NORMAL_ROW,
],
};

View file

@ -1,23 +1,26 @@
use crate::cell::{Cell, MovementType}; use crate::cell::{Cell, MovementType};
use crate::cell::CellType::Empty; use crate::cell::CellType::Empty;
use crate::engine::xy_to_i; use crate::materials::{EMPTY, EMPTY_CELL};
use crate::materials::EMPTY; use crate::rand::Random;
const WIDTH: usize = 800; const WIDTH: usize = 800;
const HEIGHT: usize = 600; const HEIGHT: usize = 600;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Change { pub struct Change {
x_from: usize, x_from: usize,
x_to: usize, x_to: usize,
y_from: usize, y_from: usize,
y_to: usize, y_to: usize,
} }
// #[derive(Ord)]
pub struct World { pub struct World {
pub width: usize, pub width: usize,
pub height: usize, pub height: usize,
pub cells: Vec<Cell>, pub cells: Vec<Cell>,
changes: Vec<Change>, changes: Vec<Change>,
pub random: Random,
} }
impl World { impl World {
pub fn new() -> Self { pub fn new() -> Self {
@ -33,6 +36,7 @@ impl World {
WIDTH * HEIGHT WIDTH * HEIGHT
], ],
changes: vec![], changes: vec![],
random: Random::new(0),
} }
} }
} }
@ -68,7 +72,74 @@ impl World {
self.changes.push(change); self.changes.push(change);
} }
// Takes the changes to the world and applies them. // Takes the changes to the world and applies them.
pub fn commit_cells() {} pub fn commit_cells(&mut self) {
let mut changes_length: usize = self.changes.len();
if changes_length == 0 {
// println!("WARN: Empty changes.");
return;
}
let mut cell_index = 0;
// while cell_index <= changes_length {
// let cell = &self.changes[cell_index];
// let first_cell = self.get_xy(cell.x_from, cell.y_from);
// if first_cell.cell_type != Empty {
// self.changes[cell_index] = *self.changes.last().unwrap();
// let _ = self.changes.pop();
// // m_changes.pop_back();
// changes_length -= 1;
// }
// cell_index += 1;
// }
// Sort the changes.
self.changes.sort();
let changes_length: usize = self.changes.len();
// TODO: Apply these changes in random order
for index in 0..changes_length {
let dst_x = self.changes[index].x_to;
let dst_y = self.changes[index].y_to;
let src_x = self.changes[index].x_from;
let src_y = self.changes[index].y_from;
let cell = self.get_xy(src_x, src_y);
self.set_xy(dst_x, dst_y, cell);
self.set_xy(src_x, src_y, EMPTY_CELL);
}
self.changes = vec![];
}
}
impl World {
pub fn move_down(&mut self, x: usize, y: usize) {
if self.is_empty(x, y + 1) {
self.move_cell(x, y, x, y + 1);
}
}
pub fn move_down_side(&mut self, x: usize, y: usize) {
let mut down_left = self.is_empty(x - 1, y + 1);
let mut down_right = self.is_empty(x + 1, y + 1);
let down = self.is_empty(x, y + 1);
if down_left && down_right {
down_left = self.random.random_bool();
down_right = down_right != down_left;
}
if down_left {
self.move_cell(x, y, x - 1, y + 1);
} else if down_right {
self.move_cell(x, y, x + 1, y + 1);
} else if down {
self.move_cell(x, y, x, y + 1);
}
}
} }
pub fn in_bounds(x: usize, y: usize) -> bool { pub fn in_bounds(x: usize, y: usize) -> bool {
@ -77,3 +148,7 @@ pub fn in_bounds(x: usize, y: usize) -> bool {
x_in_bounds && y_in_bounds x_in_bounds && y_in_bounds
} }
pub fn xy_to_i(x: usize, y: usize) -> usize {
x + WIDTH * y
}