updated texel and chunk structures to allow easier simulation
parent
c42859ded8
commit
c3ea9f4513
|
|
@ -1,7 +1,5 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
// use bevy_inspector_egui::*;
|
|
||||||
use bevy_prototype_debug_lines::DebugLinesPlugin;
|
use bevy_prototype_debug_lines::DebugLinesPlugin;
|
||||||
// use bevy_rapier2d::prelude::*;
|
|
||||||
|
|
||||||
mod terrain;
|
mod terrain;
|
||||||
|
|
||||||
|
|
@ -12,8 +10,8 @@ pub struct DebugPlugin;
|
||||||
impl Plugin for DebugPlugin {
|
impl Plugin for DebugPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_plugin(DebugLinesPlugin::default())
|
app.add_plugin(DebugLinesPlugin::default())
|
||||||
// .add_plugin(RapierDebugRenderPlugin::default())
|
// .add_plugin(bevy_rapier2d::prelude::RapierDebugRenderPlugin::default())
|
||||||
// .add_plugin(WorldInspectorPlugin::new())
|
// .add_plugin(bevy_inspector_egui::WorldInspectorPlugin::new())
|
||||||
.add_plugin(TerrainDebugPlugin);
|
.add_plugin(TerrainDebugPlugin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ fn debug_painter(
|
||||||
);
|
);
|
||||||
if mouse_input.pressed(MouseButton::Left) || mouse_input.pressed(MouseButton::Right)
|
if mouse_input.pressed(MouseButton::Left) || mouse_input.pressed(MouseButton::Right)
|
||||||
{
|
{
|
||||||
terrain.set_texel(&pos, id, None)
|
terrain.set_texel(&pos, Texel2D { id, ..default() }, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,8 +120,8 @@ fn terrain_simulation(mut terrain: ResMut<Terrain2D>, frame_counter: Res<FrameCo
|
||||||
let global = local_to_global(&local, &chunk_index);
|
let global = local_to_global(&local, &chunk_index);
|
||||||
|
|
||||||
if terrain
|
if terrain
|
||||||
.get_texel(&global)
|
.get_latest_simulation(&global)
|
||||||
.map_or(true, |t| t.last_simulation == simulation_frame)
|
.map_or(true, |frame| frame == simulation_frame)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
@ -305,6 +305,12 @@ impl Terrain2D {
|
||||||
.map_or(None, |chunk| chunk.get_texel(&global_to_local(global)))
|
.map_or(None, |chunk| chunk.get_texel(&global_to_local(global)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_latest_simulation(&self, global: &Vector2I) -> Option<u8> {
|
||||||
|
self.global_to_chunk(global).map_or(None, |chunk| {
|
||||||
|
chunk.get_latest_simulation(&global_to_local(global))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_texel_behaviour(
|
pub fn get_texel_behaviour(
|
||||||
&self,
|
&self,
|
||||||
global: &Vector2I,
|
global: &Vector2I,
|
||||||
|
|
@ -320,16 +326,22 @@ impl Terrain2D {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_texel(&mut self, global: &Vector2I, id: TexelID, simulation_frame: Option<u8>) {
|
pub fn set_texel(
|
||||||
|
&mut self,
|
||||||
|
global: &Vector2I,
|
||||||
|
new_texel: Texel2D,
|
||||||
|
simulation_frame: Option<u8>,
|
||||||
|
) {
|
||||||
if !self.is_within_boundaries(global) {
|
if !self.is_within_boundaries(global) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let index = global_to_chunk_index(global);
|
let index = global_to_chunk_index(global);
|
||||||
let changed = match self.index_to_chunk_mut(&index) {
|
let changed = match self.index_to_chunk_mut(&index) {
|
||||||
Some(chunk) => chunk.set_texel(&global_to_local(global), id, simulation_frame),
|
Some(chunk) => chunk.set_texel(&global_to_local(global), new_texel, simulation_frame),
|
||||||
None => {
|
None => {
|
||||||
let mut chunk = Chunk2D::new();
|
let mut chunk = Chunk2D::new();
|
||||||
let changed = chunk.set_texel(&global_to_local(global), id, simulation_frame);
|
let changed =
|
||||||
|
chunk.set_texel(&global_to_local(global), new_texel, simulation_frame);
|
||||||
self.add_chunk(index, chunk);
|
self.add_chunk(index, chunk);
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
|
|
@ -348,9 +360,10 @@ impl Terrain2D {
|
||||||
to_global: &Vector2I,
|
to_global: &Vector2I,
|
||||||
simulation_frame: Option<u8>,
|
simulation_frame: Option<u8>,
|
||||||
) {
|
) {
|
||||||
let from = self.get_texel(from_global).map_or(0, |t| t.id);
|
let from = self.get_texel(from_global).unwrap_or(Texel2D::default());
|
||||||
let to = self.get_texel(to_global).map_or(0, |t| t.id);
|
let to = self.get_texel(to_global).unwrap_or(Texel2D::default());
|
||||||
self.set_texel(to_global, from, simulation_frame);
|
self.set_texel(to_global, from, simulation_frame);
|
||||||
|
// REM: The displaced texel is also marked as simulated
|
||||||
self.set_texel(from_global, to, simulation_frame);
|
self.set_texel(from_global, to, simulation_frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,13 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use super::{
|
use super::*;
|
||||||
local_to_texel_index, texel_index_to_local, Terrain2D, TerrainEvent2D, Texel2D,
|
|
||||||
TexelBehaviour2D, TexelID, NEIGHBOUR_INDEX_MAP,
|
|
||||||
};
|
|
||||||
use crate::util::{CollisionLayers, Segment2I, Vector2I};
|
use crate::util::{CollisionLayers, Segment2I, Vector2I};
|
||||||
use bevy::{
|
use bevy::render::{render_resource::Extent3d, texture::ImageSampler};
|
||||||
prelude::*,
|
|
||||||
render::{render_resource::Extent3d, texture::ImageSampler},
|
|
||||||
};
|
|
||||||
use bevy_rapier2d::prelude::*;
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
type Island = VecDeque<Segment2I>;
|
type Island = VecDeque<Segment2I>;
|
||||||
|
pub type Chunk2DIndex = Vector2I;
|
||||||
|
pub type NeighbourMask = u8;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Marching Square case dictionary.
|
/// Marching Square case dictionary.
|
||||||
|
|
@ -49,6 +44,14 @@ lazy_static! {
|
||||||
/* down */ Segment2I { from: Vector2I::RIGHT, to: Vector2I::ZERO },
|
/* down */ Segment2I { from: Vector2I::RIGHT, to: Vector2I::ZERO },
|
||||||
/* left */ Segment2I { from: Vector2I::ZERO, to: Vector2I::UP },
|
/* left */ Segment2I { from: Vector2I::ZERO, to: Vector2I::UP },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
static ref NEIGHBOUR_INDEX_MAP: HashMap<Vector2I, u8> = {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
for i in 0..Chunk2D::NEIGHBOUR_OFFSET_VECTORS.len() {
|
||||||
|
map.insert(Chunk2D::NEIGHBOUR_OFFSET_VECTORS[i], i as u8);
|
||||||
|
}
|
||||||
|
map
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Reflect, Component, Default)]
|
#[derive(Reflect, Component, Default)]
|
||||||
|
|
@ -79,8 +82,6 @@ pub struct ChunkColliderBundle {
|
||||||
pub transform: TransformBundle,
|
pub transform: TransformBundle,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Chunk2DIndex = Vector2I;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct ChunkRect {
|
pub struct ChunkRect {
|
||||||
pub min: Vector2I,
|
pub min: Vector2I,
|
||||||
|
|
@ -97,7 +98,11 @@ impl ChunkRect {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Chunk2D {
|
pub struct Chunk2D {
|
||||||
pub texels: [Texel2D; (Self::SIZE_X * Self::SIZE_Y) as usize],
|
pub texels: [Texel2D; Self::SIZE_X * Self::SIZE_Y],
|
||||||
|
/// bitmask of empty/non-empty neighbours, see NEIGHBOUR_OFFSET_VECTORS for the order
|
||||||
|
pub neighbour_mask: [NeighbourMask; Self::SIZE_X * Self::SIZE_Y],
|
||||||
|
/// Used in simulation step so that texels won't be updated twice. Value of 0 is always updated.
|
||||||
|
pub simulation_frames: [u8; Self::SIZE_X * Self::SIZE_Y],
|
||||||
// TODO: handle multiple dirty rects?
|
// TODO: handle multiple dirty rects?
|
||||||
pub dirty_rect: Option<ChunkRect>,
|
pub dirty_rect: Option<ChunkRect>,
|
||||||
}
|
}
|
||||||
|
|
@ -109,65 +114,22 @@ impl Chunk2D {
|
||||||
x: Self::SIZE_X as i32,
|
x: Self::SIZE_X as i32,
|
||||||
y: Self::SIZE_Y as i32,
|
y: Self::SIZE_Y as i32,
|
||||||
};
|
};
|
||||||
|
pub const NEIGHBOUR_OFFSET_VECTORS: [Vector2I; 4] = [
|
||||||
|
Vector2I { x: 0, y: 1 },
|
||||||
|
Vector2I { x: 1, y: 0 },
|
||||||
|
Vector2I { x: 0, y: -1 },
|
||||||
|
Vector2I { x: -1, y: 0 },
|
||||||
|
];
|
||||||
|
|
||||||
pub fn new() -> Chunk2D {
|
pub fn new() -> Chunk2D {
|
||||||
Chunk2D {
|
Chunk2D {
|
||||||
texels: Self::new_texel_array(),
|
texels: [Texel2D::default(); Self::SIZE_X * Self::SIZE_Y],
|
||||||
|
neighbour_mask: [0; Self::SIZE_X * Self::SIZE_Y],
|
||||||
|
simulation_frames: [0; Self::SIZE_X * Self::SIZE_Y],
|
||||||
dirty_rect: None,
|
dirty_rect: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_full() -> Chunk2D {
|
|
||||||
let mut chunk = Chunk2D {
|
|
||||||
texels: Self::new_texel_array(),
|
|
||||||
dirty_rect: None,
|
|
||||||
};
|
|
||||||
for y in 0..Self::SIZE_Y {
|
|
||||||
for x in 0..Self::SIZE_X {
|
|
||||||
chunk.set_texel(&Vector2I::new(x as i32, y as i32), 1, None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chunk
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_half() -> Chunk2D {
|
|
||||||
let mut chunk = Chunk2D {
|
|
||||||
texels: Self::new_texel_array(),
|
|
||||||
dirty_rect: None,
|
|
||||||
};
|
|
||||||
for y in 0..Self::SIZE_Y {
|
|
||||||
for x in 0..Self::SIZE_X {
|
|
||||||
if x <= Self::SIZE_Y - y {
|
|
||||||
chunk.set_texel(&Vector2I::new(x as i32, y as i32), 1, None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chunk
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_circle() -> Chunk2D {
|
|
||||||
let mut chunk = Chunk2D {
|
|
||||||
texels: Self::new_texel_array(),
|
|
||||||
dirty_rect: None,
|
|
||||||
};
|
|
||||||
let origin = Self::SIZE / 2;
|
|
||||||
let radius = Self::SIZE_X as i32 / 2;
|
|
||||||
for y in 0..Self::SIZE_Y {
|
|
||||||
for x in 0..Self::SIZE_X {
|
|
||||||
let dx = (x as i32 - origin.x).abs();
|
|
||||||
let dy = (y as i32 - origin.y).abs();
|
|
||||||
if dx * dx + dy * dy <= (radius - 1) * (radius - 1) {
|
|
||||||
chunk.set_texel(&Vector2I::new(x as i32, y as i32), 1, None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chunk
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_texel_array() -> [Texel2D; Self::SIZE_X * Self::SIZE_Y] {
|
|
||||||
[Texel2D::default(); Self::SIZE_X * Self::SIZE_Y]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn xy_vec() -> Vec<Vector2I> {
|
pub fn xy_vec() -> Vec<Vector2I> {
|
||||||
let mut result = Vec::with_capacity(Self::SIZE_X * Self::SIZE_Y);
|
let mut result = Vec::with_capacity(Self::SIZE_X * Self::SIZE_Y);
|
||||||
for y in 0..Self::SIZE_Y {
|
for y in 0..Self::SIZE_Y {
|
||||||
|
|
@ -208,6 +170,10 @@ impl Chunk2D {
|
||||||
local_to_texel_index(position).map(|i| self.texels[i])
|
local_to_texel_index(position).map(|i| self.texels[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_latest_simulation(&self, position: &Vector2I) -> Option<u8> {
|
||||||
|
local_to_texel_index(position).map(|i| self.simulation_frames[i])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_texel_mut(&mut self, position: &Vector2I) -> Option<&mut Texel2D> {
|
pub fn get_texel_mut(&mut self, position: &Vector2I) -> Option<&mut Texel2D> {
|
||||||
local_to_texel_index(position).map(|i| &mut self.texels[i])
|
local_to_texel_index(position).map(|i| &mut self.texels[i])
|
||||||
}
|
}
|
||||||
|
|
@ -215,46 +181,44 @@ impl Chunk2D {
|
||||||
pub fn set_texel(
|
pub fn set_texel(
|
||||||
&mut self,
|
&mut self,
|
||||||
position: &Vector2I,
|
position: &Vector2I,
|
||||||
id: TexelID,
|
new_texel: Texel2D,
|
||||||
simulation_frame: Option<u8>,
|
simulation_frame: Option<u8>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let i = local_to_texel_index(position).expect("Texel index out of range");
|
let i = local_to_texel_index(position).expect("Texel index out of range");
|
||||||
if self.texels[i].id != id {
|
if self.texels[i] == new_texel {
|
||||||
self.mark_dirty(position);
|
return false;
|
||||||
}
|
}
|
||||||
let update_neighbours = TexelBehaviour2D::has_collision(&self.texels[i].id)
|
self.mark_dirty(position);
|
||||||
!= TexelBehaviour2D::has_collision(&id);
|
let update_neighbours = self.texels[i].has_collision() != new_texel.has_collision();
|
||||||
let changed = self.texels[i].id != id;
|
self.texels[i] = new_texel;
|
||||||
self.texels[i].id = id;
|
// Update simulation frame
|
||||||
if let Some(simulation_frame) = simulation_frame {
|
if let Some(simulation_frame) = simulation_frame {
|
||||||
self.texels[i].last_simulation = simulation_frame;
|
self.simulation_frames[i] = simulation_frame;
|
||||||
}
|
}
|
||||||
// Update neighbour mask
|
// Update neighbour mask
|
||||||
if update_neighbours {
|
if update_neighbours {
|
||||||
for offset in Texel2D::NEIGHBOUR_OFFSET_VECTORS {
|
for offset in Self::NEIGHBOUR_OFFSET_VECTORS {
|
||||||
// Flip neighbour's bit
|
// Flip neighbour's bit
|
||||||
match self.get_texel_mut(&(*position + offset)) {
|
match local_to_texel_index(&(*position + offset)) {
|
||||||
Some(mut neighbour) => {
|
Some(index) => {
|
||||||
neighbour.neighbour_mask ^= 1 << NEIGHBOUR_INDEX_MAP[&-offset];
|
self.neighbour_mask[index] ^= 1 << NEIGHBOUR_INDEX_MAP[&-offset];
|
||||||
}
|
}
|
||||||
None => (),
|
None => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
changed
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_texture_data(&self) -> Vec<u8> {
|
pub fn create_texture_data(&self) -> Vec<u8> {
|
||||||
let mut image_data = Vec::with_capacity(Chunk2D::SIZE_X * Chunk2D::SIZE_Y * 4);
|
let mut image_data = Vec::with_capacity(Chunk2D::SIZE_X * Chunk2D::SIZE_Y * 4);
|
||||||
for y in (0..Chunk2D::SIZE_Y).rev() {
|
for y in (0..Chunk2D::SIZE_Y).rev() {
|
||||||
for x in 0..Chunk2D::SIZE_X {
|
for x in 0..Chunk2D::SIZE_X {
|
||||||
let id = &self
|
let texel = &self.get_texel(&Vector2I::new(x as i32, y as i32)).unwrap();
|
||||||
.get_texel(&Vector2I::new(x as i32, y as i32))
|
let behaviour = texel.behaviour();
|
||||||
.unwrap()
|
let mut color =
|
||||||
.id;
|
|
||||||
let behaviour = TexelBehaviour2D::from_id(id);
|
|
||||||
let color =
|
|
||||||
behaviour.map_or(Color::rgba_u8(0, 0, 0, 0), |behaviour| behaviour.color);
|
behaviour.map_or(Color::rgba_u8(0, 0, 0, 0), |behaviour| behaviour.color);
|
||||||
|
color.set_a(color.a() * ((texel.density as f32) / 256.0));
|
||||||
let color_data = color.as_rgba_u32();
|
let color_data = color.as_rgba_u32();
|
||||||
let mut color_data: Vec<u8> = vec![
|
let mut color_data: Vec<u8> = vec![
|
||||||
((color_data >> 0) & 0xff) as u8,
|
((color_data >> 0) & 0xff) as u8,
|
||||||
|
|
@ -268,6 +232,7 @@ impl Chunk2D {
|
||||||
image_data
|
image_data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Don't create collision for falling texels, it's pretty annoying that a stream of small grains blocks movement
|
||||||
pub fn create_collision_data(&self) -> Vec<Vec<Vec2>> {
|
pub fn create_collision_data(&self) -> Vec<Vec<Vec2>> {
|
||||||
let mut islands: Vec<Island> = Vec::new();
|
let mut islands: Vec<Island> = Vec::new();
|
||||||
for i in 0..self.texels.len() {
|
for i in 0..self.texels.len() {
|
||||||
|
|
@ -287,7 +252,7 @@ impl Chunk2D {
|
||||||
let mut sides: Vec<Segment2I>;
|
let mut sides: Vec<Segment2I>;
|
||||||
let has_collision = TexelBehaviour2D::has_collision(&self.texels[i].id);
|
let has_collision = TexelBehaviour2D::has_collision(&self.texels[i].id);
|
||||||
if !has_collision {
|
if !has_collision {
|
||||||
sides = MST_CASE_MAP[self.texels[i].neighbour_mask as usize]
|
sides = MST_CASE_MAP[self.neighbour_mask[i] as usize]
|
||||||
.iter()
|
.iter()
|
||||||
.clone()
|
.clone()
|
||||||
.map(|side| Segment2I {
|
.map(|side| Segment2I {
|
||||||
|
|
@ -473,9 +438,7 @@ pub fn chunk_spawner(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Update the chunk sprite as needed
|
||||||
Update the chunk sprite as needed
|
|
||||||
*/
|
|
||||||
pub fn chunk_sprite_sync(
|
pub fn chunk_sprite_sync(
|
||||||
mut terrain_events: EventReader<TerrainEvent2D>,
|
mut terrain_events: EventReader<TerrainEvent2D>,
|
||||||
mut images: ResMut<Assets<Image>>,
|
mut images: ResMut<Assets<Image>>,
|
||||||
|
|
@ -532,9 +495,7 @@ pub fn chunk_sprite_sync(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Create and update colliders for chunk as needed
|
||||||
Create and update colliders for chunk as needed
|
|
||||||
*/
|
|
||||||
pub fn chunk_collision_sync(
|
pub fn chunk_collision_sync(
|
||||||
mut terrain_events: EventReader<TerrainEvent2D>,
|
mut terrain_events: EventReader<TerrainEvent2D>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use noise::{NoiseFn, PerlinSurflet};
|
use noise::{NoiseFn, PerlinSurflet};
|
||||||
|
|
||||||
use super::{chunk_index_to_global, Chunk2D, Chunk2DIndex};
|
use super::*;
|
||||||
|
|
||||||
pub struct TerrainGen2D {
|
pub struct TerrainGen2D {
|
||||||
pub seed: u32,
|
pub seed: u32,
|
||||||
|
|
@ -39,7 +39,7 @@ impl TerrainGen2D {
|
||||||
id = 13;
|
id = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk.set_texel(&local, id, None);
|
chunk.set_texel(&local, Texel2D { id, ..default() }, None);
|
||||||
}
|
}
|
||||||
chunk
|
chunk
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,32 @@
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
pub use u8 as TexelID;
|
pub use u8 as TexelID;
|
||||||
pub use u8 as NeighbourMask;
|
|
||||||
|
|
||||||
use crate::util::Vector2I;
|
use super::TexelBehaviour2D;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub struct Texel2D {
|
pub struct Texel2D {
|
||||||
|
/// Identifier for a set of properties
|
||||||
pub id: TexelID,
|
pub id: TexelID,
|
||||||
/// bitmask of empty/non-empty neighbours, see NEIGHBOUR_OFFSET_VECTORS for the order
|
/// Used by gas materials
|
||||||
pub neighbour_mask: NeighbourMask,
|
pub density: u8,
|
||||||
pub last_simulation: u8,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
impl Default for Texel2D {
|
||||||
pub static ref NEIGHBOUR_INDEX_MAP: HashMap<Vector2I, u8> = {
|
fn default() -> Self {
|
||||||
let mut map = HashMap::new();
|
Self {
|
||||||
for i in 0..Texel2D::NEIGHBOUR_OFFSET_VECTORS.len() {
|
id: TexelID::default(),
|
||||||
map.insert(Texel2D::NEIGHBOUR_OFFSET_VECTORS[i], i as u8);
|
density: u8::MAX,
|
||||||
}
|
}
|
||||||
map
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Texel2D {
|
impl Texel2D {
|
||||||
pub const EMPTY: TexelID = 0;
|
pub const EMPTY: TexelID = 0;
|
||||||
pub const NEIGHBOUR_OFFSET_VECTORS: [Vector2I; 4] = [
|
|
||||||
Vector2I { x: 0, y: 1 },
|
pub fn has_collision(&self) -> bool {
|
||||||
Vector2I { x: 1, y: 0 },
|
TexelBehaviour2D::has_collision(&self.id)
|
||||||
Vector2I { x: 0, y: -1 },
|
}
|
||||||
Vector2I { x: -1, y: 0 },
|
|
||||||
];
|
pub fn behaviour(&self) -> Option<TexelBehaviour2D> {
|
||||||
|
TexelBehaviour2D::from_id(&self.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ lazy_static! {
|
||||||
TexelBehaviour2D {
|
TexelBehaviour2D {
|
||||||
name: Cow::Borrowed("loose sand"),
|
name: Cow::Borrowed("loose sand"),
|
||||||
color: Color::rgb(0.61, 0.49, 0.38),
|
color: Color::rgb(0.61, 0.49, 0.38),
|
||||||
gravity: Some(TexelGravity::Down(100)),
|
gravity: Some(TexelGravity::Down(200)),
|
||||||
has_collision: true,
|
has_collision: true,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
|
|
@ -25,7 +25,7 @@ lazy_static! {
|
||||||
TexelBehaviour2D {
|
TexelBehaviour2D {
|
||||||
name: Cow::Borrowed("loose stone"),
|
name: Cow::Borrowed("loose stone"),
|
||||||
color: Color::rgb(0.21, 0.19, 0.17),
|
color: Color::rgb(0.21, 0.19, 0.17),
|
||||||
gravity: Some(TexelGravity::Down(100)),
|
gravity: Some(TexelGravity::Down(200)),
|
||||||
has_collision: true,
|
has_collision: true,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
|
|
@ -36,7 +36,7 @@ lazy_static! {
|
||||||
TexelBehaviour2D {
|
TexelBehaviour2D {
|
||||||
name: Cow::Borrowed("loose sturdy stone"),
|
name: Cow::Borrowed("loose sturdy stone"),
|
||||||
color: Color::rgb(0.11, 0.11, 0.11),
|
color: Color::rgb(0.11, 0.11, 0.11),
|
||||||
gravity: Some(TexelGravity::Down(100)),
|
gravity: Some(TexelGravity::Down(200)),
|
||||||
has_collision: true,
|
has_collision: true,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
|
|
@ -48,7 +48,7 @@ lazy_static! {
|
||||||
name: Cow::Borrowed("water"),
|
name: Cow::Borrowed("water"),
|
||||||
color: Color::rgba(0.0, 0.0, 1.0, 0.5),
|
color: Color::rgba(0.0, 0.0, 1.0, 0.5),
|
||||||
form: TexelForm::Liquid,
|
form: TexelForm::Liquid,
|
||||||
gravity: Some(TexelGravity::Down(10)),
|
gravity: Some(TexelGravity::Down(50)),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -57,9 +57,9 @@ lazy_static! {
|
||||||
5,
|
5,
|
||||||
TexelBehaviour2D {
|
TexelBehaviour2D {
|
||||||
name: Cow::Borrowed("oil"),
|
name: Cow::Borrowed("oil"),
|
||||||
color: Color::rgba(0.0, 1.0, 0.0, 0.5),
|
color: Color::rgba(0.5, 0.5, 0.25, 0.5),
|
||||||
form: TexelForm::Gas,
|
form: TexelForm::Liquid,
|
||||||
gravity: Some(TexelGravity::Up(50)),
|
gravity: Some(TexelGravity::Down(20)),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -67,10 +67,21 @@ lazy_static! {
|
||||||
result.insert(
|
result.insert(
|
||||||
6,
|
6,
|
||||||
TexelBehaviour2D {
|
TexelBehaviour2D {
|
||||||
name: Cow::Borrowed("gas"),
|
name: Cow::Borrowed("light gas"),
|
||||||
color: Color::rgba(0.5, 0.5, 0.25, 0.5),
|
color: Color::rgba(0.0, 1.0, 0.0, 0.5),
|
||||||
form: TexelForm::Liquid,
|
form: TexelForm::Gas,
|
||||||
gravity: Some(TexelGravity::Down(5)),
|
gravity: Some(TexelGravity::Up(10)),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
result.insert(
|
||||||
|
7,
|
||||||
|
TexelBehaviour2D {
|
||||||
|
name: Cow::Borrowed("heavy gas"),
|
||||||
|
color: Color::rgba(1.0, 1.0, 1.0, 0.5),
|
||||||
|
form: TexelForm::Gas,
|
||||||
|
gravity: Some(TexelGravity::Down(10)),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue