use crate::{
density_map::DensityMapSampler, height_map::cpu_sampler::HeightMapCpuSampler, prelude::*,
scatter::utils::*,
};
use bevy_derive::Deref;
use bevy_ecs::prelude::*;
use bevy_math::{Quat, Vec3};
use bevy_pbr::StandardMaterial;
use bevy_reflect::Reflect;
use bevy_transform::prelude::*;
use rand::Rng;
use rand_pcg::{Pcg64, rand_core::SeedableRng};
use std::{
hash::{Hash, Hasher},
marker::PhantomData,
slice::Iter,
};
#[derive(EntityEvent, Component, Reflect)]
pub struct Scatter<T = StandardMaterial>
where
T: ScatterMaterial,
{
pub entity: Entity,
#[reflect(ignore)]
_marker: PhantomData<T>,
}
impl<T> Scatter<T>
where
T: ScatterMaterial,
{
pub fn new(entity: Entity) -> Self {
Self {
entity,
_marker: PhantomData,
}
}
}
impl<T> From<Entity> for Scatter<T>
where
T: ScatterMaterial,
{
fn from(value: Entity) -> Self {
Self::new(value)
}
}
#[derive(EntityEvent, Message, Component, Reflect)]
pub struct ScatterChunk<T = StandardMaterial>
where
T: ScatterMaterial,
{
pub entity: Entity,
pub scatter_layer: Entity,
#[reflect(ignore)]
_marker: PhantomData<T>,
}
impl<T> ScatterChunk<T>
where
T: ScatterMaterial,
{
pub fn new(entity: Entity, scatter_layer: Entity) -> Self {
Self {
entity,
scatter_layer,
_marker: PhantomData,
}
}
}
#[derive(Clone, Copy, Debug, Reflect)]
pub struct ScatterResult {
pub transform: Transform,
pub seed: u64,
}
impl ScatterResult {
pub fn try_from_container_and_modifiers(
container: &Container,
modifiers: &InstanceModifiers,
rng: &mut impl Rng,
external_avoidance_data: &ScatterOccupancyMap,
) -> Option<ScatterResult> {
let instances_dim = container.instances_dim;
let cell_x = container.size.x / instances_dim;
let cell_z = container.size.z / instances_dim;
let local_cell_x = rng.random_range(0.0..instances_dim).floor();
let local_cell_z = rng.random_range(0.0..instances_dim).floor();
let local_cell_corner =
container.corner + Vec3::new(local_cell_x * cell_x, 0.0, local_cell_z * cell_z);
let mut local_pos = local_cell_corner + Vec3::new(cell_x / 2.0, 0.0, cell_z / 2.0);
let jitter = modifiers.jitter.map_or(0., |j| **j).clamp(0.0, 1.0);
if jitter > 0. {
let max_offset_x = (cell_x * jitter) / 2.0;
let max_offset_z = (cell_z * jitter) / 2.0;
local_pos += Vec3::new(
rng.random_range(-max_offset_x..max_offset_x),
0.0,
rng.random_range(-max_offset_z..max_offset_z),
);
};
let mut world_pos = container.global_transform.transform_point(local_pos);
let mut root_pos = container
.root_global_transform
.affine()
.inverse()
.transform_point3(world_pos);
if modifiers
.density_sampler
.as_ref()
.is_some_and(|sampler| rng.random::<f32>() > sampler.sample(root_pos))
{
return None;
}
root_pos.y = modifiers
.map_height
.map(|_| modifiers.height_sampler.sample(root_pos))
.unwrap_or(root_pos.y);
if external_avoidance_data.is_occupied(root_pos) {
return None;
}
world_pos = container.root_global_transform.transform_point(root_pos);
let world_rotation = modifiers
.rotation
.cloned()
.map_or(Quat::IDENTITY, |r| r.into_quad(rng));
let world_scale = modifiers
.scale
.cloned()
.map_or(Vec3::ONE, |s| s.into_vec3(rng));
let instance_gtf = GlobalTransform::from(Transform {
translation: world_pos,
rotation: world_rotation,
scale: world_scale,
});
let seed = generate_instance_seed(container.seed, root_pos);
let transform = instance_gtf.relative_to(&container.global_transform);
Some(ScatterResult { seed, transform })
}
}
impl PartialEq for ScatterResult {
fn eq(&self, other: &Self) -> bool {
self.transform.translation.x.to_bits() == other.transform.translation.x.to_bits()
&& self.transform.translation.y.to_bits() == other.transform.translation.y.to_bits()
&& self.transform.translation.z.to_bits() == other.transform.translation.z.to_bits()
}
}
impl Eq for ScatterResult {}
impl Hash for ScatterResult {
fn hash<H: Hasher>(&self, state: &mut H) {
self.transform.translation.x.to_bits().hash(state);
self.transform.translation.y.to_bits().hash(state);
self.transform.translation.z.to_bits().hash(state);
}
}
#[derive(EntityEvent, Message, Clone, Debug, Reflect)]
pub struct ScatterResults<T = StandardMaterial>
where
T: ScatterMaterial,
{
pub entity: Entity,
pub data: Vec<ScatterResult>,
pub chunk: Option<Entity>,
pub layer: Entity,
pub root: Entity,
pub seed: u64,
pub container_global_transform: GlobalTransform,
#[reflect(ignore)]
_marker: PhantomData<T>,
}
impl<T> From<ScatterTaskData> for ScatterResults<T>
where
T: ScatterMaterial,
{
fn from(task_data: ScatterTaskData) -> Self {
let density_sampler = task_data
.density_map_image
.as_ref()
.map(|img| DensityMapSampler::new(img, task_data.container.root_size));
let height_sampler = task_data
.height_map_config
.as_ref()
.and_then(|cfg| {
task_data
.height_map_image
.as_ref()
.map(|img| HeightMapSampler::Cpu(HeightMapCpuSampler::new(img, cfg)))
})
.unwrap_or(HeightMapSampler::Default(DefaultSampler));
let instance_modifiers = InstanceModifiers::from(&task_data)
.with_density_sampler(density_sampler.as_ref())
.with_height_sampler(&height_sampler);
ScatterResults::<T>::from_container_with_data(
&task_data.container,
instance_modifiers,
&task_data.external_avoidance_data,
)
}
}
impl<T> ScatterResults<T>
where
T: ScatterMaterial,
{
pub fn get(&self) -> &Vec<ScatterResult> {
&self.data
}
pub fn iter(&self) -> Iter<'_, ScatterResult> {
self.data.iter()
}
pub fn new(
entity: Entity,
root: Entity,
layer: Entity,
chunk: Option<Entity>,
data: Vec<ScatterResult>,
seed: u64,
container_gtf: GlobalTransform,
) -> Self {
Self {
entity,
root,
layer,
chunk,
data,
seed,
container_global_transform: container_gtf,
_marker: PhantomData,
}
}
pub fn with_data(mut self, data: Vec<ScatterResult>) -> Self {
self.data = data;
self
}
pub fn from_container_with_data(
container: &Container,
modifiers: InstanceModifiers,
external_avoidance_data: &ScatterOccupancyMap,
) -> ScatterResults<T>
where
T: ScatterMaterial,
{
let mut rng = Pcg64::seed_from_u64(container.seed);
let density = modifiers.density.map_or(1.0, |d| **d).clamp(0.0, 1.0);
let total_cells = (container.instances_dim as u32).pow(2);
let capacity = (total_cells as f32 * density).ceil() as usize;
let mut results = Vec::with_capacity(capacity);
results.extend((0..total_cells).filter_map(|_| {
if rng.random::<f32>() > density {
return None;
}
ScatterResult::try_from_container_and_modifiers(
container,
&modifiers,
&mut rng,
external_avoidance_data,
)
}));
ScatterResults::<T>::from(container).with_data(results)
}
}
impl<T> From<&Container> for ScatterResults<T>
where
T: ScatterMaterial,
{
fn from(value: &Container) -> Self {
Self::new(
value.entity,
value.root_entity,
value.layer_entity,
value.chunk_entity,
vec![],
value.seed,
value.global_transform,
)
}
}
#[derive(EntityEvent, Message, Clone, Copy, Reflect, Debug)]
pub struct ScatterFinished<T = StandardMaterial>
where
T: ScatterMaterial,
{
pub entity: Entity,
#[reflect(ignore)]
_marker: PhantomData<T>,
}
impl<T> From<Entity> for ScatterFinished<T>
where
T: ScatterMaterial,
{
fn from(value: Entity) -> Self {
Self::new(value)
}
}
impl<T> ScatterFinished<T>
where
T: ScatterMaterial,
{
pub fn new(entity: Entity) -> Self {
Self {
entity,
_marker: PhantomData,
}
}
}
#[derive(Message, Clone, Deref, Debug)]
pub struct ClearScatterLayer(pub Entity);
impl From<Entity> for ClearScatterLayer {
fn from(value: Entity) -> Self {
Self(value)
}
}
#[derive(Message, Clone, Deref, Debug)]
pub struct ClearScatterRoot(pub Entity);
impl From<Entity> for ClearScatterRoot {
fn from(value: Entity) -> Self {
Self(value)
}
}