feat: data-oriented chunks optimization with tile registry

- Phase 1: Bit-packed standability (ChunkData with bitsets)
- Phase 2: Reactive connectivity (dirty chunks)
- Phase 3: Async terrain baking (AsyncComputeTaskPool)
- Pathfinding weight system (rock=50 preferred, bedrock=150 avoided)
- Movement speed affected by tile weight
- External tiles.toml for hot-reloadable tile definitions
- TileRegistry singleton for async-safe tile lookups
- Fixed world_to_chunk to use CHUNK_SIZE_TILE (128) not CHUNK_SIZE (8)
- Fixed infinite spawner with Local<bool> state guards
- Fixed spawn coordinate grid alignment

Note: Zigzag pathfinding bug introduced - needs investigation
This commit is contained in:
2026-03-19 19:15:36 +00:00
parent 50788af3c7
commit 9535d65bd7
14 changed files with 423 additions and 84 deletions
+110 -13
View File
@@ -1,11 +1,11 @@
use bevy::prelude::*;
use bevy::tasks::AsyncComputeTaskPool;
use bevy_platform::collections::HashMap;
use bevy_platform::time::Instant;
use noise::{NoiseFn, Perlin};
use std::sync::{Arc, Mutex};
use crate::{
config::TileRegistry,
constants::{SEED, TILE_SIZE},
world::{
tiles::{ChunkData, FloorTileData, TileMap, TerrainSpriteState, CurrentWorldSpriteState},
@@ -70,6 +70,7 @@ fn generate_terrain_blob(chunk_pos: IVec2) -> TerrainBlob {
let cave_noise = Perlin::new(SEED);
let start_x = chunk_pos.x * CHUNK_SIZE;
let start_y = chunk_pos.y * CHUNK_SIZE;
let registry = TileRegistry::global();
let mut chunk_data = ChunkData::new(chunk_pos);
let mut tile_updates: Vec<(IVec3, FloorTileData)> = Vec::new();
@@ -105,51 +106,147 @@ fn generate_terrain_blob(chunk_pos: IVec2) -> TerrainBlob {
z as f64 * 0.05,
]);
if cave_value < -0.75 {
let tile = registry.floor("air");
tile_spawns.push((position, FloorTilePrefab::air(position)));
tile_updates.push((
pos_ivec,
FloorTileData::new(0, true, false, true, 0, [0; 8]),
FloorTileData::new(
tile.id,
tile.can_stand_in,
tile.can_stand_on,
tile.transparent,
tile.astar_weight,
[0; 8],
),
));
chunk_data.set_floor_tile(local_x, local_y, local_z, 0, true, false);
chunk_data.set_floor_tile(
local_x,
local_y,
local_z,
tile.id,
tile.can_stand_in,
tile.can_stand_on,
tile.astar_weight,
);
} else if cave_value < 0.8 {
let tile = registry.floor("rock");
tile_spawns.push((position, FloorTilePrefab::rock(position)));
tile_updates.push((
pos_ivec,
FloorTileData::new(2, false, true, false, 50, [0; 8]),
FloorTileData::new(
tile.id,
tile.can_stand_in,
tile.can_stand_on,
tile.transparent,
tile.astar_weight,
[0; 8],
),
));
chunk_data.set_floor_tile(local_x, local_y, local_z, 2, false, true);
chunk_data.set_floor_tile(
local_x,
local_y,
local_z,
tile.id,
tile.can_stand_in,
tile.can_stand_on,
tile.astar_weight,
);
} else {
let tile = registry.floor("dirt");
tile_spawns.push((position, FloorTilePrefab::dirt(position)));
tile_updates.push((
pos_ivec,
FloorTileData::new(1, false, true, false, 85, [0; 8]),
FloorTileData::new(
tile.id,
tile.can_stand_in,
tile.can_stand_on,
tile.transparent,
tile.astar_weight,
[0; 8],
),
));
chunk_data.set_floor_tile(local_x, local_y, local_z, 1, false, true);
chunk_data.set_floor_tile(
local_x,
local_y,
local_z,
tile.id,
tile.can_stand_in,
tile.can_stand_on,
tile.astar_weight,
);
}
} else if noise_position.z > position.z {
if surface_height <= position.z + TILE_SIZE {
let tile = registry.floor("grass");
tile_spawns.push((position, FloorTilePrefab::grass(position)));
tile_updates.push((
pos_ivec,
FloorTileData::new(1, false, true, false, 100, [0; 8]),
FloorTileData::new(
tile.id,
tile.can_stand_in,
tile.can_stand_on,
tile.transparent,
tile.astar_weight,
[0; 8],
),
));
chunk_data.set_floor_tile(local_x, local_y, local_z, 1, false, true);
chunk_data.set_floor_tile(
local_x,
local_y,
local_z,
tile.id,
tile.can_stand_in,
tile.can_stand_on,
tile.astar_weight,
);
surface_positions.push((position, "grass".to_string()));
} else {
let tile = registry.floor("dirt");
tile_spawns.push((position, FloorTilePrefab::dirt(position)));
tile_updates.push((
pos_ivec,
FloorTileData::new(1, false, true, false, 85, [0; 8]),
FloorTileData::new(
tile.id,
tile.can_stand_in,
tile.can_stand_on,
tile.transparent,
tile.astar_weight,
[0; 8],
),
));
chunk_data.set_floor_tile(local_x, local_y, local_z, 1, false, true);
chunk_data.set_floor_tile(
local_x,
local_y,
local_z,
tile.id,
tile.can_stand_in,
tile.can_stand_on,
tile.astar_weight,
);
}
} else {
let tile = registry.floor("air");
tile_spawns.push((position, FloorTilePrefab::air(position)));
tile_updates.push((
pos_ivec,
FloorTileData::new(0, true, false, true, 0, [0; 8]),
FloorTileData::new(
tile.id,
tile.can_stand_in,
tile.can_stand_on,
tile.transparent,
tile.astar_weight,
[0; 8],
),
));
chunk_data.set_floor_tile(local_x, local_y, local_z, 0, true, false);
chunk_data.set_floor_tile(
local_x,
local_y,
local_z,
tile.id,
tile.can_stand_in,
tile.can_stand_on,
tile.astar_weight,
);
}
}
}