nightshade 0.51.0

A cross-platform data-oriented game engine.
Documentation
//! Commands for spawning and controlling cloth entities.

use crate::ecs::cloth::components::Cloth;
use crate::ecs::material::components::MaterialRef;
use crate::ecs::material::resources::material_registry_insert;
use crate::ecs::mesh::components::RenderMesh;
use crate::ecs::world::{
    BOUNDING_VOLUME, CASTS_SHADOW, CLOTH, GLOBAL_TRANSFORM, LOCAL_TRANSFORM, MATERIAL_REF, NAME,
    RENDER_MESH, World,
    components::{CastsShadow, LocalTransform, Name},
};
use crate::render::material::Material;
use freecs::Entity;
use nalgebra_glm::{Quat, Vec3};

/// Spawns a cloth entity at the given position.
///
/// The cloth hangs from the entity's origin: it spans `cloth.width` along
/// local +X centered on the origin and falls `cloth.height` down local -Y.
/// Move or rotate the entity afterwards and the pinned particles follow.
/// The entity scale must stay (1, 1, 1).
///
/// The entity is a regular mesh entity: a grid mesh named
/// `Cloth_{entity_id}` is registered in the mesh cache, a default
/// double-sided material named `Cloth_{entity_id}` is registered in the
/// material registry, and the entity casts shadows. Replace the material
/// through the registry or by setting
/// [`MaterialRef`](crate::ecs::material::components::MaterialRef) for full
/// PBR control over the sheet's appearance. Cloth materials should be
/// double-sided since both faces of the sheet are visible.
pub fn spawn_cloth(world: &mut World, cloth: Cloth, position: Vec3, name: String) -> Entity {
    let entity = crate::ecs::world::commands::spawn_entities(
        world,
        NAME | LOCAL_TRANSFORM
            | GLOBAL_TRANSFORM
            | CLOTH
            | RENDER_MESH
            | MATERIAL_REF
            | BOUNDING_VOLUME
            | CASTS_SHADOW,
        1,
    )[0];

    world.set(entity, Name(name));
    crate::ecs::world::commands::setup_entity_transforms(
        world,
        entity,
        LocalTransform {
            translation: position,
            rotation: Quat::identity(),
            scale: Vec3::new(1.0, 1.0, 1.0),
        },
    );

    let resource_name = format!("Cloth_{}", entity.id);
    world.set(entity, RenderMesh::new(&resource_name));
    world.set(entity, CastsShadow);
    world.set(entity, super::systems::cloth_bounding_volume(&cloth));

    material_registry_insert(
        &mut world.resources.assets.material_registry,
        resource_name.clone(),
        Material {
            double_sided: true,
            roughness: 0.9,
            metallic: 0.0,
            ..Default::default()
        },
    );
    if let Some(&index) = world
        .resources
        .assets
        .material_registry
        .registry
        .name_to_index
        .get(&resource_name)
    {
        crate::render::generational_registry::registry_add_reference(
            &mut world.resources.assets.material_registry.registry,
            index,
        );
    }
    world.set(entity, MaterialRef::new(resource_name));

    world.set(entity, cloth);
    entity
}

/// Restores a cloth to its rest pose at the entity's current transform.
///
/// Velocities are cleared and every particle returns to the flat grid. The
/// global [`Wind`](super::resources::Wind) immediately acts on the fresh
/// state.
pub fn reset_cloth(world: &mut World, entity: Entity) {
    if let Some(cloth) = world.get_mut::<crate::ecs::cloth::components::Cloth>(entity) {
        cloth.reset_epoch = cloth.reset_epoch.wrapping_add(1);
    }
}