use crate::{
core::{
algebra::Vector3,
math::aabb::AxisAlignedBoundingBox,
pool::Handle,
reflect::prelude::*,
type_traits::prelude::*,
uuid::{uuid, Uuid},
variable::InheritableVariable,
visitor::prelude::*,
},
graph::{constructor::ConstructorProvider, SceneGraph},
scene::EnvironmentLightingSource,
scene::{
base::{Base, BaseBuilder},
graph::Graph,
node::{constructor::NodeConstructor, Node, NodeTrait, UpdateContext},
},
};
use fyrox_core::color::Color;
use fyrox_texture::{TextureResource, TextureResourceExtension};
use std::{
cell::Cell,
ops::{Deref, DerefMut},
};
use strum_macros::{AsRefStr, EnumString, VariantNames};
const DEFAULT_RESOLUTION: usize = 512;
#[derive(
Clone,
Reflect,
PartialEq,
Default,
Debug,
Visit,
TypeUuidProvider,
AsRefStr,
EnumString,
VariantNames,
)]
#[type_uuid(id = "66450303-4f6c-4456-bde5-a7309f25b7ce")]
pub enum UpdateMode {
#[default]
Once,
EachFrame,
}
#[derive(Clone, Reflect, Debug, Visit, ComponentProvider, TypeUuidProvider)]
#[type_uuid(id = "7e0c138f-e371-4045-bd2c-ff5b165c7ee6")]
#[reflect(derived_type = "Node")]
#[visit(optional, post_visit_method = "on_visited")]
pub struct ReflectionProbe {
base: Base,
pub rendering_position: InheritableVariable<Vector3<f32>>,
#[reflect(max_value = 2048.0, min_value = 16.0, setter = "set_resolution")]
pub resolution: InheritableVariable<usize>,
#[reflect(min_value = 0.0)]
pub z_near: InheritableVariable<f32>,
#[reflect(min_value = 0.0)]
pub z_far: InheritableVariable<f32>,
pub update_mode: InheritableVariable<UpdateMode>,
pub ambient_lighting_color: InheritableVariable<Color>,
pub environment_lighting_source: InheritableVariable<EnvironmentLightingSource>,
#[reflect(hidden)]
#[visit(skip)]
pub need_update: bool,
#[reflect(hidden)]
#[visit(skip)]
pub(crate) updated: Cell<bool>,
#[reflect(hidden)]
render_target: TextureResource,
}
impl Default for ReflectionProbe {
fn default() -> Self {
Self {
base: Default::default(),
rendering_position: Default::default(),
resolution: DEFAULT_RESOLUTION.into(),
z_near: 0.001.into(),
z_far: 128.0.into(),
update_mode: Default::default(),
ambient_lighting_color: Color::repeat_opaque(120).into(),
environment_lighting_source: Default::default(),
need_update: true,
updated: Cell::new(false),
render_target: TextureResource::new_cube_render_target(DEFAULT_RESOLUTION as u32),
}
}
}
impl ReflectionProbe {
pub fn set_resolution(&mut self, resolution: usize) -> usize {
let old = self.resolution.set_value_and_mark_modified(resolution);
self.recreate_render_target();
old
}
pub fn resolution(&self) -> usize {
*self.resolution
}
pub fn render_target(&self) -> &TextureResource {
&self.render_target
}
pub fn force_update(&mut self) {
self.need_update = true;
self.updated.set(false);
}
pub fn global_rendering_position(&self) -> Vector3<f32> {
self.global_position() + *self.rendering_position
}
fn recreate_render_target(&mut self) {
self.render_target = TextureResource::new_cube_render_target(*self.resolution as u32);
self.force_update();
}
fn on_visited(&mut self, visitor: &mut Visitor) {
if visitor.is_reading() {
self.recreate_render_target();
}
}
}
impl Deref for ReflectionProbe {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for ReflectionProbe {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl ConstructorProvider<Node, Graph> for ReflectionProbe {
fn constructor() -> NodeConstructor {
NodeConstructor::new::<Self>()
.with_group("Light")
.with_variant("Reflection Probe", |_| {
ReflectionProbeBuilder::new(BaseBuilder::new().with_name("Reflection Probe"))
.build_node()
.into()
})
}
}
impl NodeTrait for ReflectionProbe {
fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
self.base.local_bounding_box()
}
fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
self.base.world_bounding_box()
}
fn id(&self) -> Uuid {
Self::type_uuid()
}
fn update(&mut self, _context: &mut UpdateContext) {
match *self.update_mode {
UpdateMode::Once => {
if self.need_update {
self.updated.set(false);
self.need_update = false;
}
}
UpdateMode::EachFrame => {
self.updated.set(false);
}
}
}
}
pub struct ReflectionProbeBuilder {
base_builder: BaseBuilder,
offset: Vector3<f32>,
z_near: f32,
z_far: f32,
resolution: usize,
update_mode: UpdateMode,
ambient_lighting_color: Color,
environment_lighting_source: EnvironmentLightingSource,
}
impl ReflectionProbeBuilder {
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
base_builder,
offset: Default::default(),
z_near: 0.1,
z_far: 32.0,
resolution: DEFAULT_RESOLUTION,
update_mode: Default::default(),
ambient_lighting_color: Color::repeat_opaque(120),
environment_lighting_source: Default::default(),
}
}
pub fn with_rendering_local_position(mut self, offset: Vector3<f32>) -> Self {
self.offset = offset;
self
}
pub fn with_z_near(mut self, z_near: f32) -> Self {
self.z_near = z_near;
self
}
pub fn with_z_far(mut self, z_far: f32) -> Self {
self.z_far = z_far;
self
}
pub fn with_resolution(mut self, resolution: usize) -> Self {
self.resolution = resolution;
self
}
pub fn with_update_mode(mut self, mode: UpdateMode) -> Self {
self.update_mode = mode;
self
}
pub fn with_environment(
mut self,
environment_lighting_source: EnvironmentLightingSource,
) -> Self {
self.environment_lighting_source = environment_lighting_source;
self
}
pub fn with_ambient_lighting_color(mut self, ambient_lighting_color: Color) -> Self {
self.ambient_lighting_color = ambient_lighting_color;
self
}
pub fn build_node(self) -> Node {
Node::new(ReflectionProbe {
base: self.base_builder.build_base(),
rendering_position: self.offset.into(),
resolution: self.resolution.into(),
z_near: self.z_near.into(),
z_far: self.z_far.into(),
update_mode: self.update_mode.into(),
ambient_lighting_color: self.ambient_lighting_color.into(),
environment_lighting_source: self.environment_lighting_source.into(),
need_update: true,
updated: Cell::new(false),
render_target: TextureResource::new_cube_render_target(self.resolution as u32),
})
}
pub fn build(self, graph: &mut Graph) -> Handle<ReflectionProbe> {
graph.add_node(self.build_node()).to_variant()
}
}