use crate::{
core::{
algebra::{UnitQuaternion, Vector3},
color::Color,
math::aabb::AxisAlignedBoundingBox,
pool::Handle,
reflect::prelude::*,
type_traits::prelude::*,
uuid::{uuid, Uuid},
uuid_provider,
variable::InheritableVariable,
visitor::{Visit, VisitResult, Visitor},
},
scene::{
base::{Base, BaseBuilder},
debug::SceneDrawingContext,
graph::Graph,
light::{BaseLight, BaseLightBuilder},
node::constructor::NodeConstructor,
node::{Node, NodeTrait},
},
};
use fyrox_graph::constructor::ConstructorProvider;
use fyrox_graph::SceneGraph;
use std::ops::{Deref, DerefMut};
use strum_macros::{AsRefStr, EnumString, VariantNames};
pub const CSM_NUM_CASCADES: usize = 3;
#[derive(Reflect, Clone, Visit, Debug, PartialEq, AsRefStr, EnumString, VariantNames)]
pub enum FrustumSplitOptions {
Absolute {
far_planes: [f32; CSM_NUM_CASCADES],
},
Relative {
fractions: [f32; CSM_NUM_CASCADES],
},
}
uuid_provider!(FrustumSplitOptions = "b2ed128a-b7da-4d34-b027-a0af19c2f563");
impl Default for FrustumSplitOptions {
fn default() -> Self {
Self::Absolute {
far_planes: [5.0, 25.0, 64.0],
}
}
}
#[derive(Reflect, Clone, Visit, PartialEq, Debug)]
pub struct CsmOptions {
pub split_options: FrustumSplitOptions,
#[reflect(min_value = 0.0, step = 0.000025)]
shadow_bias: f32,
}
impl Default for CsmOptions {
fn default() -> Self {
Self {
split_options: Default::default(),
shadow_bias: 0.00025,
}
}
}
impl CsmOptions {
pub fn set_shadow_bias(&mut self, bias: f32) {
self.shadow_bias = bias.max(0.0);
}
pub fn shadow_bias(&self) -> f32 {
self.shadow_bias
}
}
#[derive(Default, Debug, Visit, Reflect, Clone, ComponentProvider)]
#[reflect(derived_type = "Node")]
pub struct DirectionalLight {
#[component(include)]
base_light: BaseLight,
pub csm_options: InheritableVariable<CsmOptions>,
}
impl From<BaseLight> for DirectionalLight {
fn from(base_light: BaseLight) -> Self {
Self {
base_light,
csm_options: Default::default(),
}
}
}
impl Deref for DirectionalLight {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base_light.base
}
}
impl DerefMut for DirectionalLight {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base_light.base
}
}
impl TypeUuidProvider for DirectionalLight {
fn type_uuid() -> Uuid {
uuid!("8b8248e1-1cdf-42a3-9abe-0691de82c519")
}
}
impl DirectionalLight {
pub fn base_light_ref(&self) -> &BaseLight {
&self.base_light
}
pub fn base_light_mut(&mut self) -> &mut BaseLight {
&mut self.base_light
}
}
impl ConstructorProvider<Node, Graph> for DirectionalLight {
fn constructor() -> NodeConstructor {
NodeConstructor::new::<Self>()
.with_variant("Directional Light", |_| {
DirectionalLightBuilder::new(BaseLightBuilder::new(
BaseBuilder::new().with_name("DirectionalLight"),
))
.build_node()
.into()
})
.with_group("Light")
}
}
impl NodeTrait for DirectionalLight {
fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
AxisAlignedBoundingBox::default()
}
fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
self.local_bounding_box()
.transform(&self.global_transform())
}
fn id(&self) -> Uuid {
Self::type_uuid()
}
fn debug_draw(&self, ctx: &mut SceneDrawingContext) {
ctx.draw_arrow(
16,
Color::GREEN,
1.0,
0.2,
self.global_transform()
* UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 180.0f32.to_radians())
.to_homogeneous(),
);
}
}
pub struct DirectionalLightBuilder {
base_light_builder: BaseLightBuilder,
csm_options: CsmOptions,
}
impl DirectionalLightBuilder {
pub fn new(base_light_builder: BaseLightBuilder) -> Self {
Self {
base_light_builder,
csm_options: Default::default(),
}
}
pub fn build_directional_light(self) -> DirectionalLight {
DirectionalLight {
base_light: self.base_light_builder.build(),
csm_options: self.csm_options.into(),
}
}
pub fn with_csm_options(mut self, csm_options: CsmOptions) -> Self {
self.csm_options = csm_options;
self
}
pub fn build_node(self) -> Node {
Node::new(self.build_directional_light())
}
pub fn build(self, graph: &mut Graph) -> Handle<DirectionalLight> {
graph.add_node(self.build_node()).to_variant()
}
}