bevy_feronia 0.8.2

Foliage/grass scattering tools and wind simulation shaders/materials that prioritize visual fidelity/artistic freedom, a declarative api and modularity.
Documentation
use crate::height_map::cpu_sampler::HeightMapCpuSampler;
use crate::prelude::*;

use bevy_asset::Assets;
use bevy_camera::primitives::Aabb;
use bevy_ecs::prelude::*;
use bevy_image::Image;
use bevy_math::{IVec2, Vec3};
use bevy_transform::prelude::{GlobalTransform, Transform};

pub fn setup_chunks(
    mut cmd: Commands,
    images: Res<Assets<Image>>,
    height_map_cfg: Option<Res<HeightMapConfig>>,
    height_map: Option<Res<HeightMap>>,
    q_root: Query<
        (
            Entity,
            &LodConfig,
            &ChunkSizeScalarConfig,
            &Aabb,
            &GlobalTransform,
            &ChunkRootSizeDim,
        ),
        (With<ChunkRoot>, Without<BaseChunkSize>),
    >,
) {
    let has_height_map = height_map_cfg.is_some();
    let height_sampler = height_map
        .as_deref()
        .and_then(|height_map_image| {
            height_map_cfg.as_deref().and_then(|cfg| {
                images
                    .get(&height_map_image.0)
                    .map(|img| HeightMapSampler::Cpu(HeightMapCpuSampler::new(img, cfg)))
            })
        })
        .unwrap_or(HeightMapSampler::Default(DefaultSampler));

    for (entity, lod_cfg, scalar_config, aabb, gtf, chunk_root_size) in &q_root {
        let root_scale = gtf.compute_transform().scale;

        let local_aabb_size = Vec3::from(aabb.half_extents * 2.0);
        let center_offset = Vec3::from(aabb.half_extents);

        let world_aabb_size = local_aabb_size * root_scale;

        let top_lod = lod_cfg.get_max_lod();
        let top_scalar_config = scalar_config.get_scalar_config(top_lod);

        let top_chunk_size_local =
            (local_aabb_size / **chunk_root_size as f32).with_y(local_aabb_size.y);
        let top_chunk_size_world =
            (world_aabb_size / **chunk_root_size as f32).with_y(world_aabb_size.y);

        let base_chunk_size_world =
            BaseChunkSize(top_chunk_size_world / **top_scalar_config as f32);

        let chunk_lod_cfg =
            ChunkLodConfig::from_sources(lod_cfg, scalar_config, &base_chunk_size_world, 5.0);

        cmd.entity(entity)
            .insert((base_chunk_size_world, chunk_lod_cfg.clone()));

        let inverse_transform = gtf.affine().inverse();

        for z in 0..**chunk_root_size {
            for x in 0..**chunk_root_size {
                let grid_offset = Vec3::new(x as f32, 0., z as f32)
                    * top_chunk_size_local.with_y(0.)
                    + top_chunk_size_local.with_y(0.) / 2.
                    - center_offset.with_y(0.);

                let mut local_pos = Vec3::from(aabb.center) + grid_offset;

                if has_height_map {
                    let world_pos = gtf.transform_point(local_pos);

                    let height = height_sampler.sample(world_pos);

                    let target_world_pos = Vec3::new(world_pos.x, height, world_pos.z);

                    local_pos = inverse_transform.transform_point3(target_world_pos);
                }

                let child_lod_config =
                    chunk_lod_cfg.get_lod_config(chunk_lod_cfg.get_max_lod() - 1);

                cmd.spawn((
                    Chunk,
                    ChunkLevel(top_lod),
                    ChunkSize(**top_scalar_config),
                    Transform::from_translation(local_pos),
                    ChildOf(entity),
                    ChunkOf(entity),
                    SplitDistance(*child_lod_config),
                    ChunkCoord(IVec2::new(x as i32, z as i32)),
                ));
            }
        }
    }
}