ambient_core 0.2.1

Ambient core functionality. Host-only.
Documentation
use std::collections::HashSet;

use ambient_ecs::{
    components, query_mut, Debuggable, EntityId, FramedEventsReader, MaybeResource, Networked,
    Store, System, SystemGroup, World,
};
use ambient_std::{
    shapes::{Sphere, AABB},
    sparse_vec::SparseVec,
};
use glam::{uvec4, UVec4};
use itertools::Itertools;

use crate::{
    gpu, gpu_components,
    gpu_ecs::{
        gpu_world, ArchChangeDetection, ComponentToGpuSystem, GpuComponentFormat, GpuWorldSyncEvent,
    },
    hierarchy::children,
    transform::local_to_world,
};

components!("rendering", {
    @[Debuggable, Networked, Store]
    visibility_from: EntityId,
    @[Debuggable, Networked, Store, MaybeResource]
    local_bounding_aabb: AABB,
    @[Debuggable, Networked, Store]
    world_bounding_aabb: AABB,
    @[Debuggable]
    world_bounding_sphere: Sphere,
});
gpu_components! {
    world_bounding_sphere() => world_bounding_sphere: GpuComponentFormat::Vec4,
    visibility_from() => visibility_from: GpuComponentFormat::Vec4,
}

pub fn bounding_systems() -> SystemGroup {
    SystemGroup::new(
        "bounding",
        vec![query_mut(
            (world_bounding_aabb(), world_bounding_sphere()),
            (local_bounding_aabb().changed(), local_to_world().changed()),
        )
        .to_system(|q, world, qs, _| {
            for (_, (world_aabb, world_sphere), (aabb, local_to_world)) in q.iter(world, qs) {
                let world_box = aabb.transform(local_to_world);
                *world_aabb = world_box.to_aabb();
                *world_sphere = world_box.to_sphere();
            }
        })],
    )
}

pub fn gpu_world_systems() -> SystemGroup<GpuWorldSyncEvent> {
    SystemGroup::new(
        "bounding/gpu_world",
        vec![
            Box::new(ComponentToGpuSystem::new(
                GpuComponentFormat::Vec4,
                world_bounding_sphere(),
                gpu_components::world_bounding_sphere(),
            )),
            Box::new(VisibilityFromToGpuSystem::new()),
        ],
    )
}

pub fn calc_world_bounding_recursive(world: &World, id: EntityId) -> Option<AABB> {
    let mut aabbs = Vec::new();
    if let Ok(aabb) = world.get(id, world_bounding_aabb()) {
        aabbs.push(aabb);
    }
    if let Ok(childs) = world.get_ref(id, children()) {
        for c in childs {
            if let Some(aabb) = calc_world_bounding_recursive(world, *c) {
                aabbs.push(aabb);
            }
        }
    }

    AABB::unions(&aabbs)
}

struct VisibilityFromToGpuSystem {
    entity_sets: SparseVec<HashSet<EntityId>>,
    event_readers: SparseVec<FramedEventsReader<EntityId>>,
    changed: ArchChangeDetection,
}
impl VisibilityFromToGpuSystem {
    fn new() -> Self {
        Self {
            entity_sets: SparseVec::new(),
            event_readers: SparseVec::new(),
            changed: ArchChangeDetection::new(),
        }
    }
}
impl System<GpuWorldSyncEvent> for VisibilityFromToGpuSystem {
    fn run(&mut self, world: &mut World, _: &GpuWorldSyncEvent) {
        ambient_profiling::scope!("VisibilityFromToGpu.run");
        let gpu_world = world.resource(gpu_world()).lock();
        let gpu = world.resource(gpu());
        for arch in world.archetypes() {
            if let Some((gpu_buff, offset, layout_version)) = gpu_world.get_buffer(
                GpuComponentFormat::Vec4,
                gpu_components::visibility_from(),
                arch.id,
            ) {
                let content_changed = self
                    .changed
                    .changed(arch, visibility_from(), layout_version);
                let buf = arch.get_component_buffer(visibility_from()).unwrap();
                if content_changed {
                    let entity_set: HashSet<EntityId> = buf.data.iter().copied().collect();
                    self.entity_sets.set(arch.id, entity_set);
                }
                let mut loc_changed = false;
                let reader = self
                    .event_readers
                    .get_mut_or_insert_with(arch.id, FramedEventsReader::new);
                let entity_set = self.entity_sets.get(arch.id).unwrap();
                for (_, id) in reader.iter(world.loc_changed()) {
                    if entity_set.contains(id) {
                        loc_changed = true;
                        break;
                    }
                }
                if content_changed || loc_changed {
                    let data = buf
                        .data
                        .iter()
                        .map(&|value: &EntityId| {
                            if let Some(loc) = world.entity_loc(*value) {
                                uvec4(loc.archetype as u32, loc.index as u32, 0, 0)
                            } else {
                                UVec4::ZERO
                            }
                        })
                        .collect_vec();

                    gpu.queue
                        .write_buffer(gpu_buff, offset, bytemuck::cast_slice(&data));
                }
            }
        }
    }
}
impl std::fmt::Debug for VisibilityFromToGpuSystem {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("VisibilityFromToGpu").finish()
    }
}