amethyst_renderer 0.10.4

High-level rendering engine with multiple backends
use std::mem;

use glsl_layout::*;

use amethyst_core::{
    specs::prelude::{Join, ReadStorage},
    GlobalTransform,
};

use crate::{
    cam::Camera,
    light::Light,
    pipe::{Effect, EffectBuilder},
    resources::AmbientColor,
    types::Encoder,
};

#[derive(Clone, Copy, Debug, Uniform)]
pub(crate) struct FragmentArgs {
    point_light_count: uint,
    directional_light_count: uint,
    spot_light_count: uint,
}

#[derive(Clone, Copy, Debug, Uniform)]
pub(crate) struct PointLightPod {
    position: vec3,
    color: vec3,
    pad: float,
    intensity: float,
}

#[derive(Clone, Copy, Debug, Uniform)]
pub(crate) struct DirectionalLightPod {
    color: vec3,
    direction: vec3,
}

#[derive(Clone, Copy, Debug, Uniform)]
pub(crate) struct SpotLightPod {
    position: vec3,
    color: vec3,
    direction: vec3,
    angle: float,
    intensity: float,
    range: float,
    smoothness: float,
}

pub(crate) fn set_light_args(
    effect: &mut Effect,
    encoder: &mut Encoder,
    light: &ReadStorage<'_, Light>,
    global: &ReadStorage<'_, GlobalTransform>,
    ambient: &AmbientColor,
    camera: Option<(&Camera, &GlobalTransform)>,
) {
    let point_lights: Vec<_> = (light, global)
        .join()
        .filter_map(|(light, transform)| {
            if let Light::Point(ref light) = *light {
                let position: [f32; 3] = transform.0.column(3).xyz().into();
                Some(
                    PointLightPod {
                        position: position.into(),
                        color: light.color.into(),
                        intensity: light.intensity,
                        pad: 0.0,
                    }
                    .std140(),
                )
            } else {
                None
            }
        })
        .collect();

    let directional_lights: Vec<_> = light
        .join()
        .filter_map(|light| {
            if let Light::Directional(ref light) = *light {
                Some(
                    DirectionalLightPod {
                        color: light.color.into(),
                        direction: light.direction.into(),
                    }
                    .std140(),
                )
            } else {
                None
            }
        })
        .collect();

    let spot_lights: Vec<_> = (light, global)
        .join()
        .filter_map(|(light, transform)| {
            if let Light::Spot(ref light) = *light {
                let position: [f32; 3] = transform.0.column(3).xyz().into();
                Some(
                    SpotLightPod {
                        position: position.into(),
                        color: light.color.into(),
                        direction: light.direction.into(),
                        angle: light.angle.cos(),
                        intensity: light.intensity,
                        range: light.range,
                        smoothness: light.smoothness,
                    }
                    .std140(),
                )
            } else {
                None
            }
        })
        .collect();

    let fragment_args = FragmentArgs {
        point_light_count: point_lights.len() as u32,
        directional_light_count: directional_lights.len() as u32,
        spot_light_count: spot_lights.len() as u32,
    };

    effect.update_constant_buffer("FragmentArgs", &fragment_args.std140(), encoder);
    effect.update_buffer("PointLights", &point_lights[..], encoder);
    effect.update_buffer("DirectionalLights", &directional_lights[..], encoder);
    effect.update_buffer("SpotLights", &spot_lights[..], encoder);

    effect.update_global("ambient_color", Into::<[f32; 3]>::into(*ambient.as_ref()));

    effect.update_global(
        "camera_position",
        camera
            .as_ref()
            .map(|&(_, ref trans)| trans.0.column(3).xyz().into())
            .unwrap_or([0.0; 3]),
    );
}

pub(crate) fn setup_light_buffers(builder: &mut EffectBuilder<'_>) {
    builder
        .with_raw_constant_buffer(
            "FragmentArgs",
            mem::size_of::<<FragmentArgs as Uniform>::Std140>(),
            1,
        )
        .with_raw_constant_buffer(
            "PointLights",
            mem::size_of::<<PointLightPod as Uniform>::Std140>(),
            128,
        )
        .with_raw_constant_buffer(
            "DirectionalLights",
            mem::size_of::<<DirectionalLightPod as Uniform>::Std140>(),
            16,
        )
        .with_raw_constant_buffer(
            "SpotLights",
            mem::size_of::<<SpotLightPod as Uniform>::Std140>(),
            128,
        )
        .with_raw_global("ambient_color")
        .with_raw_global("camera_position");
}