159 lines
4.3 KiB
Rust
159 lines
4.3 KiB
Rust
use std::collections::{
|
|
hash_map::{Iter, IterMut},
|
|
HashMap,
|
|
};
|
|
|
|
use bevy::prelude::*;
|
|
|
|
mod chunk2d;
|
|
mod texel2d;
|
|
|
|
pub use chunk2d::*;
|
|
pub use texel2d::*;
|
|
|
|
use crate::util::{math::*, Vector2I};
|
|
|
|
pub struct Terrain2DPlugin;
|
|
|
|
impl Plugin for Terrain2DPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app.register_type::<Chunk2DIndex>()
|
|
.insert_resource(Terrain2D::new())
|
|
.add_event::<TerrainEvent>()
|
|
.add_system(emit_terrain_events)
|
|
.add_system(chunk_spawner);
|
|
}
|
|
}
|
|
|
|
fn emit_terrain_events(
|
|
mut terrain: ResMut<Terrain2D>,
|
|
mut terrain_events: EventWriter<TerrainEvent>,
|
|
) {
|
|
for event in terrain.events.drain(..) {
|
|
terrain_events.send(event)
|
|
}
|
|
for (chunk_index, mut chunk) in terrain.chunk_iter_mut() {
|
|
if let Some(rect) = &chunk.dirty_rect {
|
|
terrain_events.send(TerrainEvent::TexelsUpdated(*chunk_index, *rect));
|
|
chunk.dirty_rect = None;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub enum TerrainEvent {
|
|
ChunkAdded(ChunkIndex),
|
|
ChunkRemoved(ChunkIndex),
|
|
TexelsUpdated(ChunkIndex, ChunkRect),
|
|
}
|
|
|
|
#[derive(Default, Resource)]
|
|
pub struct Terrain2D {
|
|
chunk_map: HashMap<ChunkIndex, Chunk2D>,
|
|
events: Vec<TerrainEvent>,
|
|
}
|
|
|
|
impl Terrain2D {
|
|
pub fn new() -> Terrain2D {
|
|
Terrain2D {
|
|
chunk_map: HashMap::new(),
|
|
events: Vec::new(),
|
|
}
|
|
}
|
|
|
|
pub fn add_chunk(&mut self, index: ChunkIndex, chunk: Chunk2D) {
|
|
self.chunk_map.insert(index, chunk);
|
|
self.events.push(TerrainEvent::ChunkAdded(index))
|
|
}
|
|
|
|
pub fn remove_chunk(&mut self, index: ChunkIndex) {
|
|
self.events.push(TerrainEvent::ChunkRemoved(index));
|
|
self.chunk_map.remove(&index);
|
|
}
|
|
|
|
pub fn chunk_iter(&self) -> Iter<ChunkIndex, Chunk2D> {
|
|
self.chunk_map.iter()
|
|
}
|
|
|
|
pub fn chunk_iter_mut(&mut self) -> IterMut<ChunkIndex, Chunk2D> {
|
|
self.chunk_map.iter_mut()
|
|
}
|
|
|
|
pub fn index_to_chunk(&self, index: &ChunkIndex) -> Option<&Chunk2D> {
|
|
self.chunk_map.get(index)
|
|
}
|
|
|
|
pub fn index_to_chunk_mut(&mut self, index: &ChunkIndex) -> Option<&mut Chunk2D> {
|
|
self.chunk_map.get_mut(index)
|
|
}
|
|
|
|
pub fn global_to_chunk(&self, global: &Vector2I) -> Option<&Chunk2D> {
|
|
self.index_to_chunk(&global_to_chunk_index(global))
|
|
}
|
|
|
|
pub fn global_to_chunk_mut(&mut self, global: &Vector2I) -> Option<&mut Chunk2D> {
|
|
self.index_to_chunk_mut(&global_to_chunk_index(global))
|
|
}
|
|
|
|
pub fn global_to_texel(&self, global: &Vector2I) -> Option<Texel2D> {
|
|
match self.global_to_chunk(global) {
|
|
Some(chunk) => chunk.get_texel(&global_to_local(global)),
|
|
None => None,
|
|
}
|
|
}
|
|
|
|
pub fn global_to_texel_mut(&mut self, global: &Vector2I) -> Option<Texel2D> {
|
|
match self.global_to_chunk(global) {
|
|
Some(chunk) => chunk.get_texel(&global_to_local(global)),
|
|
None => None,
|
|
}
|
|
}
|
|
|
|
pub fn set_texel(&mut self, global: &Vector2I, id: TexelID) {
|
|
let index = global_to_chunk_index(global);
|
|
match self.index_to_chunk_mut(&index) {
|
|
Some(chunk) => chunk.set_texel(&global_to_local(global), id),
|
|
None => {
|
|
let mut chunk = Chunk2D::new();
|
|
chunk.set_texel(&global_to_local(global), id);
|
|
self.add_chunk(index, chunk);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn local_to_texel_index(position: &Vector2I) -> Option<usize> {
|
|
match position.x >= 0
|
|
&& position.y >= 0
|
|
&& position.x < Chunk2D::SIZE.x
|
|
&& position.y < Chunk2D::SIZE.y
|
|
{
|
|
true => Some(position.y as usize * Chunk2D::SIZE_X + position.x as usize),
|
|
false => None,
|
|
}
|
|
}
|
|
|
|
pub fn texel_index_to_local(i: usize) -> Vector2I {
|
|
Vector2I {
|
|
x: i as i32 % Chunk2D::SIZE.x,
|
|
y: i as i32 / Chunk2D::SIZE.y,
|
|
}
|
|
}
|
|
|
|
pub fn global_to_local(position: &Vector2I) -> Vector2I {
|
|
Vector2I {
|
|
x: wrapping_remainder(position.x, Chunk2D::SIZE.x),
|
|
y: wrapping_remainder(position.y, Chunk2D::SIZE.y),
|
|
}
|
|
}
|
|
|
|
pub fn global_to_chunk_index(position: &Vector2I) -> ChunkIndex {
|
|
Vector2I {
|
|
x: wrapping_quotient(position.x, Chunk2D::SIZE.x),
|
|
y: wrapping_quotient(position.y, Chunk2D::SIZE.y),
|
|
}
|
|
}
|
|
|
|
pub fn chunk_index_to_global(chunk_pos: &ChunkIndex) -> Vector2I {
|
|
*chunk_pos * Chunk2D::SIZE
|
|
}
|