This commit is contained in:
2025-01-08 01:33:18 +00:00
parent ed88d5bdce
commit 6c3232df74
5 changed files with 252 additions and 252 deletions
+234 -99
View File
@@ -1,12 +1,11 @@
use crate::constants::TILE_SIZE;
use crate::tile::{FixtureTile, FloorTile, TileMap};
use crate::tiles::{FixtureTilePrefab, FloorTilePrefab};
use bevy::prelude::*;
use noise::{NoiseFn, Perlin};
use std::collections::HashMap;
use std::process::exit;
pub const CHUNK_SIZE: i32 = 8;
pub const CHUNK_HEIGHT: i32 = 200;
#[derive(Resource)]
pub struct ChunkMap {
@@ -27,28 +26,150 @@ pub struct LoadChunkEvent {
}
fn setup_chunk_system(mut commands: Commands) {
commands.insert_resource(TileMap::default());
commands.insert_resource(ChunkMap::default());
}
// Events
#[derive(Event)]
pub struct ChunkOcclusionEvent {
pub chunk_pos: IVec2,
}
#[derive(Event)]
pub struct TileOcclusionUpdateEvent {
pub positions: Vec<IVec3>,
}
// System to handle individual tile occlusion updates
fn handle_tile_occlusion_updates(
mut events: EventReader<TileOcclusionUpdateEvent>,
tilemap: Res<TileMap>,
mut query_set: ParamSet<(
Query<(&mut FloorTile, &Transform)>,
Query<(&mut FixtureTile, &Transform)>,
)>,
) {
let tile_size = TILE_SIZE as i32;
let calculate_visibility = |pos: IVec3, tilemap: &TileMap| {
let mut visible_range = [0u32; 8];
for mut camera_z in -150..100 {
camera_z *= tile_size;
let mut is_visible = false;
if pos.z > camera_z {
continue;
}
let mut is_occluded = false;
'vertical_check: for z_offset in 1..=35 {
let above_pos = IVec3::new(pos.x, pos.y, pos.z + (z_offset * tile_size));
if above_pos.z <= camera_z {
// Check both floors and fixtures for occlusion
if let Some(&(_, opaque, _, _, _)) = tilemap.floors.get(&above_pos) {
if opaque {
is_occluded = true;
break 'vertical_check;
}
}
if let Some(&(_, solid, _)) = tilemap.fixtures.get(&above_pos) {
if solid {
is_occluded = true;
break 'vertical_check;
}
}
} else {
break 'vertical_check;
}
}
if !is_occluded {
'neighbor_check: for x_offset in -1..=1 {
for y_offset in -1..=1 {
for z_offset in 0..=1 {
if x_offset == 0 && y_offset == 0 && z_offset == 0 {
continue;
}
let neighbor_pos = IVec3::new(
pos.x + x_offset * tile_size,
pos.y + y_offset * tile_size,
pos.z + z_offset * tile_size,
);
// Check both floors and fixtures for visibility
if let Some(&(id, _, _, _, _)) = tilemap.floors.get(&neighbor_pos) {
if id == 0 {
is_visible = true;
break 'neighbor_check;
}
}
if let Some(&(id, _, _)) = tilemap.fixtures.get(&neighbor_pos) {
if id == 0 {
is_visible = true;
break 'neighbor_check;
}
}
}
}
}
}
if is_visible {
print!("#");
let z2 = ((camera_z / tile_size) + 150) as usize;
visible_range[z2 / 32] |= 1 << ((z2 % 32) as u32);
}
}
visible_range
};
for event in events.read() {
print!("event2");
// Process floor tiles
for (mut tile, transform) in query_set.p0().iter_mut() {
let pos = transform.translation.as_ivec3();
if event.positions.contains(&pos) {
// this never calls
print!("floor");
tile.visible_range = calculate_visibility(pos, &tilemap);
}
}
// Process fixture tiles
for (mut fixture, transform) in query_set.p1().iter_mut() {
let pos = transform.translation.as_ivec3();
if event.positions.contains(&pos) {
// this never calls
print!("fixture");
let adjusted_pos = pos - IVec3::new(0, 0, 1);
fixture.visible_range = calculate_visibility(adjusted_pos, &tilemap);
}
}
}
}
fn handle_chunk_loading(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut events: EventReader<LoadChunkEvent>,
mut chunk_map: ResMut<ChunkMap>,
mut tilemap: ResMut<TileMap>,
mut occlusion_events: EventWriter<ChunkOcclusionEvent>,
) {
let noise = Perlin::new(0);
for event in events.read() {
let chunk_pos = event.chunk_position;
// Skip if chunk is already loaded
if chunk_map.loaded_chunks.contains_key(&chunk_pos) {
continue;
}
// Mark chunk as loaded
chunk_map.loaded_chunks.insert(chunk_pos, true);
// Calculate world space coordinates for chunk
let start_x = chunk_pos.x * CHUNK_SIZE;
let start_y = chunk_pos.y * CHUNK_SIZE;
@@ -60,10 +181,8 @@ fn handle_chunk_loading(
let world_x = start_x + local_x;
let world_y = start_y + local_y;
let noise_value_a =
noise.get([world_x as f64 * 0.01, world_y as f64 * 0.01]) * 1.25;
let noise_value_b =
noise.get([world_x as f64 * 0.05, world_y as f64 * 0.05]) * 0.25;
let noise_value_a = noise.get([world_x as f64 * 0.01, world_y as f64 * 0.01]) * 1.25;
let noise_value_b = noise.get([world_x as f64 * 0.05, world_y as f64 * 0.05]) * 0.25;
let combined_noise_value = noise_value_a + noise_value_b;
let noise_position = Vec3::new(
@@ -71,112 +190,113 @@ fn handle_chunk_loading(
(world_y as f32 * TILE_SIZE).round(),
(combined_noise_value * 4.).round() as f32 * TILE_SIZE,
);
generate_vertical_slice(
&mut commands,
&asset_server,
world_x,
world_y,
&noise,
noise_position,
&mut floor_positions,
);
}
}
// Generate fixtures after floor tiles
generate_fixtures_for_chunk(&mut commands, &asset_server, &floor_positions);
}
}
// Spawn tiles and add them to tilemap
for z in -150..50 {
let position = Vec3::new(
(world_x as f32 * TILE_SIZE).round(),
(world_y as f32 * TILE_SIZE).round(),
(z as f32 * TILE_SIZE).round(),
);
let pos_ivec = position.as_ivec3();
fn generate_vertical_slice(
commands: &mut Commands,
asset_server: &Res<AssetServer>,
x: i32,
y: i32,
noise: &Perlin,
noise_position: Vec3,
floor_positions: &mut Vec<(Vec3, &'static str)>,
) {
for z in -150..50 {
let position = Vec3::new(
(x as f32 * TILE_SIZE).round(),
(y as f32 * TILE_SIZE).round(),
(z as f32 * TILE_SIZE).round(),
);
let floor_type = if noise_position.z > position.z {
if z < -15 {
let noise_value = noise.get([x as f64 * 0.05, y as f64 * 0.05, z as f64 * 0.05]);
if noise_value < -0.5 {
spawn_tile(commands, FloorTilePrefab::air(position, asset_server));
Some("air")
} else if noise_value < 0.8 {
spawn_tile(commands, FloorTilePrefab::rock(position, asset_server));
Some("rock")
} else {
spawn_tile(commands, FloorTilePrefab::dirt(position, asset_server));
Some("dirt")
if noise_position.z > position.z {
if z < -15 {
let noise_value = noise.get([world_x as f64 * 0.05, world_y as f64 * 0.05, z as f64 * 0.05]);
if noise_value < -0.5 {
commands.spawn(FloorTilePrefab::air(position, &asset_server));
tilemap.floors.insert(pos_ivec, (0, false, true, 1, [0; 8])); // Air tile
floor_positions.push((position, "air"));
} else if noise_value < 0.8 {
commands.spawn(FloorTilePrefab::rock(position, &asset_server));
tilemap.floors.insert(pos_ivec, (2, true, false, 255, [0; 8])); // Rock tile
floor_positions.push((position, "rock"));
} else {
commands.spawn(FloorTilePrefab::dirt(position, &asset_server));
tilemap.floors.insert(pos_ivec, (1, true, true, 1, [0; 8])); // Dirt tile
floor_positions.push((position, "dirt"));
}
} else {
commands.spawn(FloorTilePrefab::dirt(position, &asset_server));
tilemap.floors.insert(pos_ivec, (1, true, true, 1, [0; 8])); // Dirt tile
floor_positions.push((position, "dirt"));
}
} else if noise_position.z < position.z {
commands.spawn(FloorTilePrefab::air(position, &asset_server));
tilemap.floors.insert(pos_ivec, (0, false, true, 1, [0; 8])); // Air tile
floor_positions.push((position, "air"));
} else {
commands.spawn(FloorTilePrefab::dirt(position, &asset_server));
tilemap.floors.insert(pos_ivec, (1, true, true, 1, [0; 8])); // Dirt tile
floor_positions.push((position, "dirt"));
}
}
} else {
spawn_tile(commands, FloorTilePrefab::dirt(position, asset_server));
Some("dirt")
}
} else if noise_position.z < position.z {
spawn_tile(commands, FloorTilePrefab::air(position, asset_server));
Some("air")
} else {
spawn_tile(commands, FloorTilePrefab::dirt(position, asset_server));
Some("dirt")
};
if let Some(floor_type) = floor_type {
floor_positions.push((position, floor_type));
}
}
}
fn spawn_tile(commands: &mut Commands, bundle: impl Bundle) -> Entity {
commands.spawn(bundle).id()
}
// Generate fixtures and add them to tilemap
for (position, floor_type) in floor_positions.iter() {
let above_pos = *position + Vec3::new(0.0, 0.0, TILE_SIZE);
let above_ivec = above_pos.as_ivec3();
fn generate_fixtures_for_chunk(
commands: &mut Commands,
asset_server: &Res<AssetServer>,
floor_positions: &[(Vec3, &str)],
) {
let floor_map: HashMap<(i32, i32, i32), &str> = floor_positions
.iter()
.map(|(pos, floor_type)| ((pos.x as i32, pos.y as i32, pos.z as i32), *floor_type))
.collect();
for (position, _) in floor_positions {
let x = position.x as i32;
let y = position.y as i32;
let z = position.z as i32;
if let Some(&above_floor_type) = floor_map.get(&(x, y, z + TILE_SIZE as i32)) {
if above_floor_type == "air" {
continue;
}
let fixture_position = Vec3::new(position.x, position.y, position.z + 1.);
match above_floor_type {
match *floor_type {
"dirt" => {
commands.spawn(FixtureTilePrefab::dirt_wall(fixture_position, asset_server));
commands.spawn(FixtureTilePrefab::dirt_wall(above_pos, &asset_server));
tilemap.fixtures.insert(above_ivec, (1, true, [0; 8])); // Dirt wall
}
"rock" => {
commands.spawn(FixtureTilePrefab::rock_wall(fixture_position, asset_server));
commands.spawn(FixtureTilePrefab::rock_wall(above_pos, &asset_server));
tilemap.fixtures.insert(above_ivec, (2, true, [0; 8])); // Rock wall
}
"bedrock" => {
commands.spawn(FixtureTilePrefab::bedrock_wall(
fixture_position,
asset_server,
));
commands.spawn(FixtureTilePrefab::bedrock_wall(above_pos, &asset_server));
tilemap.fixtures.insert(above_ivec, (3, true, [0; 8])); // Bedrock wall
}
_ => {}
}
}
// Emit occlusion event for this chunk
occlusion_events.send(ChunkOcclusionEvent {
chunk_pos,
});
}
}
// System to handle chunk occlusion events
fn handle_chunk_occlusion_events(
mut events: EventReader<ChunkOcclusionEvent>,
tilemap: Res<TileMap>,
mut tile_update_events: EventWriter<TileOcclusionUpdateEvent>,
) {
for event in events.read() {
let chunk_pos = event.chunk_pos;
let tile_size = TILE_SIZE as i32;
let start_x = chunk_pos.x * CHUNK_SIZE * tile_size;
let end_x = start_x + (CHUNK_SIZE * tile_size);
let start_y = chunk_pos.y * CHUNK_SIZE * tile_size;
let end_y = start_y + (CHUNK_SIZE * tile_size);
// Collect positions that need visibility updates
let mut positions = Vec::new();
// Add positions from the current chunk
for x in (start_x..end_x).step_by(tile_size as usize) {
for y in (start_y..end_y).step_by(tile_size as usize) {
for z in -150..50 {
let pos = IVec3::new(y, x, z * tile_size);
if tilemap.floors.contains_key(&pos) || tilemap.fixtures.contains_key(&pos) {
positions.push(pos);
}
}
}
}
// Send update event if we have positions to process
if !positions.is_empty() {
tile_update_events.send(TileOcclusionUpdateEvent { positions });
}
}
}
@@ -190,3 +310,18 @@ impl Plugin for TilemapPlugin {
.add_systems(Update, handle_chunk_loading);
}
}
// Plugin to handle occlusion systems
pub struct OcclusionPlugin;
impl Plugin for OcclusionPlugin {
fn build(&self, app: &mut App) {
app.add_event::<ChunkOcclusionEvent>()
.add_event::<TileOcclusionUpdateEvent>()
.add_systems(Update, (
handle_chunk_occlusion_events,
handle_tile_occlusion_updates,
).chain());
}
}