Log (wip), parallelisation

This commit is contained in:
2025-05-11 22:58:25 +01:00
parent 31e6eb29b2
commit e2ccd0cead
7 changed files with 463 additions and 156 deletions
+9 -5
View File
@@ -22,13 +22,17 @@ pub fn camera_z_movement(
camera_moved.0 = false;
if keyboard_input.just_pressed(KeyCode::ShiftLeft) {
z_index.0 -= 1.0;
z_index.0 = z_index.0.clamp(-tilemap::Z_BELOW + 1.0, tilemap::Z_ABOVE);
z_index.0 = z_index
.0
.clamp(-tilemap::Z_BELOW + 1.0, tilemap::Z_ABOVE - 1.);
camera_moved.0 = true;
println!("z_index = {}", z_index.0);
}
if keyboard_input.just_pressed(KeyCode::ShiftRight) {
z_index.0 += 1.0;
z_index.0 = z_index.0.clamp(-tilemap::Z_BELOW + 1.0, tilemap::Z_ABOVE);
z_index.0 = z_index
.0
.clamp(-tilemap::Z_BELOW + 1.0, tilemap::Z_ABOVE - 1.);
camera_moved.0 = true;
println!("z_index = {}", z_index.0);
}
@@ -77,9 +81,9 @@ pub fn spawn_panning_camera(mut commands: Commands) {
}
pub fn scroll_events(mut evr_scroll: EventReader<MouseWheel>, mut query: Query<&mut Projection>) {
const ZOOM_SENSITIVITY: f32 = 0.05;
const MIN_SCALE: f32 = 0.4;
const MAX_SCALE: f32 = 1.5;
const ZOOM_SENSITIVITY: f32 = 0.035;
const MIN_SCALE: f32 = 0.2;
const MAX_SCALE: f32 = 2.0;
for ev in evr_scroll.read() {
for mut projection_component in query.iter_mut() {
+2 -2
View File
@@ -49,8 +49,8 @@ impl Default for FixtureTile {
#[derive(Resource, Default, Clone)]
pub struct TileMap {
pub floor_tiles: HashMap<IVec3, (u32, bool, bool, u8, [u32; 8])>, // id, opaque, walkable, astar_weight, visible_range
pub fixture_tiles: HashMap<IVec3, (u32, bool, [u32; 8])>,
pub floor_tiles: HashMap<IVec3, (u32, bool, bool, u8, [u32; 8])>, //id, opaque, walkable, astar_weight, visible_range
pub fixture_tiles: HashMap<IVec3, (u32, bool, [u32; 8])>, // id, solid, visible_range
}
pub fn update_tile_visibility(
+225 -69
View File
@@ -1,3 +1,4 @@
use std::sync::Mutex;
use std::time::Instant;
use crate::constants::{ITILE_SIZE, TILE_SIZE};
@@ -12,7 +13,7 @@ use noise::{NoiseFn, Perlin};
pub const CHUNK_SIZE: i32 = 8;
pub const Z_BELOW: f32 = 20.0;
pub const Z_ABOVE: f32 = 10.0;
pub const Z_ABOVE: f32 = 4.0;
pub const Z_TOTAL: f32 = Z_ABOVE + Z_BELOW; // MAX 255 DO NOT EXCEED
#[derive(Resource)]
@@ -29,7 +30,29 @@ impl Default for ChunkMap {
}
#[derive(Event)]
pub struct LoadChunkEvent {
pub struct GenerateChunkEvent {
pub chunk_position: IVec2,
}
#[derive(Event)]
pub struct ChunkTerrainEvent {
pub chunk_position: IVec2,
}
#[derive(Event)]
// CAlculate the weather effecrs and rivers/lakes of this chunk
pub struct ChunkWeatheringAndPrecipitationEvent {
pub chunk_position: IVec2,
}
#[derive(Event)]
pub struct ChunkForrestryEvent {
pub chunk_position: IVec2,
pub floor_tiles: Vec<(Vec3, String)>,
}
#[derive(Event)]
pub struct ChunkFoliageEvent {
pub chunk_position: IVec2,
}
#[derive(Event)]
pub struct ChunkFaunaEvent {
pub chunk_position: IVec2,
}
@@ -142,7 +165,7 @@ pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] {
visible_range
}
pub fn generate_surface_noise(x: i32, y: i32) -> f32 {
pub fn generate_surface_terrain(x: i32, y: i32) -> f32 {
let noise = Perlin::new(0);
let mut noise_value = 0.0;
let mut amplitude = 1.0;
@@ -157,28 +180,67 @@ pub fn generate_surface_noise(x: i32, y: i32) -> f32 {
}
fn generate_chunks_from_algo(
mut commands: Commands,
mut events: EventReader<LoadChunkEvent>,
mut chunk_events: EventReader<GenerateChunkEvent>,
mut terrain_event_writer: EventWriter<ChunkTerrainEvent>,
mut weathering_event_writer: EventWriter<ChunkWeatheringAndPrecipitationEvent>,
mut foliage_event_writer: EventWriter<ChunkFoliageEvent>,
mut fauna_event_writer: EventWriter<ChunkFaunaEvent>,
) {
// Fire each terrain pass. They will all fire sequentially.
for event in chunk_events.read() {
let chunk_pos = event.chunk_position;
terrain_event_writer.write(ChunkTerrainEvent {
chunk_position: chunk_pos,
});
weathering_event_writer.write(ChunkWeatheringAndPrecipitationEvent {
chunk_position: chunk_pos,
});
foliage_event_writer.write(ChunkFoliageEvent {
chunk_position: chunk_pos,
});
fauna_event_writer.write(ChunkFaunaEvent {
chunk_position: chunk_pos,
});
}
}
fn generate_chunk_terrain(
commands: ParallelCommands<'_, '_>, // Use ParallelCommands for parallel spawning
mut events: EventReader<ChunkTerrainEvent>,
mut chunk_map: ResMut<ChunkMap>,
mut tilemap: ResMut<TileMap>,
mut forrestry_event_writer: EventWriter<ChunkForrestryEvent>,
) {
let is_empty = events.is_empty();
let start = Instant::now();
let count = events.len();
let cave_noise = Perlin::new(0);
for event in events.read() {
// Create mutexes for our shared resources
let chunk_map_updates = Mutex::new(HashMap::new());
let tilemap_updates = Mutex::new(HashMap::new());
let forrestry_events = Mutex::new(Vec::new());
events.par_read().for_each(|event| {
let chunk_pos = event.chunk_position;
if chunk_map.loaded_chunks.contains_key(&chunk_pos) {
continue;
// Check if chunk is already loaded - using a local check first to avoid locking
{
let chunk_map_guard = chunk_map.loaded_chunks.get(&chunk_pos);
if let Some(true) = chunk_map_guard {
return;
}
}
chunk_map.loaded_chunks.insert(chunk_pos, true);
// Mark chunk as loaded in our thread-safe collection
chunk_map_updates.lock().unwrap().insert(chunk_pos, true);
let start_x = chunk_pos.x * CHUNK_SIZE;
let start_y = chunk_pos.y * CHUNK_SIZE;
let mut floor_positions = Vec::new();
let mut surface_positions: Vec<(Vec3, String)> = Vec::new();
let mut local_tilemap_updates = HashMap::new();
// Generate tiles for this chunk
for local_y in 0..CHUNK_SIZE {
@@ -189,7 +251,7 @@ fn generate_chunks_from_algo(
let noise_position = Vec3::new(
(world_x as f32 * TILE_SIZE).round(),
(world_y as f32 * TILE_SIZE).round(),
(generate_surface_noise(world_x, world_y) * TILE_SIZE).round(),
(generate_surface_terrain(world_x, world_y) * TILE_SIZE).round(),
);
// Spawn tiles and add them to tilemap
@@ -202,91 +264,168 @@ fn generate_chunks_from_algo(
let pos_ivec = position.as_ivec3();
if z < -5 {
let noise_value = cave_noise.get([
let cave_value = cave_noise.get([
world_x as f64 * 0.05,
world_y as f64 * 0.05,
z as f64 * 0.05,
]);
if noise_value < -0.85 {
FloorTilePrefab::air(position).spawn(&mut commands);
tilemap
.floor_tiles
.insert(pos_ivec, (0, false, true, 0, [0; 8])); // Air tile
floor_positions.push((position, "air"));
} else if noise_value < 0.8 {
FloorTilePrefab::rock(position).spawn(&mut commands);
tilemap
.floor_tiles
.insert(pos_ivec, (2, true, false, 50, [0; 8])); // Rock tile
floor_positions.push((position, "rock"));
if cave_value < -0.75 {
commands.command_scope(|mut cmd| {
FloorTilePrefab::air(position).spawn(&mut cmd);
});
local_tilemap_updates.insert(pos_ivec, (0, false, true, 0, [0; 8]));
// Air tile
} else if cave_value < 0.8 {
commands.command_scope(|mut cmd| {
FloorTilePrefab::rock(position).spawn(&mut cmd);
});
local_tilemap_updates.insert(pos_ivec, (2, true, false, 50, [0; 8]));
// Rock tile
} else {
FloorTilePrefab::dirt(position).spawn(&mut commands);
tilemap
.floor_tiles
.insert(pos_ivec, (1, true, true, 85, [0; 8])); // Dirt tile
floor_positions.push((position, "dirt"));
commands.command_scope(|mut cmd| {
FloorTilePrefab::dirt(position).spawn(&mut cmd);
});
local_tilemap_updates.insert(pos_ivec, (1, true, true, 85, [0; 8]));
// Dirt tile
}
} else if noise_position.z > position.z {
if (generate_surface_noise(world_x, world_y) * TILE_SIZE).round()
if (generate_surface_terrain(world_x, world_y) * TILE_SIZE).round()
<= position.z + TILE_SIZE
{
FloorTilePrefab::grass(position).spawn(&mut commands);
tilemap
.floor_tiles
.insert(pos_ivec, (1, true, true, 100, [0; 8])); // Dirt tile (grass)
floor_positions.push((position, "dirt"));
commands.command_scope(|mut cmd| {
FloorTilePrefab::grass(position).spawn(&mut cmd);
});
local_tilemap_updates.insert(pos_ivec, (1, true, true, 100, [0; 8])); // Dirt tile (grass)
surface_positions.push((position, ("grass").to_string()));
} else {
FloorTilePrefab::dirt(position).spawn(&mut commands);
tilemap
.floor_tiles
.insert(pos_ivec, (1, true, true, 85, [0; 8])); // Dirt tile
floor_positions.push((position, "dirt"));
commands.command_scope(|mut cmd| {
FloorTilePrefab::dirt(position).spawn(&mut cmd);
});
local_tilemap_updates.insert(pos_ivec, (1, true, true, 85, [0; 8]));
// Dirt tile
}
} else {
FloorTilePrefab::air(position).spawn(&mut commands);
tilemap
.floor_tiles
.insert(pos_ivec, (0, false, true, 0, [0; 8])); // Air tile
floor_positions.push((position, "air"));
commands.command_scope(|mut cmd| {
FloorTilePrefab::air(position).spawn(&mut cmd);
});
local_tilemap_updates.insert(pos_ivec, (0, false, true, 0, [0; 8]));
// Air tile
}
}
}
}
// Generate fixtures and add them to tilemap
// Add our local updates to the global mutexes
{
let mut tilemap_guard = tilemap_updates.lock().unwrap();
for (pos, data) in local_tilemap_updates {
tilemap_guard.insert(pos, data);
}
}
// Store forrestry event for this chunk
forrestry_events.lock().unwrap().push(ChunkForrestryEvent {
chunk_position: chunk_pos,
floor_tiles: surface_positions,
});
});
// Apply all collected updates to the actual resources
for (chunk_pos, value) in chunk_map_updates.into_inner().unwrap() {
chunk_map.loaded_chunks.insert(chunk_pos, value);
}
for (pos, data) in tilemap_updates.into_inner().unwrap() {
tilemap.floor_tiles.insert(pos, data);
}
// Send all forrestry events
for event in forrestry_events.into_inner().unwrap() {
forrestry_event_writer.write(event);
}
if !is_empty {
println!("{} terrain chunks loaded in {:.2?}", count, start.elapsed());
}
}
fn generate_chunk_weathering_and_precipitation(// mut commands: Commands,
// mut events: EventReader<GenerateChunkEvent>,
// mut chunk_map: ResMut<ChunkMap>,
// mut tilemap: ResMut<TileMap>,
) {
// TODO: Generate weathering and precipitation
// Temperature and humidity
// Erosion
// Hadley lines etc
// Biomes
}
fn generate_chunk_forrestry(
commands: ParallelCommands<'_, '_>, // Use ParallelCommands for parallel spawning
mut events: EventReader<ChunkForrestryEvent>,
mut tilemap: ResMut<TileMap>,
) {
let start = Instant::now();
let count = events.len();
let collected_tilemap_updates = Mutex::new(Vec::<(IVec3, (u32, bool, [u32; 8]))>::new());
events.par_read().for_each(|event| {
let floor_positions = &event.floor_tiles;
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();
match *floor_type {
"dirt" => {
FixtureTilePrefab::dirt_wall(above_pos).spawn(&mut commands);
tilemap.fixture_tiles.insert(above_ivec, (1, true, [0; 8]));
// Dirt wall
}
"rock" => {
FixtureTilePrefab::rock_wall(above_pos).spawn(&mut commands);
tilemap.fixture_tiles.insert(above_ivec, (2, true, [0; 8]));
// Rock wall
}
"bedrock" => {
FixtureTilePrefab::bedrock_wall(above_pos).spawn(&mut commands);
tilemap.fixture_tiles.insert(above_ivec, (3, true, [0; 8]));
// Bedrock wall
match floor_type.as_str() {
// TODO Fix rendering
"grass" => {
commands.command_scope(|mut commands| {
FixtureTilePrefab::log(above_pos).spawn(&mut commands);
});
collected_tilemap_updates
.lock()
.unwrap()
.push((above_ivec, (1, true, [0; 8])));
}
_ => {}
}
}
});
let collected_updates = collected_tilemap_updates.into_inner().unwrap();
for (ivec, data) in collected_updates {
tilemap.fixture_tiles.insert(ivec, data);
}
if !is_empty {
println!("{} chunks loaded in {:.2?}", count, start.elapsed());
}
println!(
"Forrestry update for {:?} chunks in {:.2?}",
count,
start.elapsed()
);
}
fn setup_initial_chunks(mut event_writer: EventWriter<LoadChunkEvent>) {
fn generate_chunk_foliage(
mut commands: Commands,
mut events: EventReader<GenerateChunkEvent>,
mut chunk_map: ResMut<ChunkMap>,
mut tilemap: ResMut<TileMap>,
) {
}
fn generate_chunk_fauna(
mut commands: Commands,
mut events: EventReader<GenerateChunkEvent>,
mut chunk_map: ResMut<ChunkMap>,
mut tilemap: ResMut<TileMap>,
) {
}
fn setup_initial_chunks(mut event_writer: EventWriter<GenerateChunkEvent>) {
for x in -16..=16 {
for y in -9..=9 {
event_writer.write(LoadChunkEvent {
event_writer.write(GenerateChunkEvent {
chunk_position: IVec2::new(x, y),
});
}
@@ -314,12 +453,29 @@ pub struct TilemapPlugin;
impl Plugin for TilemapPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<ChunkMap>()
.add_event::<LoadChunkEvent>()
.add_event::<GenerateChunkEvent>()
.add_event::<ChunkTerrainEvent>()
.add_event::<ChunkWeatheringAndPrecipitationEvent>()
.add_event::<ChunkForrestryEvent>()
.add_event::<ChunkFoliageEvent>()
.add_event::<ChunkFaunaEvent>()
.add_systems(Startup, (setup_chunk_system, setup_initial_chunks))
.add_systems(
PostStartup,
(generate_chunks_from_algo, handle_tile_occlusion_updates).chain(),
(
generate_chunks_from_algo,
(
generate_chunk_terrain,
generate_chunk_weathering_and_precipitation,
generate_chunk_forrestry,
generate_chunk_foliage,
generate_chunk_fauna,
)
.chain(),
handle_tile_occlusion_updates,
)
.chain(),
)
.add_systems(FixedUpdate, (generate_chunks_from_algo).chain());
.add_systems(FixedUpdate, generate_chunks_from_algo);
}
}
+124 -55
View File
@@ -5,6 +5,7 @@ use bevy::asset::RenderAssetUsages;
use bevy::prelude::*;
use bevy::render::render_resource;
use bevy_platform::collections::hash_map::HashMap;
use rayon::prelude::*;
#[derive(Resource)]
pub struct Textures {
@@ -29,6 +30,7 @@ const SKY_PATH: &str = "sky.png";
const DIRT_WALL_PATH: &str = "dirt_wall.png";
const ROCK_WALL_PATH: &str = "rock_wall.png";
const BEDROCK_WALL_PATH: &str = "bedrock_wall.png";
const LOG_PATH: &str = "log.png";
pub fn initialize_textures(mut commands: Commands, asset_server: Res<AssetServer>) {
let mut textures: HashMap<String, Handle<Image>> = HashMap::new();
@@ -73,6 +75,8 @@ pub fn initialize_textures(mut commands: Commands, asset_server: Res<AssetServer
BEDROCK_WALL_PATH.to_string(),
asset_server.load(BEDROCK_WALL_PATH),
);
texture_ids.insert(FIXTURE_ID_OFFSET + 4, LOG_PATH.to_string());
textures.insert(LOG_PATH.to_string(), asset_server.load(LOG_PATH));
commands.insert_resource(Textures { handles: textures });
commands.insert_resource(TextureIDs { refs: texture_ids });
@@ -91,6 +95,7 @@ pub struct CurrentWorldSpriteState {
pub state: TerrainSpriteState,
}
use std::sync::Mutex;
use std::time::Instant;
#[derive(Component)]
@@ -116,7 +121,7 @@ impl Default for QuiltCache {
// here be dragons :(
pub fn build_quilted_terrain_sprites(
query: Query<(&FloorTile, &Transform)>,
mut commands: Commands,
commands: ParallelCommands<'_, '_>,
mut cwss: ResMut<CurrentWorldSpriteState>,
textures: Res<Textures>,
texture_ids: Res<TextureIDs>,
@@ -130,18 +135,25 @@ pub fn build_quilted_terrain_sprites(
let now = Instant::now();
cwss.state = TerrainSpriteState::InProgress;
for entity in query_sprites.iter() {
commands.entity(entity).despawn();
// Despawn existing terrain sprites
let despawn_entities: Vec<Entity> = query_sprites.iter().collect();
for entity in despawn_entities {
commands.command_scope(|mut cmd| {
cmd.entity(entity).despawn();
});
}
// Collect tiles by z-index first
let mut tiles_by_z: HashMap<usize, Vec<(Vec2, &FloorTile)>> = HashMap::new();
// Calculate bounds for all visible tiles
for z_index in 0..=tilemap::Z_TOTAL as usize {
for (floortile, transform) in query.iter() {
for (floortile, transform) in query.iter() {
let position = Vec2::new(transform.translation.x, transform.translation.y);
for z_index in 0..=tilemap::Z_TOTAL as usize {
let is_visible =
(floortile.visible_range[z_index / 32] & (1 << (z_index % 32) as u32)) != 0;
if is_visible {
let position = Vec2::new(transform.translation.x, transform.translation.y);
tiles_by_z
.entry(z_index)
.or_default()
@@ -150,12 +162,25 @@ pub fn build_quilted_terrain_sprites(
}
}
// For each z-index, create a new quilted texture, baked and composited.
for (z_index, tiles) in tiles_by_z.iter() {
// Thread-safe collections to store results
let dimensions_mutex = Mutex::new(HashMap::new());
let texture_handles_mutex = Mutex::new(HashMap::new());
// Process each z-level in parallel
let z_indices: Vec<usize> = tiles_by_z.keys().cloned().collect();
z_indices.into_par_iter().for_each(|z_index| {
let tiles = if let Some(tiles) = tiles_by_z.get(&z_index) {
tiles
} else {
return; // Skip empty z-levels
};
if tiles.is_empty() {
continue;
return;
}
// Calculate bounds
let min_x_aligned: f32 = ((tiles.iter().map(|(pos, _)| pos.x).reduce(f32::min).unwrap()
- TILE_SIZE / 2.0)
/ TILE_SIZE)
@@ -183,49 +208,56 @@ pub fn build_quilted_terrain_sprites(
let width_px = width_tiles * TILE_PIXELS;
let height_px = height_tiles * TILE_PIXELS;
quilt_cache
.dimensions
.insert(*z_index, (width_px, height_px));
// Store dimensions in our thread-safe map
dimensions_mutex
.lock()
.unwrap()
.insert(z_index, (width_px, height_px));
let mut texture_data = vec![0u8; (width_px * height_px * 4) as usize];
// Process tiles for this z-level
// We can't parallelize this inner loop without more complex locking on texture_data
for (pos, floortile) in tiles {
let texture = textures
.handles
.get(texture_ids.refs.get(&floortile.id).unwrap())
.unwrap();
if let Some(texture_id) = texture_ids.refs.get(&floortile.id) {
if let Some(texture) = textures.handles.get(texture_id) {
let rel_x = pos.x - min_x_aligned;
let rel_y = pos.y - min_y_aligned;
let rel_x = pos.x - min_x_aligned;
let rel_y = pos.y - min_y_aligned;
let tile_x = (rel_x / TILE_SIZE).round() as u32;
let tile_y = (height_tiles as f32 - 1.0 - (rel_y / TILE_SIZE).round()) as u32;
let tile_x = (rel_x / TILE_SIZE).round() as u32;
let tile_y = (height_tiles as f32 - 1.0 - (rel_y / TILE_SIZE).round()) as u32;
let target_x = tile_x * TILE_PIXELS;
let target_y = tile_y * TILE_PIXELS;
let mut data: Vec<&[u8]> = vec![];
let target_x = tile_x * TILE_PIXELS;
let target_y = tile_y * TILE_PIXELS;
let mut data: Vec<&[u8]> = vec![];
let mut base_texture: &Image = &Default::default();
let mut base_texture: &Image = &Default::default();
// Use a thread-safe approach to access images
// In a full implementation, this would require a more sophisticated
// thread-safe access pattern to Assets<Image>
if let Some(_base_texture) = images.get(texture) {
if let Some(_data) = &_base_texture.data {
data.push(_data);
base_texture = _base_texture;
}
}
if let Some(_base_texture) = images.get(texture) {
if let Some(_data) = &_base_texture.data {
data.push(_data);
base_texture = _base_texture;
blit_texture_with_alpha(
data, // tile
&mut texture_data, // terrain
base_texture.size().x as u32,
base_texture.size().y as u32,
width_px,
height_px,
target_x,
target_y,
);
}
}
blit_texture_with_alpha(
data, // tile
&mut texture_data, // terrain
base_texture.size().x as u32,
base_texture.size().y as u32,
width_px,
height_px,
target_x,
target_y,
);
}
// Create the quilted texture
let quilted_texture = Image::new_fill(
render_resource::Extent3d {
width: width_px,
@@ -237,25 +269,48 @@ pub fn build_quilted_terrain_sprites(
render_resource::TextureFormat::Rgba8UnormSrgb,
RenderAssetUsages::RENDER_WORLD,
);
let texture_handle = images.add(quilted_texture);
// In a real implementation, we would need thread-safe access to images
// For now, we'll collect the textures and add them after parallel processing
let center_x = min_x_aligned + (max_x_aligned - min_x_aligned) / 2.0;
let center_y = min_y_aligned + (max_y_aligned - min_y_aligned) / 2.0;
commands.spawn((
Sprite {
image: texture_handle,
..Default::default()
},
Transform::from_xyz(
center_x - TILE_SIZE / 2.0,
center_y - TILE_SIZE / 2.0,
-10.0 * TILE_SIZE,
)
.with_scale(Vec3::splat(PIXEL_RATIO)),
Visibility::Hidden,
TerrainSprite { z_index: *z_index },
));
// Store texture and position data for later spawning
texture_handles_mutex
.lock()
.unwrap()
.insert(z_index, (quilted_texture, center_x, center_y));
});
// Process collected textures and spawn entities
let collected_dimensions = dimensions_mutex.into_inner().unwrap();
let collected_textures = texture_handles_mutex.into_inner().unwrap();
// Update quilt_cache dimensions
for (z_index, dimensions) in collected_dimensions {
quilt_cache.dimensions.insert(z_index, dimensions);
}
// Add images and spawn entities with the collected data
for (z_index, (quilted_texture, center_x, center_y)) in collected_textures {
let texture_handle = images.add(quilted_texture);
commands.command_scope(|mut cmd| {
cmd.spawn((
Sprite {
image: texture_handle,
..Default::default()
},
Transform::from_xyz(
center_x - TILE_SIZE / 2.0,
center_y - TILE_SIZE / 2.0,
-10.0 * TILE_SIZE,
)
.with_scale(Vec3::splat(PIXEL_RATIO)),
Visibility::Hidden,
TerrainSprite { z_index },
));
});
}
quilt_cache.dirty_indices.clear();
@@ -488,7 +543,21 @@ impl FixtureTilePrefab {
FixtureTilePrefab {
transform: Transform::from_translation(position),
tile: FixtureTile {
id: 2,
id: FIXTURE_ID_OFFSET + 2,
..Default::default()
},
visibility: Visibility::Hidden,
needs_occluded: NeedsOccluded {
has_been_occluded: false,
},
}
}
pub fn log(position: Vec3) -> Self {
FixtureTilePrefab {
transform: Transform::from_translation(position),
tile: FixtureTile {
id: FIXTURE_ID_OFFSET + 3,
..Default::default()
},
visibility: Visibility::Hidden,