use crate::components::PlaceholderEntity;
use crate::plugin::MapStateResource;
use crate::systems::frame_change_detection::{frame_unchanged, FrameChangeDetection};
use bevy::mesh::{Indices, PrimitiveTopology};
use bevy::prelude::*;
use glam::DVec3;
use rustial_engine::TileId;
use std::collections::HashSet;
pub fn sync_placeholders(
mut commands: Commands,
state: Res<MapStateResource>,
mut existing: Query<(
Entity,
&PlaceholderEntity,
&mut Transform,
&MeshMaterial3d<StandardMaterial>,
)>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
detection: Res<FrameChangeDetection>,
) {
if frame_unchanged(&detection, &state.0) {
return;
}
let placeholders = state.0.loading_placeholders();
let style = state.0.placeholder_style();
let camera_origin: DVec3 = state.0.scene_world_origin();
let desired_tiles: HashSet<TileId> = placeholders.iter().map(|ph| ph.tile).collect();
let existing_tiles: HashSet<TileId> = existing.iter().map(|(_, ph, _, _)| ph.tile_id).collect();
if desired_tiles == existing_tiles {
for (_, ph, mut transform, mat_handle) in existing.iter_mut() {
if let Some(placeholder) = placeholders.iter().find(|p| p.tile == ph.tile_id) {
let min = placeholder.bounds.min.position;
let max = placeholder.bounds.max.position;
let cx = (min.x + max.x) * 0.5;
let cy = (min.y + max.y) * 0.5;
transform.translation.x = (cx - camera_origin.x) as f32;
transform.translation.y = (cy - camera_origin.y) as f32;
let opacity = style.shimmer_opacity(placeholder.animation_phase);
if let Some(material) = materials.get_mut(&mat_handle.0) {
material.base_color = Color::linear_rgba(
style.background_color[0],
style.background_color[1],
style.background_color[2],
style.background_color[3] * opacity,
);
}
}
}
return;
}
for (entity, ph, _, _) in existing.iter() {
if !desired_tiles.contains(&ph.tile_id) {
commands.entity(entity).despawn();
}
}
if placeholders.is_empty() {
return;
}
for ph in placeholders {
if existing_tiles.contains(&ph.tile) {
for (_, existing_ph, mut transform, mat_handle) in existing.iter_mut() {
if existing_ph.tile_id == ph.tile {
let min = ph.bounds.min.position;
let max = ph.bounds.max.position;
let cx = (min.x + max.x) * 0.5;
let cy = (min.y + max.y) * 0.5;
transform.translation.x = (cx - camera_origin.x) as f32;
transform.translation.y = (cy - camera_origin.y) as f32;
let opacity = style.shimmer_opacity(ph.animation_phase);
if let Some(material) = materials.get_mut(&mat_handle.0) {
material.base_color = Color::linear_rgba(
style.background_color[0],
style.background_color[1],
style.background_color[2],
style.background_color[3] * opacity,
);
}
break;
}
}
continue;
}
let opacity = style.shimmer_opacity(ph.animation_phase);
let color = Color::linear_rgba(
style.background_color[0],
style.background_color[1],
style.background_color[2],
style.background_color[3] * opacity,
);
let min = ph.bounds.min.position;
let max = ph.bounds.max.position;
let cx = (min.x + max.x) * 0.5;
let cy = (min.y + max.y) * 0.5;
let half_w = ((max.x - min.x) * 0.5) as f32;
let half_h = ((max.y - min.y) * 0.5) as f32;
let z: f32 = -0.01;
let positions = vec![
[-half_w, -half_h, z],
[half_w, -half_h, z],
[half_w, half_h, z],
[-half_w, half_h, z],
];
let normals = vec![[0.0, 0.0, 1.0]; 4];
let uvs = vec![[0.0, 1.0], [1.0, 1.0], [1.0, 0.0], [0.0, 0.0]];
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, Default::default());
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
mesh.insert_indices(Indices::U32(vec![0, 1, 2, 0, 2, 3]));
let mesh_handle = meshes.add(mesh);
let material_handle = materials.add(StandardMaterial {
base_color: color,
unlit: true,
alpha_mode: AlphaMode::Blend,
..Default::default()
});
let tx = (cx - camera_origin.x) as f32;
let ty = (cy - camera_origin.y) as f32;
commands.spawn((
Mesh3d(mesh_handle),
MeshMaterial3d(material_handle),
Transform::from_xyz(tx, ty, 0.0),
Visibility::default(),
PlaceholderEntity { tile_id: ph.tile },
));
}
}