use bevy::ecs::relationship::Relationship;
use crate::foliage_scene::FoliageRoot;
use crate::foliage_scene::FoliageScene;
use crate::foliage_density::FoliageDensityMapsComponent;
use crate::foliage_proto::FoliageProtoBundle;
use crate::foliage_types::FoliageTypesManifest;
use rand::Rng;
use crate::foliage_chunk::FoliageHeightMapData;
use crate::foliage_chunk::FoliageDimensionsData;
use crate::foliage_density::FoliageDensityMapU8;
use crate::noise::NoiseResource;
use crate::foliage_chunk::{FoliageChunk, FoliageChunkNeedsRebuild};
use bevy::prelude::*;
use bevy::platform::collections::hash_map::HashMap;
use serde::{Deserialize, Serialize};
pub(crate) fn foliage_chunk_layer_plugin(app: &mut App) {
app
.add_systems(
Update,
(
build_chunk_layers,
)
.chain() ) ;
}
#[derive(Component)]
pub struct FoliageChunkLayer {
pub layer_index: usize,
}
fn build_chunk_layers (
chunk_layer_query: Query < ( Entity, &FoliageChunkLayer , &ChildOf ) , Added< FoliageChunkLayer > >,
chunk_query: Query< ( &FoliageChunk, &FoliageHeightMapData, &FoliageDimensionsData ) > ,
global_xform_query: Query < &GlobalTransform >,
foliage_root_query: Query< ( &FoliageRoot,&FoliageScene, & FoliageDensityMapsComponent, &FoliageTypesManifest ) >,
noise_resource: Res<NoiseResource>,
image_assets: Res<Assets<Image>>,
mut commands: Commands,
){
for (chunk_layer_entity, chunk_layer, chunk_layer_parent ) in chunk_layer_query.iter(){
let Ok( (foliage_root, foliage_scene, foliage_density_map, foliage_types ) ) = foliage_root_query.get_single() else {
warn!("no single foliage root found ");
continue ;
};
let Ok( ( foliage_chunk, height_map_data, dimensions_data ) ) = chunk_query.get( chunk_layer_parent.get() ) else {
continue;
};
let global_xform = global_xform_query.get( chunk_layer_entity );
let chunk_id = foliage_chunk.chunk_id; let layer_index = chunk_layer.layer_index;
let Some( foliage_definition_for_layer) = foliage_types.foliage_definitions.get( layer_index ) else {
warn!( "no foliage_definition_for_layer to build chunk layer " );
continue;
};
let Some( layer_density_map) = &foliage_density_map.0.get( &layer_index ) else {
warn!( "no layer density map to build chunk layer " );
continue;
};
let chunk_height_data = & height_map_data;
let noise_texture_handle = &noise_resource.density_noise_texture;
let noise_texture = image_assets
.get(noise_texture_handle)
.expect("no noise texture");
let chunk_rows = foliage_scene.chunk_rows as i32 ;
let height_scale = foliage_scene.height_scale;
let layer_dimensions = dimensions_data.0;
let chunk_dimensions = IVec2::new( layer_dimensions.x / 1 ,layer_dimensions.y / 1 ) ;
let chunk_density_map = layer_density_map.get_sub_section_by_chunk_id(
chunk_id,
chunk_rows as u32,
chunk_dimensions
);
let step_by = 2 ;
for x in (0..chunk_dimensions.x ).step_by( step_by as usize ) {
for y in (0..chunk_dimensions.y).step_by( step_by as usize ) {
let transform_opt = get_foliage_node_spawn_using_noise(
x as usize,
y as usize,
chunk_height_data,
&chunk_density_map,
noise_texture,
height_scale
);
if let Some(xform) = transform_opt {
commands.spawn(
(
xform,
FoliageProtoBundle::new ( foliage_definition_for_layer.clone() ),
Name::new(format!("foliage_node_{}_{}", x, y)),
Visibility::default(),
)
).set_parent( chunk_layer_entity );
}
}
}
}
}
fn get_foliage_node_spawn_using_noise(
x: usize,
y: usize,
chunk_height_data: &FoliageHeightMapData,
chunk_density_map: &FoliageDensityMapU8,
noise_texture: &Image,
height_scale: f32,
) -> Option< Transform > {
let max_chunk_density = 255.0;
let max_noise_value = 255.0;
let density_at_point = chunk_density_map.0[y][x];
let height_at_point = chunk_height_data.0[y][x] ;
let density_scaled = density_at_point as f32 / max_chunk_density ;
let Some( noise_texture_data ) = &noise_texture.data else {
return None ;
} ;
let noise_tex_data_index = (y * 256 + x) as usize % noise_texture_data.len();
let noise_sample_at_point = noise_texture_data[noise_tex_data_index];
let noise_sample_scaled = noise_sample_at_point as f32 / max_noise_value;
if density_scaled < noise_sample_scaled {
return None;
}
let mut rng = rand::thread_rng();
let random_float_x: f32 = rng.r#gen::<f32>() - 0.5;
let random_float_y: f32 = rng.r#gen::<f32>() - 0.5; let random_float_z: f32 = rng.r#gen::<f32>() - 0.5;
let foliage_offset = Vec3::new(
noise_sample_scaled,
0.0,
1.0 - noise_sample_scaled
) * 2.0;
let foliage_translation = Vec3::new(
x as f32 , height_at_point as f32 * height_scale,
y as f32 , );
let normal = get_normal_from_height_data( chunk_height_data , height_scale , x, y ).unwrap_or(Vec3::Y);
let normal_rotation = Quat::from_rotation_arc(Vec3::Y, normal);
let final_rotation = normal_rotation;
Some( Transform::from_translation(foliage_translation).with_rotation(final_rotation) )
}
fn get_normal_from_height_data(
height_data: &FoliageHeightMapData,
height_scale: f32,
x: usize,
y: usize,
) -> Option<Vec3> {
let height_map = &height_data.0;
let rows = height_map.len();
let cols = height_map[0].len();
if y == 0 || y >= rows - 1 || x == 0 || x >= cols - 1 {
return Some(Vec3::Y); }
let h_center = height_map[y][x] as f32;
let h_left = height_map[y][x - 1] as f32;
let h_right = height_map[y][x + 1] as f32;
let h_up = height_map[y - 1][x] as f32;
let h_down = height_map[y + 1][x] as f32;
let grid_spacing = 1.0;
let dx = (h_right - h_left) / (2.0 * grid_spacing);
let dz = (h_down - h_up) / (2.0 * grid_spacing);
let slope_scale_factor = 1.0 / height_scale;
let normal = Vec3::new(
-dx / slope_scale_factor,
1.0,
-dz / slope_scale_factor
);
Some(normal.normalize())
}