feat: added TerrainStages and moved terrain debug systems to DebugPlugin
parent
7edca9d472
commit
5f2b2b9d06
|
|
@ -22,10 +22,10 @@ pub fn init() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
.add_plugin(RapierPhysicsPlugin::<NoUserData>::default())
|
.add_plugin(RapierPhysicsPlugin::<NoUserData>::default())
|
||||||
|
.add_plugin(Terrain2DPlugin)
|
||||||
.add_plugin(DebugPlugin)
|
.add_plugin(DebugPlugin)
|
||||||
.add_plugin(KinematicPlugin)
|
.add_plugin(KinematicPlugin)
|
||||||
.add_plugin(GameCameraPlugin)
|
.add_plugin(GameCameraPlugin)
|
||||||
.add_plugin(Terrain2DPlugin)
|
|
||||||
.add_plugin(PlayerPlugin)
|
.add_plugin(PlayerPlugin)
|
||||||
.add_startup_system(setup_terrain)
|
.add_startup_system(setup_terrain)
|
||||||
.add_startup_system(setup_window)
|
.add_startup_system(setup_window)
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,17 @@ use bevy_inspector_egui::*;
|
||||||
use bevy_prototype_debug_lines::DebugLinesPlugin;
|
use bevy_prototype_debug_lines::DebugLinesPlugin;
|
||||||
use bevy_rapier2d::prelude::*;
|
use bevy_rapier2d::prelude::*;
|
||||||
|
|
||||||
|
mod terrain;
|
||||||
|
|
||||||
|
use terrain::TerrainDebugPlugin;
|
||||||
|
|
||||||
pub struct DebugPlugin;
|
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(RapierDebugRenderPlugin::default())
|
||||||
.add_plugin(WorldInspectorPlugin::new());
|
.add_plugin(WorldInspectorPlugin::new())
|
||||||
|
.add_plugin(TerrainDebugPlugin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
use crate::{game::camera::GameCamera, terrain2d::*, util::Vector2I};
|
||||||
|
use bevy::{input::mouse::MouseWheel, prelude::*, render::camera::RenderTarget};
|
||||||
|
use bevy_prototype_debug_lines::DebugLines;
|
||||||
|
|
||||||
|
pub struct TerrainDebugPlugin;
|
||||||
|
|
||||||
|
impl Plugin for TerrainDebugPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.insert_resource(TerrainBrush2D::default())
|
||||||
|
.add_system(debug_painter)
|
||||||
|
.add_system_to_stage(
|
||||||
|
TerrainStages::First,
|
||||||
|
dirty_rect_visualizer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct TerrainBrush2D {
|
||||||
|
pub radius: i32,
|
||||||
|
pub tile: TexelID,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TerrainBrush2D {
|
||||||
|
fn default() -> Self {
|
||||||
|
TerrainBrush2D { radius: 7, tile: 3 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// REM: Dirty and hopefully temporary
|
||||||
|
fn debug_painter(
|
||||||
|
mut terrain: ResMut<Terrain2D>,
|
||||||
|
mut debug_draw: ResMut<DebugLines>,
|
||||||
|
mut brush: ResMut<TerrainBrush2D>,
|
||||||
|
windows: Res<Windows>,
|
||||||
|
mouse_input: Res<Input<MouseButton>>,
|
||||||
|
key_input: Res<Input<KeyCode>>,
|
||||||
|
mut mouse_wheel: EventReader<MouseWheel>,
|
||||||
|
camera_query: Query<(&Camera, &GlobalTransform), With<GameCamera>>,
|
||||||
|
) {
|
||||||
|
let allow_painting = key_input.pressed(KeyCode::LControl);
|
||||||
|
|
||||||
|
// Change brush
|
||||||
|
for event in mouse_wheel.iter() {
|
||||||
|
if allow_painting {
|
||||||
|
brush.radius = (brush.radius + event.y.round() as i32).clamp(1, 128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !allow_painting {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://bevy-cheatbook.github.io/cookbook/cursor2world.html#2d-games
|
||||||
|
// get the camera info and transform
|
||||||
|
// assuming there is exactly one main camera entity, so query::single() is OK
|
||||||
|
let (camera, camera_transform) = camera_query.single();
|
||||||
|
|
||||||
|
// get the window that the camera is displaying to (or the primary window)
|
||||||
|
let window = if let RenderTarget::Window(id) = camera.target {
|
||||||
|
windows.get(id).unwrap()
|
||||||
|
} else {
|
||||||
|
windows.get_primary().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if the cursor is inside the window and get its position
|
||||||
|
let world_pos = if let Some(screen_pos) = window.cursor_position() {
|
||||||
|
// get the size of the window
|
||||||
|
let window_size = Vec2::new(window.width() as f32, window.height() as f32);
|
||||||
|
|
||||||
|
// convert screen position [0..resolution] to ndc [-1..1] (gpu coordinates)
|
||||||
|
let ndc = (screen_pos / window_size) * 2.0 - Vec2::ONE;
|
||||||
|
|
||||||
|
// matrix for undoing the projection and camera transform
|
||||||
|
let ndc_to_world = camera_transform.compute_matrix() * camera.projection_matrix().inverse();
|
||||||
|
|
||||||
|
// use it to convert ndc to world-space coordinates
|
||||||
|
let world_pos = ndc_to_world.project_point3(ndc.extend(-1.0));
|
||||||
|
|
||||||
|
// reduce it to a 2D value
|
||||||
|
world_pos.truncate()
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if key_input.just_pressed(KeyCode::Key1) {
|
||||||
|
brush.tile = 1;
|
||||||
|
}
|
||||||
|
if key_input.just_pressed(KeyCode::Key2) {
|
||||||
|
brush.tile = 2;
|
||||||
|
}
|
||||||
|
if key_input.just_pressed(KeyCode::Key3) {
|
||||||
|
brush.tile = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
let colors = vec![
|
||||||
|
Color::rgba(1.0, 0.25, 0.25, 1.0),
|
||||||
|
Color::rgba(0.25, 1.0, 0.25, 1.0),
|
||||||
|
Color::rgba(0.25, 0.25, 1.0, 1.0),
|
||||||
|
];
|
||||||
|
|
||||||
|
let origin = Vector2I::from(world_pos);
|
||||||
|
let radius = brush.radius;
|
||||||
|
let color = colors[brush.tile as usize % colors.len()];
|
||||||
|
let id = match (
|
||||||
|
mouse_input.pressed(MouseButton::Left),
|
||||||
|
mouse_input.pressed(MouseButton::Right),
|
||||||
|
) {
|
||||||
|
(true, false) => brush.tile,
|
||||||
|
(_, _) => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
for y in origin.y - (radius - 1)..origin.y + radius {
|
||||||
|
for x in origin.x - (radius - 1)..origin.x + radius {
|
||||||
|
let dx = (x - origin.x).abs();
|
||||||
|
let dy = (y - origin.y).abs();
|
||||||
|
|
||||||
|
if dx * dx + dy * dy <= (radius - 1) * (radius - 1) {
|
||||||
|
let pos: Vector2I = Vector2I { x, y };
|
||||||
|
debug_draw.line_colored(
|
||||||
|
Vec3::from(pos) + Vec3::new(0.45, 0.45, 0.0),
|
||||||
|
Vec3::from(pos) + Vec3::new(0.55, 0.55, 0.0),
|
||||||
|
0.0,
|
||||||
|
color,
|
||||||
|
);
|
||||||
|
if mouse_input.pressed(MouseButton::Left) || mouse_input.pressed(MouseButton::Right)
|
||||||
|
{
|
||||||
|
terrain.set_texel(&pos, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Visualize dirty rects
|
||||||
|
*/
|
||||||
|
fn dirty_rect_visualizer(terrain: Res<Terrain2D>, mut debug_draw: ResMut<DebugLines>) {
|
||||||
|
for (chunk_index, chunk) in terrain.chunk_iter() {
|
||||||
|
let rect = if let Some(rect) = chunk.dirty_rect {
|
||||||
|
rect
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let color = Color::RED;
|
||||||
|
|
||||||
|
let points = vec![
|
||||||
|
Vec3::new(rect.min.x as f32, rect.min.y as f32, 0.0),
|
||||||
|
Vec3::new((rect.max.x + 1) as f32, rect.min.y as f32, 0.0),
|
||||||
|
Vec3::new((rect.max.x + 1) as f32, (rect.max.y + 1) as f32, 0.0),
|
||||||
|
Vec3::new(rect.min.x as f32, (rect.max.y + 1) as f32, 0.0),
|
||||||
|
];
|
||||||
|
for i in 0..points.len() {
|
||||||
|
let offset = Vec3::from(chunk_index_to_global(chunk_index));
|
||||||
|
debug_draw.line_colored(
|
||||||
|
offset + points[i],
|
||||||
|
offset + points[(i + 1) % points.len()],
|
||||||
|
0.0,
|
||||||
|
color,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
207
src/terrain2d.rs
207
src/terrain2d.rs
|
|
@ -3,197 +3,68 @@ use std::collections::{
|
||||||
HashMap,
|
HashMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy::{input::mouse::MouseWheel, prelude::*, render::camera::RenderTarget};
|
use bevy::ecs::prelude::SystemStage;
|
||||||
use bevy_prototype_debug_lines::DebugLines;
|
use bevy::prelude::*;
|
||||||
|
use bevy_rapier2d::prelude::*;
|
||||||
|
|
||||||
mod chunk2d;
|
mod chunk2d;
|
||||||
mod terrain_gen2d;
|
mod terrain_gen2d;
|
||||||
mod texel2d;
|
mod texel2d;
|
||||||
|
mod texel_behaviour2d;
|
||||||
|
|
||||||
pub use chunk2d::*;
|
pub use chunk2d::*;
|
||||||
pub use terrain_gen2d::*;
|
pub use terrain_gen2d::*;
|
||||||
pub use texel2d::*;
|
pub use texel2d::*;
|
||||||
|
pub use texel_behaviour2d::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::util::{math::*, Vector2I};
|
||||||
game::camera::GameCamera,
|
|
||||||
util::{math::*, Vector2I},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Terrain2DPlugin;
|
pub struct Terrain2DPlugin;
|
||||||
|
|
||||||
impl Plugin for Terrain2DPlugin {
|
impl Plugin for Terrain2DPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
|
// Add terrain stages. They should go between CoreStage::Update and Rapier's own stages
|
||||||
|
app.add_stage_before(
|
||||||
|
PhysicsStages::SyncBackend,
|
||||||
|
TerrainStages::First,
|
||||||
|
SystemStage::parallel(),
|
||||||
|
).add_stage_after(
|
||||||
|
TerrainStages::First,
|
||||||
|
TerrainStages::EventHandler,
|
||||||
|
SystemStage::parallel(),
|
||||||
|
).add_stage_after(
|
||||||
|
TerrainStages::EventHandler,
|
||||||
|
TerrainStages::ChunkSync,
|
||||||
|
SystemStage::parallel(),
|
||||||
|
).add_stage_after(
|
||||||
|
TerrainStages::ChunkSync,
|
||||||
|
TerrainStages::Last,
|
||||||
|
SystemStage::parallel(),
|
||||||
|
);
|
||||||
|
|
||||||
app.register_type::<TerrainChunk2D>()
|
app.register_type::<TerrainChunk2D>()
|
||||||
.insert_resource(Terrain2D::new())
|
.insert_resource(Terrain2D::new())
|
||||||
.insert_resource(TerrainBrush2D::default())
|
|
||||||
.add_event::<TerrainEvent2D>()
|
.add_event::<TerrainEvent2D>()
|
||||||
.add_system(debug_painter)
|
.add_system_to_stage(TerrainStages::EventHandler, emit_terrain_events)
|
||||||
.add_system_to_stage(
|
.add_system_to_stage(
|
||||||
CoreStage::PostUpdate,
|
TerrainStages::EventHandler,
|
||||||
dirty_rect_visualizer.before(emit_terrain_events),
|
|
||||||
)
|
|
||||||
.add_system_to_stage(
|
|
||||||
CoreStage::PostUpdate,
|
|
||||||
chunk_spawner.before(emit_terrain_events),
|
chunk_spawner.before(emit_terrain_events),
|
||||||
)
|
)
|
||||||
.add_system_to_stage(
|
.add_system_to_stage(TerrainStages::ChunkSync, chunk_sprite_sync)
|
||||||
CoreStage::PostUpdate,
|
.add_system_to_stage(CoreStage::PostUpdate, chunk_collision_sync);
|
||||||
chunk_sprite_sync.after(chunk_spawner),
|
|
||||||
)
|
|
||||||
.add_system_to_stage(
|
|
||||||
CoreStage::PostUpdate,
|
|
||||||
chunk_collision_sync.after(chunk_spawner),
|
|
||||||
)
|
|
||||||
.add_system_to_stage(CoreStage::PostUpdate, emit_terrain_events);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(StageLabel)]
|
||||||
struct TerrainBrush2D {
|
pub enum TerrainStages {
|
||||||
pub radius: i32,
|
/// First of terrain stages to be called
|
||||||
pub tile: TexelID,
|
First,
|
||||||
}
|
/// The stage that Handles collected events and creates new chunk entities as needed
|
||||||
|
EventHandler,
|
||||||
impl Default for TerrainBrush2D {
|
/// Chunk sync systems (e.g. collsion and sprite) run in this stage
|
||||||
fn default() -> Self {
|
ChunkSync,
|
||||||
TerrainBrush2D { radius: 7, tile: 3 }
|
/// Last of terrain stages to be called
|
||||||
}
|
Last,
|
||||||
}
|
|
||||||
|
|
||||||
// REM: Dirty and hopefully temporary
|
|
||||||
fn debug_painter(
|
|
||||||
mut terrain: ResMut<Terrain2D>,
|
|
||||||
mut debug_draw: ResMut<DebugLines>,
|
|
||||||
mut brush: ResMut<TerrainBrush2D>,
|
|
||||||
windows: Res<Windows>,
|
|
||||||
mouse_input: Res<Input<MouseButton>>,
|
|
||||||
key_input: Res<Input<KeyCode>>,
|
|
||||||
mut mouse_wheel: EventReader<MouseWheel>,
|
|
||||||
camera_query: Query<(&Camera, &GlobalTransform), With<GameCamera>>,
|
|
||||||
) {
|
|
||||||
let allow_painting = key_input.pressed(KeyCode::LControl);
|
|
||||||
|
|
||||||
// Change brush
|
|
||||||
for event in mouse_wheel.iter() {
|
|
||||||
if allow_painting {
|
|
||||||
brush.radius = (brush.radius + event.y.round() as i32).clamp(1, 128);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !allow_painting {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://bevy-cheatbook.github.io/cookbook/cursor2world.html#2d-games
|
|
||||||
// get the camera info and transform
|
|
||||||
// assuming there is exactly one main camera entity, so query::single() is OK
|
|
||||||
let (camera, camera_transform) = camera_query.single();
|
|
||||||
|
|
||||||
// get the window that the camera is displaying to (or the primary window)
|
|
||||||
let window = if let RenderTarget::Window(id) = camera.target {
|
|
||||||
windows.get(id).unwrap()
|
|
||||||
} else {
|
|
||||||
windows.get_primary().unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
// check if the cursor is inside the window and get its position
|
|
||||||
let world_pos = if let Some(screen_pos) = window.cursor_position() {
|
|
||||||
// get the size of the window
|
|
||||||
let window_size = Vec2::new(window.width() as f32, window.height() as f32);
|
|
||||||
|
|
||||||
// convert screen position [0..resolution] to ndc [-1..1] (gpu coordinates)
|
|
||||||
let ndc = (screen_pos / window_size) * 2.0 - Vec2::ONE;
|
|
||||||
|
|
||||||
// matrix for undoing the projection and camera transform
|
|
||||||
let ndc_to_world = camera_transform.compute_matrix() * camera.projection_matrix().inverse();
|
|
||||||
|
|
||||||
// use it to convert ndc to world-space coordinates
|
|
||||||
let world_pos = ndc_to_world.project_point3(ndc.extend(-1.0));
|
|
||||||
|
|
||||||
// reduce it to a 2D value
|
|
||||||
world_pos.truncate()
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if key_input.just_pressed(KeyCode::Key1) {
|
|
||||||
brush.tile = 1;
|
|
||||||
}
|
|
||||||
if key_input.just_pressed(KeyCode::Key2) {
|
|
||||||
brush.tile = 2;
|
|
||||||
}
|
|
||||||
if key_input.just_pressed(KeyCode::Key3) {
|
|
||||||
brush.tile = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
let colors = vec![
|
|
||||||
Color::rgba(1.0, 0.25, 0.25, 1.0),
|
|
||||||
Color::rgba(0.25, 1.0, 0.25, 1.0),
|
|
||||||
Color::rgba(0.25, 0.25, 1.0, 1.0),
|
|
||||||
];
|
|
||||||
|
|
||||||
let origin = Vector2I::from(world_pos);
|
|
||||||
let radius = brush.radius;
|
|
||||||
let color = colors[brush.tile as usize % colors.len()];
|
|
||||||
let id = match (
|
|
||||||
mouse_input.pressed(MouseButton::Left),
|
|
||||||
mouse_input.pressed(MouseButton::Right),
|
|
||||||
) {
|
|
||||||
(true, false) => brush.tile,
|
|
||||||
(_, _) => 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
for y in origin.y - (radius - 1)..origin.y + radius {
|
|
||||||
for x in origin.x - (radius - 1)..origin.x + radius {
|
|
||||||
let dx = (x - origin.x).abs();
|
|
||||||
let dy = (y - origin.y).abs();
|
|
||||||
|
|
||||||
if dx * dx + dy * dy <= (radius - 1) * (radius - 1) {
|
|
||||||
let pos: Vector2I = Vector2I { x, y };
|
|
||||||
debug_draw.line_colored(
|
|
||||||
Vec3::from(pos) + Vec3::new(0.45, 0.45, 0.0),
|
|
||||||
Vec3::from(pos) + Vec3::new(0.55, 0.55, 0.0),
|
|
||||||
0.0,
|
|
||||||
color,
|
|
||||||
);
|
|
||||||
if mouse_input.pressed(MouseButton::Left) || mouse_input.pressed(MouseButton::Right)
|
|
||||||
{
|
|
||||||
terrain.set_texel(&pos, id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Visualize dirty rects
|
|
||||||
*/
|
|
||||||
fn dirty_rect_visualizer(terrain: Res<Terrain2D>, mut debug_draw: ResMut<DebugLines>) {
|
|
||||||
for (chunk_index, chunk) in terrain.chunk_iter() {
|
|
||||||
let rect = if let Some(rect) = chunk.dirty_rect {
|
|
||||||
rect
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let color = Color::RED;
|
|
||||||
|
|
||||||
let points = vec![
|
|
||||||
Vec3::new(rect.min.x as f32, rect.min.y as f32, 0.0),
|
|
||||||
Vec3::new((rect.max.x + 1) as f32, rect.min.y as f32, 0.0),
|
|
||||||
Vec3::new((rect.max.x + 1) as f32, (rect.max.y + 1) as f32, 0.0),
|
|
||||||
Vec3::new(rect.min.x as f32, (rect.max.y + 1) as f32, 0.0),
|
|
||||||
];
|
|
||||||
for i in 0..points.len() {
|
|
||||||
let offset = Vec3::from(chunk_index_to_global(chunk_index));
|
|
||||||
debug_draw.line_colored(
|
|
||||||
offset + points[i],
|
|
||||||
offset + points[(i + 1) % points.len()],
|
|
||||||
0.0,
|
|
||||||
color,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_terrain_events(
|
fn emit_terrain_events(
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
local_to_texel_index, texel_index_to_local, Terrain2D, TerrainEvent2D, Texel2D, TexelID,
|
local_to_texel_index, texel_index_to_local, Terrain2D, TerrainEvent2D, Texel2D,
|
||||||
NEIGHBOUR_INDEX_MAP,
|
TexelBehaviour2D, TexelID, NEIGHBOUR_INDEX_MAP,
|
||||||
};
|
};
|
||||||
use crate::util::{CollisionLayers, Segment2I, Vector2I};
|
use crate::util::{CollisionLayers, Segment2I, Vector2I};
|
||||||
use bevy::{
|
use bevy::{
|
||||||
|
|
@ -15,17 +15,6 @@ use lazy_static::lazy_static;
|
||||||
type Island = VecDeque<Segment2I>;
|
type Island = VecDeque<Segment2I>;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref COLOR_MAP: HashMap<TexelID, [u8; 4]> = {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
map.insert(0, [0x00, 0x00, 0x00, 0x00]);
|
|
||||||
// map.insert(0, [0x03, 0x03, 0x03, 0xff]);
|
|
||||||
// map.insert(1, [0x47, 0x8e, 0x48, 0xff]);
|
|
||||||
map.insert(1, [0x9e, 0x7f, 0x63, 0xff]);
|
|
||||||
map.insert(2, [0x38, 0x32, 0x2d, 0xff]);
|
|
||||||
map.insert(3, [0x1e, 0x1e, 0x1e, 0xff]);
|
|
||||||
map
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Marching Square case dictionary.
|
/// Marching Square case dictionary.
|
||||||
///
|
///
|
||||||
/// Key is a bitmask of neighbouring tiles (up, right, down, left - least significant bit first).
|
/// Key is a bitmask of neighbouring tiles (up, right, down, left - least significant bit first).
|
||||||
|
|
@ -247,21 +236,23 @@ impl Chunk2D {
|
||||||
|
|
||||||
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);
|
||||||
let fallback: [u8; 4] = [0x00, 0x00, 0x00, 0x00];
|
|
||||||
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 {
|
||||||
image_data.append(
|
let id = &self
|
||||||
&mut COLOR_MAP
|
.get_texel(&Vector2I::new(x as i32, y as i32))
|
||||||
.get(
|
.unwrap()
|
||||||
&self
|
.id;
|
||||||
.get_texel(&Vector2I::new(x as i32, y as i32))
|
let behaviour = TexelBehaviour2D::from_id(id);
|
||||||
.unwrap()
|
let color =
|
||||||
.id,
|
behaviour.map_or(Color::rgba_u8(0, 0, 0, 0), |behaviour| behaviour.color);
|
||||||
)
|
let color_data = color.as_rgba_u32();
|
||||||
.unwrap_or(&fallback)
|
let mut color_data: Vec<u8> = vec![
|
||||||
.to_vec()
|
((color_data >> 0) & 0xff) as u8,
|
||||||
.clone(),
|
((color_data >> 8) & 0xff) as u8,
|
||||||
);
|
((color_data >> 16) & 0xff) as u8,
|
||||||
|
((color_data >> 24) & 0xff) as u8,
|
||||||
|
];
|
||||||
|
image_data.append(&mut color_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
image_data
|
image_data
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
use super::TexelID;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ID_MAP: HashMap<TexelID, TexelBehaviour2D> = {
|
||||||
|
let mut result = HashMap::new();
|
||||||
|
|
||||||
|
result.insert(1, TexelBehaviour2D {
|
||||||
|
color: Color::rgb(0.61, 0.49, 0.38),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
result.insert(2, TexelBehaviour2D {
|
||||||
|
color: Color::rgb(0.21, 0.19, 0.17),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
result.insert(3, TexelBehaviour2D {
|
||||||
|
color: Color::rgb(0.11, 0.11, 0.11),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
result.insert(4, TexelBehaviour2D {
|
||||||
|
color: Color::rgb(1.0, 0.0, 0.0),
|
||||||
|
form: TexelForm::Gas,
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
result
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub enum TexelForm {
|
||||||
|
#[default]
|
||||||
|
Solid,
|
||||||
|
Liquid,
|
||||||
|
Gas,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct TexelBehaviour2D {
|
||||||
|
// pub flammability: Option<f32>,
|
||||||
|
// pub gravity: Option<f32>,
|
||||||
|
pub form: TexelForm,
|
||||||
|
pub color: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TexelBehaviour2D {
|
||||||
|
pub fn from_id(id: &TexelID) -> Option<Self> {
|
||||||
|
ID_MAP.get(id).copied()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue