1use crate::scene::node::constructor::NodeConstructor;
24use crate::{
25 asset::{
26 embedded_data_source, manager::BuiltInResource, state::LoadError, untyped::ResourceKind,
27 },
28 core::{
29 algebra::{Matrix4, Point3, Vector2, Vector3, Vector4},
30 color::Color,
31 log::Log,
32 math::{aabb::AxisAlignedBoundingBox, frustum::Frustum, ray::Ray, Rect},
33 pool::Handle,
34 reflect::prelude::*,
35 type_traits::prelude::*,
36 uuid::{uuid, Uuid},
37 uuid_provider,
38 variable::InheritableVariable,
39 visitor::{Visit, VisitResult, Visitor},
40 },
41 graph::BaseSceneGraph,
42 resource::texture::{
43 CompressionOptions, Texture, TextureImportOptions, TextureKind, TextureMinificationFilter,
44 TexturePixelKind, TextureResource, TextureResourceExtension, TextureWrapMode,
45 },
46 scene::{
47 base::{Base, BaseBuilder},
48 debug::SceneDrawingContext,
49 graph::Graph,
50 node::{Node, NodeTrait, UpdateContext},
51 },
52};
53use fyrox_graph::constructor::ConstructorProvider;
54use lazy_static::lazy_static;
55use serde::{Deserialize, Serialize};
56use std::{
57 fmt::{Display, Formatter},
58 ops::{Deref, DerefMut},
59};
60use strum_macros::{AsRefStr, EnumString, VariantNames};
61
62#[derive(Reflect, Clone, Debug, PartialEq, Visit, Serialize, Deserialize)]
66pub struct PerspectiveProjection {
67 #[reflect(min_value = 0.0, max_value = 6.28, step = 0.1)]
71 pub fov: f32,
72 #[reflect(min_value = 0.0, step = 0.1)]
75 pub z_near: f32,
76 #[reflect(min_value = 0.0, step = 0.1)]
79 pub z_far: f32,
80}
81
82impl Default for PerspectiveProjection {
83 fn default() -> Self {
84 Self {
85 fov: 75.0f32.to_radians(),
86 z_near: 0.025,
87 z_far: 2048.0,
88 }
89 }
90}
91
92impl PerspectiveProjection {
93 #[inline]
95 pub fn matrix(&self, frame_size: Vector2<f32>) -> Matrix4<f32> {
96 let limit = 10.0 * f32::EPSILON;
97
98 let z_near = self.z_far.min(self.z_near);
99 let mut z_far = self.z_far.max(self.z_near);
100
101 if z_far - z_near < limit {
103 z_far += limit;
104 }
105
106 Matrix4::new_perspective(
107 (frame_size.x / frame_size.y).max(limit),
108 self.fov,
109 z_near,
110 z_far,
111 )
112 }
113}
114
115#[derive(Reflect, Clone, Debug, PartialEq, Visit, Serialize, Deserialize)]
118pub struct OrthographicProjection {
119 #[reflect(min_value = 0.0, step = 0.1)]
122 pub z_near: f32,
123 #[reflect(min_value = 0.0, step = 0.1)]
126 pub z_far: f32,
127 #[reflect(step = 0.1)]
131 pub vertical_size: f32,
132}
133
134impl Default for OrthographicProjection {
135 fn default() -> Self {
136 Self {
137 z_near: 0.0,
138 z_far: 2048.0,
139 vertical_size: 5.0,
140 }
141 }
142}
143
144impl OrthographicProjection {
145 #[inline]
147 pub fn matrix(&self, frame_size: Vector2<f32>) -> Matrix4<f32> {
148 fn clamp_to_limit_signed(value: f32, limit: f32) -> f32 {
149 if value < 0.0 && -value < limit {
150 -limit
151 } else if value >= 0.0 && value < limit {
152 limit
153 } else {
154 value
155 }
156 }
157
158 let limit = 10.0 * f32::EPSILON;
159
160 let aspect = (frame_size.x / frame_size.y).max(limit);
161
162 let vertical_size = clamp_to_limit_signed(self.vertical_size, limit);
164 let horizontal_size = clamp_to_limit_signed(aspect * vertical_size, limit);
165
166 let z_near = self.z_far.min(self.z_near);
167 let mut z_far = self.z_far.max(self.z_near);
168
169 if z_far - z_near < limit {
171 z_far += limit;
172 }
173
174 let left = -horizontal_size;
175 let top = vertical_size;
176 let right = horizontal_size;
177 let bottom = -vertical_size;
178 Matrix4::new_orthographic(left, right, bottom, top, z_near, z_far)
179 }
180}
181
182#[derive(
189 Reflect,
190 Clone,
191 Debug,
192 PartialEq,
193 Visit,
194 AsRefStr,
195 EnumString,
196 VariantNames,
197 Serialize,
198 Deserialize,
199)]
200pub enum Projection {
201 Perspective(PerspectiveProjection),
203 Orthographic(OrthographicProjection),
205}
206
207uuid_provider!(Projection = "0eb5bec0-fc4e-4945-99b6-e6c5392ad971");
208
209impl Projection {
210 #[inline]
212 #[must_use]
213 pub fn with_z_near(mut self, z_near: f32) -> Self {
214 match self {
215 Projection::Perspective(ref mut v) => v.z_near = z_near,
216 Projection::Orthographic(ref mut v) => v.z_near = z_near,
217 }
218 self
219 }
220
221 #[inline]
223 #[must_use]
224 pub fn with_z_far(mut self, z_far: f32) -> Self {
225 match self {
226 Projection::Perspective(ref mut v) => v.z_far = z_far,
227 Projection::Orthographic(ref mut v) => v.z_far = z_far,
228 }
229 self
230 }
231
232 #[inline]
234 pub fn set_z_near(&mut self, z_near: f32) {
235 match self {
236 Projection::Perspective(v) => v.z_near = z_near,
237 Projection::Orthographic(v) => v.z_near = z_near,
238 }
239 }
240
241 #[inline]
243 pub fn set_z_far(&mut self, z_far: f32) {
244 match self {
245 Projection::Perspective(v) => v.z_far = z_far,
246 Projection::Orthographic(v) => v.z_far = z_far,
247 }
248 }
249
250 #[inline]
252 pub fn z_near(&self) -> f32 {
253 match self {
254 Projection::Perspective(v) => v.z_near,
255 Projection::Orthographic(v) => v.z_near,
256 }
257 }
258
259 #[inline]
261 pub fn z_far(&self) -> f32 {
262 match self {
263 Projection::Perspective(v) => v.z_far,
264 Projection::Orthographic(v) => v.z_far,
265 }
266 }
267
268 #[inline]
270 pub fn matrix(&self, frame_size: Vector2<f32>) -> Matrix4<f32> {
271 match self {
272 Projection::Perspective(v) => v.matrix(frame_size),
273 Projection::Orthographic(v) => v.matrix(frame_size),
274 }
275 }
276
277 #[inline]
279 pub fn is_perspective(&self) -> bool {
280 matches!(self, Projection::Perspective(_))
281 }
282
283 #[inline]
285 pub fn is_orthographic(&self) -> bool {
286 matches!(self, Projection::Orthographic(_))
287 }
288}
289
290impl Default for Projection {
291 fn default() -> Self {
292 Self::Perspective(PerspectiveProjection::default())
293 }
294}
295
296#[derive(Visit, Copy, Clone, PartialEq, Debug, Reflect, AsRefStr, EnumString, VariantNames)]
299pub enum Exposure {
300 Auto {
307 #[reflect(min_value = 0.0, step = 0.1)]
309 key_value: f32,
310 #[reflect(min_value = 0.0, step = 0.1)]
312 min_luminance: f32,
313 #[reflect(min_value = 0.0, step = 0.1)]
315 max_luminance: f32,
316 },
317
318 Manual(f32),
320}
321
322uuid_provider!(Exposure = "0e35ee3d-8baa-4b0c-b3dd-6c31a08c121e");
323
324impl Default for Exposure {
325 fn default() -> Self {
326 Self::Auto {
327 key_value: 0.01556,
328 min_luminance: 0.00778,
329 max_luminance: 64.0,
330 }
331 }
332}
333
334#[derive(Debug, Visit, Reflect, Clone, ComponentProvider)]
358pub struct Camera {
359 base: Base,
360
361 #[reflect(setter = "set_projection")]
362 projection: InheritableVariable<Projection>,
363
364 #[reflect(setter = "set_viewport")]
365 viewport: InheritableVariable<Rect<f32>>,
366
367 #[reflect(setter = "set_enabled")]
368 enabled: InheritableVariable<bool>,
369
370 #[reflect(setter = "set_skybox")]
371 sky_box: InheritableVariable<Option<SkyBox>>,
372
373 #[reflect(setter = "set_environment")]
374 environment: InheritableVariable<Option<TextureResource>>,
375
376 #[reflect(setter = "set_exposure")]
377 exposure: InheritableVariable<Exposure>,
378
379 #[reflect(setter = "set_color_grading_lut")]
380 color_grading_lut: InheritableVariable<Option<ColorGradingLut>>,
381
382 #[reflect(setter = "set_color_grading_enabled")]
383 color_grading_enabled: InheritableVariable<bool>,
384
385 #[visit(skip)]
386 #[reflect(hidden)]
387 view_matrix: Matrix4<f32>,
388
389 #[visit(skip)]
390 #[reflect(hidden)]
391 projection_matrix: Matrix4<f32>,
392}
393
394impl Deref for Camera {
395 type Target = Base;
396
397 fn deref(&self) -> &Self::Target {
398 &self.base
399 }
400}
401
402impl DerefMut for Camera {
403 fn deref_mut(&mut self) -> &mut Self::Target {
404 &mut self.base
405 }
406}
407
408impl Default for Camera {
409 fn default() -> Self {
410 CameraBuilder::new(BaseBuilder::new()).build_camera()
411 }
412}
413
414impl TypeUuidProvider for Camera {
415 fn type_uuid() -> Uuid {
416 uuid!("198d3aca-433c-4ce1-bb25-3190699b757f")
417 }
418}
419
420pub enum FitParameters {
426 Perspective {
428 position: Vector3<f32>,
430 distance: f32,
432 },
433 Orthographic {
435 position: Vector3<f32>,
437 vertical_size: f32,
439 },
440}
441
442impl Camera {
443 #[inline]
446 pub fn calculate_matrices(&mut self, frame_size: Vector2<f32>) {
447 let pos = self.base.global_position();
448 let look = self.base.look_vector();
449 let up = self.base.up_vector();
450
451 self.view_matrix = Matrix4::look_at_rh(&Point3::from(pos), &Point3::from(pos + look), &up);
452 self.projection_matrix = self.projection.matrix(frame_size);
453 }
454
455 pub fn set_viewport(&mut self, mut viewport: Rect<f32>) -> Rect<f32> {
463 viewport.position.x = viewport.position.x.clamp(0.0, 1.0);
464 viewport.position.y = viewport.position.y.clamp(0.0, 1.0);
465 viewport.size.x = viewport.size.x.clamp(0.0, 1.0);
466 viewport.size.y = viewport.size.y.clamp(0.0, 1.0);
467 self.viewport.set_value_and_mark_modified(viewport)
468 }
469
470 pub fn viewport(&self) -> Rect<f32> {
472 *self.viewport
473 }
474
475 #[inline]
485 pub fn viewport_pixels(&self, frame_size: Vector2<f32>) -> Rect<i32> {
486 Rect::new(
487 (self.viewport.x() * frame_size.x) as i32,
488 (self.viewport.y() * frame_size.y) as i32,
489 ((self.viewport.w() * frame_size.x) as i32).max(1),
490 ((self.viewport.h() * frame_size.y) as i32).max(1),
491 )
492 }
493
494 #[inline]
496 pub fn view_projection_matrix(&self) -> Matrix4<f32> {
497 self.projection_matrix * self.view_matrix
498 }
499
500 #[inline]
502 pub fn projection_matrix(&self) -> Matrix4<f32> {
503 self.projection_matrix
504 }
505
506 #[inline]
508 pub fn view_matrix(&self) -> Matrix4<f32> {
509 self.view_matrix
510 }
511
512 #[inline]
514 pub fn inv_view_matrix(&self) -> Option<Matrix4<f32>> {
515 self.view_matrix.try_inverse()
516 }
517
518 #[inline]
520 pub fn projection(&self) -> &Projection {
521 &self.projection
522 }
523
524 #[inline]
526 pub fn projection_value(&self) -> Projection {
527 (*self.projection).clone()
528 }
529
530 #[inline]
532 pub fn projection_mut(&mut self) -> &mut Projection {
533 self.projection.get_value_mut_and_mark_modified()
534 }
535
536 #[inline]
538 pub fn set_projection(&mut self, projection: Projection) -> Projection {
539 self.projection.set_value_and_mark_modified(projection)
540 }
541
542 #[inline]
544 pub fn is_enabled(&self) -> bool {
545 *self.enabled
546 }
547
548 #[inline]
552 pub fn set_enabled(&mut self, enabled: bool) -> bool {
553 self.enabled.set_value_and_mark_modified(enabled)
554 }
555
556 pub fn set_skybox(&mut self, skybox: Option<SkyBox>) -> Option<SkyBox> {
558 self.sky_box.set_value_and_mark_modified(skybox)
559 }
560
561 pub fn skybox_mut(&mut self) -> Option<&mut SkyBox> {
563 self.sky_box.get_value_mut_and_mark_modified().as_mut()
564 }
565
566 pub fn skybox_ref(&self) -> Option<&SkyBox> {
568 self.sky_box.as_ref()
569 }
570
571 pub fn replace_skybox(&mut self, new: Option<SkyBox>) -> Option<SkyBox> {
573 std::mem::replace(self.sky_box.get_value_mut_and_mark_modified(), new)
574 }
575
576 pub fn set_environment(
578 &mut self,
579 environment: Option<TextureResource>,
580 ) -> Option<TextureResource> {
581 self.environment.set_value_and_mark_modified(environment)
582 }
583
584 pub fn environment_mut(&mut self) -> Option<&mut TextureResource> {
586 self.environment.get_value_mut_and_mark_modified().as_mut()
587 }
588
589 pub fn environment_ref(&self) -> Option<&TextureResource> {
591 self.environment.as_ref()
592 }
593
594 pub fn environment_map(&self) -> Option<TextureResource> {
596 (*self.environment).clone()
597 }
598
599 pub fn make_ray(&self, screen_coord: Vector2<f32>, screen_size: Vector2<f32>) -> Ray {
601 let viewport = self.viewport_pixels(screen_size);
602 let nx = screen_coord.x / (viewport.w() as f32) * 2.0 - 1.0;
603 let ny = (viewport.h() as f32 - screen_coord.y) / (viewport.h() as f32) * 2.0 - 1.0;
606 let inv_view_proj = self
607 .view_projection_matrix()
608 .try_inverse()
609 .unwrap_or_default();
610 let near = inv_view_proj * Vector4::new(nx, ny, -1.0, 1.0);
611 let far = inv_view_proj * Vector4::new(nx, ny, 1.0, 1.0);
612 let begin = near.xyz().scale(1.0 / near.w);
613 let end = far.xyz().scale(1.0 / far.w);
614 Ray::from_two_points(begin, end)
615 }
616
617 #[inline]
625 #[must_use]
626 pub fn fit(&self, aabb: &AxisAlignedBoundingBox, aspect_ratio: f32) -> FitParameters {
627 let look_vector = self
628 .look_vector()
629 .try_normalize(f32::EPSILON)
630 .unwrap_or_default();
631
632 match self.projection.deref() {
633 Projection::Perspective(perspective) => {
634 let radius = aabb.half_extents().max();
635 let distance = radius / (perspective.fov * 0.5).sin();
636
637 FitParameters::Perspective {
638 position: aabb.center() - look_vector.scale(distance),
639 distance,
640 }
641 }
642 Projection::Orthographic(_) => {
643 let mut min_x = f32::MAX;
644 let mut min_y = f32::MAX;
645 let mut max_x = -f32::MAX;
646 let mut max_y = -f32::MAX;
647 let inv = self.global_transform().try_inverse().unwrap_or_default();
648 for point in aabb.corners() {
649 let local = inv.transform_point(&Point3::from(point));
650 if local.x < min_x {
651 min_x = local.x;
652 }
653 if local.y < min_y {
654 min_y = local.y;
655 }
656 if local.x > max_x {
657 max_x = local.x;
658 }
659 if local.y > max_y {
660 max_y = local.y;
661 }
662 }
663
664 FitParameters::Orthographic {
665 position: aabb.center() - look_vector.scale((aabb.max - aabb.min).norm()),
666 vertical_size: (max_y - min_y).max((max_x - min_x) * aspect_ratio),
667 }
668 }
669 }
670 }
671
672 #[inline]
674 pub fn frustum(&self) -> Frustum {
675 Frustum::from_view_projection_matrix(self.view_projection_matrix()).unwrap_or_default()
676 }
677
678 pub fn project(
680 &self,
681 world_pos: Vector3<f32>,
682 screen_size: Vector2<f32>,
683 ) -> Option<Vector2<f32>> {
684 let viewport = self.viewport_pixels(screen_size);
685 let proj = self.view_projection_matrix()
686 * Vector4::new(world_pos.x, world_pos.y, world_pos.z, 1.0);
687 if proj.w != 0.0 && proj.z >= 0.0 {
688 let k = (1.0 / proj.w) * 0.5;
689 Some(Vector2::new(
690 viewport.x() as f32 + viewport.w() as f32 * (proj.x * k + 0.5),
691 viewport.h() as f32
692 - (viewport.y() as f32 + viewport.h() as f32 * (proj.y * k + 0.5)),
693 ))
694 } else {
695 None
696 }
697 }
698
699 pub fn set_color_grading_lut(
701 &mut self,
702 lut: Option<ColorGradingLut>,
703 ) -> Option<ColorGradingLut> {
704 self.color_grading_lut.set_value_and_mark_modified(lut)
705 }
706
707 pub fn color_grading_lut(&self) -> Option<ColorGradingLut> {
709 (*self.color_grading_lut).clone()
710 }
711
712 pub fn color_grading_lut_ref(&self) -> Option<&ColorGradingLut> {
714 self.color_grading_lut.as_ref()
715 }
716
717 pub fn set_color_grading_enabled(&mut self, enable: bool) -> bool {
719 self.color_grading_enabled
720 .set_value_and_mark_modified(enable)
721 }
722
723 pub fn color_grading_enabled(&self) -> bool {
725 *self.color_grading_enabled
726 }
727
728 pub fn set_exposure(&mut self, exposure: Exposure) -> Exposure {
730 self.exposure.set_value_and_mark_modified(exposure)
731 }
732
733 pub fn exposure(&self) -> Exposure {
735 *self.exposure
736 }
737}
738
739impl ConstructorProvider<Node, Graph> for Camera {
740 fn constructor() -> NodeConstructor {
741 NodeConstructor::new::<Self>().with_variant("Camera", |_| {
742 CameraBuilder::new(BaseBuilder::new().with_name("Camera"))
743 .build_node()
744 .into()
745 })
746 }
747}
748
749impl NodeTrait for Camera {
750 #[inline]
752 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
753 self.base.local_bounding_box()
755 }
756
757 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
759 self.base.world_bounding_box()
760 }
761
762 fn id(&self) -> Uuid {
763 Self::type_uuid()
764 }
765
766 fn update(&mut self, context: &mut UpdateContext) {
767 self.calculate_matrices(context.frame_size);
768 }
769
770 fn debug_draw(&self, ctx: &mut SceneDrawingContext) {
771 let transform = self.global_transform.get();
772 ctx.draw_pyramid(
773 self.frustum().center(),
774 self.frustum().right_top_front_corner(),
775 self.frustum().left_top_front_corner(),
776 self.frustum().left_bottom_front_corner(),
777 self.frustum().right_bottom_front_corner(),
778 Color::GREEN,
779 transform,
780 );
781 }
782}
783
784#[derive(Debug)]
786pub enum ColorGradingLutCreationError {
787 NotEnoughData {
789 required: usize,
791 current: usize,
793 },
794
795 InvalidPixelFormat(TexturePixelKind),
797
798 Texture(LoadError),
800}
801
802impl Display for ColorGradingLutCreationError {
803 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
804 match self {
805 ColorGradingLutCreationError::NotEnoughData { required, current } => {
806 write!(
807 f,
808 "There is not enough data in provided \
809 texture to build LUT. Required: {required}, current: {current}.",
810 )
811 }
812 ColorGradingLutCreationError::InvalidPixelFormat(v) => {
813 write!(
814 f,
815 "Pixel format is not supported. It must be either RGB8 \
816 or RGBA8, but texture has {v:?} pixel format"
817 )
818 }
819 ColorGradingLutCreationError::Texture(v) => {
820 write!(f, "Texture load error: {v:?}")
821 }
822 }
823 }
824}
825
826#[derive(Visit, Clone, Default, PartialEq, Debug, Reflect, Eq)]
833pub struct ColorGradingLut {
834 unwrapped_lut: Option<TextureResource>,
835
836 #[visit(skip)]
837 #[reflect(hidden)]
838 lut: Option<TextureResource>,
839}
840
841uuid_provider!(ColorGradingLut = "bca9c90a-7cde-4960-8814-c132edfc9614");
842
843impl ColorGradingLut {
844 pub async fn new(unwrapped_lut: TextureResource) -> Result<Self, ColorGradingLutCreationError> {
873 match unwrapped_lut.await {
874 Ok(unwrapped_lut) => {
875 let data = unwrapped_lut.data_ref();
876
877 if data.pixel_kind() != TexturePixelKind::RGBA8
878 && data.pixel_kind() != TexturePixelKind::RGB8
879 {
880 return Err(ColorGradingLutCreationError::InvalidPixelFormat(
881 data.pixel_kind(),
882 ));
883 }
884
885 let bytes = data.data();
886
887 const RGBA8_SIZE: usize = 16 * 16 * 16 * 4;
888 const RGB8_SIZE: usize = 16 * 16 * 16 * 3;
889
890 if data.pixel_kind() == TexturePixelKind::RGBA8 {
891 if bytes.len() != RGBA8_SIZE {
892 return Err(ColorGradingLutCreationError::NotEnoughData {
893 required: RGBA8_SIZE,
894 current: bytes.len(),
895 });
896 }
897 } else if bytes.len() != RGB8_SIZE {
898 return Err(ColorGradingLutCreationError::NotEnoughData {
899 required: RGB8_SIZE,
900 current: bytes.len(),
901 });
902 }
903
904 let pixel_size = if data.pixel_kind() == TexturePixelKind::RGBA8 {
905 4
906 } else {
907 3
908 };
909
910 let mut lut_bytes = Vec::with_capacity(16 * 16 * 16 * 3);
911
912 for z in 0..16 {
913 for y in 0..16 {
914 for x in 0..16 {
915 let pixel_index = z * 16 + y * 16 * 16 + x;
916 let pixel_byte_pos = pixel_index * pixel_size;
917
918 lut_bytes.push(bytes[pixel_byte_pos]); lut_bytes.push(bytes[pixel_byte_pos + 1]); lut_bytes.push(bytes[pixel_byte_pos + 2]); }
922 }
923 }
924
925 let lut = TextureResource::from_bytes(
926 TextureKind::Volume {
927 width: 16,
928 height: 16,
929 depth: 16,
930 },
931 TexturePixelKind::RGB8,
932 lut_bytes,
933 ResourceKind::Embedded,
934 )
935 .unwrap();
936
937 let mut lut_ref = lut.data_ref();
938
939 lut_ref.set_s_wrap_mode(TextureWrapMode::ClampToEdge);
940 lut_ref.set_t_wrap_mode(TextureWrapMode::ClampToEdge);
941
942 drop(lut_ref);
943 drop(data);
944
945 Ok(Self {
946 lut: Some(lut),
947 unwrapped_lut: Some(unwrapped_lut),
948 })
949 }
950 Err(e) => Err(ColorGradingLutCreationError::Texture(e)),
951 }
952 }
953
954 pub fn unwrapped_lut(&self) -> TextureResource {
957 self.unwrapped_lut.clone().unwrap()
958 }
959
960 pub fn lut(&self) -> TextureResource {
962 self.lut.clone().unwrap()
963 }
964
965 pub fn lut_ref(&self) -> &TextureResource {
967 self.lut.as_ref().unwrap()
968 }
969}
970
971#[derive(Default)]
973pub enum SkyBoxKind {
974 #[default]
976 Builtin,
977 None,
979 Specific(SkyBox),
981}
982
983fn load_texture(data: &[u8], id: &str) -> TextureResource {
984 TextureResource::load_from_memory(
985 ResourceKind::External(id.into()),
986 data,
987 TextureImportOptions::default()
988 .with_compression(CompressionOptions::NoCompression)
989 .with_minification_filter(TextureMinificationFilter::Linear),
990 )
991 .ok()
992 .unwrap()
993}
994
995lazy_static! {
996 static ref BUILT_IN_SKYBOX_FRONT: BuiltInResource<Texture> =
997 BuiltInResource::new(embedded_data_source!("skybox/front.png"), |data| {
998 load_texture(data, "__BUILT_IN_SKYBOX_FRONT")
999 });
1000 static ref BUILT_IN_SKYBOX_BACK: BuiltInResource<Texture> =
1001 BuiltInResource::new(embedded_data_source!("skybox/back.png"), |data| {
1002 load_texture(data, "__BUILT_IN_SKYBOX_BACK")
1003 });
1004 static ref BUILT_IN_SKYBOX_TOP: BuiltInResource<Texture> =
1005 BuiltInResource::new(embedded_data_source!("skybox/top.png"), |data| {
1006 load_texture(data, "__BUILT_IN_SKYBOX_TOP")
1007 });
1008 static ref BUILT_IN_SKYBOX_BOTTOM: BuiltInResource<Texture> =
1009 BuiltInResource::new(embedded_data_source!("skybox/bottom.png"), |data| {
1010 load_texture(data, "__BUILT_IN_SKYBOX_BOTTOM")
1011 });
1012 static ref BUILT_IN_SKYBOX_LEFT: BuiltInResource<Texture> =
1013 BuiltInResource::new(embedded_data_source!("skybox/left.png"), |data| {
1014 load_texture(data, "__BUILT_IN_SKYBOX_LEFT")
1015 });
1016 static ref BUILT_IN_SKYBOX_RIGHT: BuiltInResource<Texture> =
1017 BuiltInResource::new(embedded_data_source!("skybox/right.png"), |data| {
1018 load_texture(data, "__BUILT_IN_SKYBOX_RIGHT")
1019 });
1020 static ref BUILT_IN_SKYBOX: SkyBox = SkyBoxKind::make_built_in_skybox();
1021}
1022
1023impl SkyBoxKind {
1024 fn make_built_in_skybox() -> SkyBox {
1025 let front = BUILT_IN_SKYBOX_FRONT.resource();
1026 let back = BUILT_IN_SKYBOX_BACK.resource();
1027 let top = BUILT_IN_SKYBOX_TOP.resource();
1028 let bottom = BUILT_IN_SKYBOX_BOTTOM.resource();
1029 let left = BUILT_IN_SKYBOX_LEFT.resource();
1030 let right = BUILT_IN_SKYBOX_RIGHT.resource();
1031
1032 SkyBoxBuilder {
1033 front: Some(front),
1034 back: Some(back),
1035 left: Some(left),
1036 right: Some(right),
1037 top: Some(top),
1038 bottom: Some(bottom),
1039 }
1040 .build()
1041 .unwrap()
1042 }
1043
1044 pub fn built_in_skybox() -> &'static SkyBox {
1046 &BUILT_IN_SKYBOX
1047 }
1048
1049 pub fn built_in_skybox_textures() -> [&'static BuiltInResource<Texture>; 6] {
1052 [
1053 &BUILT_IN_SKYBOX_FRONT,
1054 &BUILT_IN_SKYBOX_BACK,
1055 &BUILT_IN_SKYBOX_TOP,
1056 &BUILT_IN_SKYBOX_BOTTOM,
1057 &BUILT_IN_SKYBOX_LEFT,
1058 &BUILT_IN_SKYBOX_RIGHT,
1059 ]
1060 }
1061}
1062
1063pub struct CameraBuilder {
1066 base_builder: BaseBuilder,
1067 fov: f32,
1068 z_near: f32,
1069 z_far: f32,
1070 viewport: Rect<f32>,
1071 enabled: bool,
1072 skybox: SkyBoxKind,
1073 environment: Option<TextureResource>,
1074 exposure: Exposure,
1075 color_grading_lut: Option<ColorGradingLut>,
1076 color_grading_enabled: bool,
1077 projection: Projection,
1078}
1079
1080impl CameraBuilder {
1081 pub fn new(base_builder: BaseBuilder) -> Self {
1083 Self {
1084 enabled: true,
1085 base_builder,
1086 fov: 75.0f32.to_radians(),
1087 z_near: 0.025,
1088 z_far: 2048.0,
1089 viewport: Rect::new(0.0, 0.0, 1.0, 1.0),
1090 skybox: SkyBoxKind::Builtin,
1091 environment: None,
1092 exposure: Exposure::Manual(std::f32::consts::E),
1093 color_grading_lut: None,
1094 color_grading_enabled: false,
1095 projection: Projection::default(),
1096 }
1097 }
1098
1099 pub fn with_fov(mut self, fov: f32) -> Self {
1101 self.fov = fov;
1102 self
1103 }
1104
1105 pub fn with_z_near(mut self, z_near: f32) -> Self {
1107 self.z_near = z_near;
1108 self
1109 }
1110
1111 pub fn with_z_far(mut self, z_far: f32) -> Self {
1113 self.z_far = z_far;
1114 self
1115 }
1116
1117 pub fn with_viewport(mut self, viewport: Rect<f32>) -> Self {
1119 self.viewport = viewport;
1120 self
1121 }
1122
1123 pub fn enabled(mut self, enabled: bool) -> Self {
1125 self.enabled = enabled;
1126 self
1127 }
1128
1129 pub fn with_skybox(mut self, skybox: SkyBox) -> Self {
1131 self.skybox = SkyBoxKind::Specific(skybox);
1132 self
1133 }
1134
1135 pub fn with_specific_skybox(mut self, skybox_kind: SkyBoxKind) -> Self {
1137 self.skybox = skybox_kind;
1138 self
1139 }
1140
1141 pub fn with_environment(mut self, environment: TextureResource) -> Self {
1143 self.environment = Some(environment);
1144 self
1145 }
1146
1147 pub fn with_color_grading_lut(mut self, lut: ColorGradingLut) -> Self {
1149 self.color_grading_lut = Some(lut);
1150 self
1151 }
1152
1153 pub fn with_color_grading_enabled(mut self, enabled: bool) -> Self {
1155 self.color_grading_enabled = enabled;
1156 self
1157 }
1158
1159 pub fn with_exposure(mut self, exposure: Exposure) -> Self {
1161 self.exposure = exposure;
1162 self
1163 }
1164
1165 pub fn with_projection(mut self, projection: Projection) -> Self {
1167 self.projection = projection;
1168 self
1169 }
1170
1171 pub fn build_camera(self) -> Camera {
1173 Camera {
1174 enabled: self.enabled.into(),
1175 base: self.base_builder.build_base(),
1176 projection: self.projection.into(),
1177 viewport: self.viewport.into(),
1178 view_matrix: Matrix4::identity(),
1181 projection_matrix: Matrix4::identity(),
1182 sky_box: InheritableVariable::new_modified(match self.skybox {
1183 SkyBoxKind::Builtin => Some(SkyBoxKind::built_in_skybox().clone()),
1184 SkyBoxKind::None => None,
1185 SkyBoxKind::Specific(skybox) => Some(skybox),
1186 }),
1187 environment: self.environment.into(),
1188 exposure: self.exposure.into(),
1189 color_grading_lut: self.color_grading_lut.into(),
1190 color_grading_enabled: self.color_grading_enabled.into(),
1191 }
1192 }
1193
1194 pub fn build_node(self) -> Node {
1196 Node::new(self.build_camera())
1197 }
1198
1199 pub fn build(self, graph: &mut Graph) -> Handle<Node> {
1201 graph.add_node(self.build_node())
1202 }
1203}
1204
1205pub struct SkyBoxBuilder {
1207 pub front: Option<TextureResource>,
1209 pub back: Option<TextureResource>,
1211 pub left: Option<TextureResource>,
1213 pub right: Option<TextureResource>,
1215 pub top: Option<TextureResource>,
1217 pub bottom: Option<TextureResource>,
1219}
1220
1221impl SkyBoxBuilder {
1222 pub fn with_front(mut self, texture: TextureResource) -> Self {
1224 self.front = Some(texture);
1225 self
1226 }
1227
1228 pub fn with_back(mut self, texture: TextureResource) -> Self {
1230 self.back = Some(texture);
1231 self
1232 }
1233
1234 pub fn with_left(mut self, texture: TextureResource) -> Self {
1236 self.left = Some(texture);
1237 self
1238 }
1239
1240 pub fn with_right(mut self, texture: TextureResource) -> Self {
1242 self.right = Some(texture);
1243 self
1244 }
1245
1246 pub fn with_top(mut self, texture: TextureResource) -> Self {
1248 self.top = Some(texture);
1249 self
1250 }
1251
1252 pub fn with_bottom(mut self, texture: TextureResource) -> Self {
1254 self.bottom = Some(texture);
1255 self
1256 }
1257
1258 pub fn build(self) -> Result<SkyBox, SkyBoxError> {
1260 let mut skybox = SkyBox {
1261 left: self.left,
1262 right: self.right,
1263 top: self.top,
1264 bottom: self.bottom,
1265 front: self.front,
1266 back: self.back,
1267 cubemap: None,
1268 };
1269
1270 skybox.create_cubemap()?;
1271
1272 Ok(skybox)
1273 }
1274}
1275
1276#[derive(Debug, Clone, Default, PartialEq, Reflect, Visit, Eq)]
1282pub struct SkyBox {
1283 #[reflect(setter = "set_front")]
1285 pub(crate) front: Option<TextureResource>,
1286
1287 #[reflect(setter = "set_back")]
1289 pub(crate) back: Option<TextureResource>,
1290
1291 #[reflect(setter = "set_left")]
1293 pub(crate) left: Option<TextureResource>,
1294
1295 #[reflect(setter = "set_right")]
1297 pub(crate) right: Option<TextureResource>,
1298
1299 #[reflect(setter = "set_top")]
1301 pub(crate) top: Option<TextureResource>,
1302
1303 #[reflect(setter = "set_bottom")]
1305 pub(crate) bottom: Option<TextureResource>,
1306
1307 #[reflect(hidden)]
1309 #[visit(skip)]
1310 pub(crate) cubemap: Option<TextureResource>,
1311}
1312
1313uuid_provider!(SkyBox = "45f359f1-e26f-4ace-81df-097f63474c72");
1314
1315#[derive(Debug)]
1317pub enum SkyBoxError {
1318 UnsupportedTextureKind(TextureKind),
1320 UnableToBuildCubeMap,
1322 NonSquareTexture {
1324 index: usize,
1326 width: u32,
1328 height: u32,
1330 },
1331 DifferentTexture {
1333 expected_width: u32,
1335 expected_height: u32,
1337 expected_pixel_kind: TexturePixelKind,
1339 index: usize,
1341 actual_width: u32,
1343 actual_height: u32,
1345 actual_pixel_kind: TexturePixelKind,
1347 },
1348 TextureIsNotReady {
1350 index: usize,
1352 },
1353}
1354
1355impl SkyBox {
1356 pub fn cubemap(&self) -> Option<TextureResource> {
1358 self.cubemap.clone()
1359 }
1360
1361 pub fn cubemap_ref(&self) -> Option<&TextureResource> {
1363 self.cubemap.as_ref()
1364 }
1365
1366 pub fn validate(&self) -> Result<(), SkyBoxError> {
1371 struct TextureInfo {
1372 pixel_kind: TexturePixelKind,
1373 width: u32,
1374 height: u32,
1375 }
1376
1377 let mut first_info: Option<TextureInfo> = None;
1378
1379 for (index, texture) in self.textures().iter().enumerate() {
1380 if let Some(texture) = texture {
1381 if let Some(texture) = texture.state().data() {
1382 if let TextureKind::Rectangle { width, height } = texture.kind() {
1383 if width != height {
1384 return Err(SkyBoxError::NonSquareTexture {
1385 index,
1386 width,
1387 height,
1388 });
1389 }
1390
1391 if let Some(first_info) = first_info.as_mut() {
1392 if first_info.width != width
1393 || first_info.height != height
1394 || first_info.pixel_kind != texture.pixel_kind()
1395 {
1396 return Err(SkyBoxError::DifferentTexture {
1397 expected_width: first_info.width,
1398 expected_height: first_info.height,
1399 expected_pixel_kind: first_info.pixel_kind,
1400 index,
1401 actual_width: width,
1402 actual_height: height,
1403 actual_pixel_kind: texture.pixel_kind(),
1404 });
1405 }
1406 } else {
1407 first_info = Some(TextureInfo {
1408 pixel_kind: texture.pixel_kind(),
1409 width,
1410 height,
1411 });
1412 }
1413 }
1414 } else {
1415 return Err(SkyBoxError::TextureIsNotReady { index });
1416 }
1417 }
1418 }
1419
1420 Ok(())
1421 }
1422
1423 pub fn create_cubemap(&mut self) -> Result<(), SkyBoxError> {
1429 self.validate()?;
1430
1431 let (kind, pixel_kind, bytes_per_face) =
1432 self.textures().iter().find(|face| face.is_some()).map_or(
1433 (
1434 TextureKind::Rectangle {
1435 width: 1,
1436 height: 1,
1437 },
1438 TexturePixelKind::R8,
1439 1,
1440 ),
1441 |face| {
1442 let face = face.clone().unwrap();
1443 let data = face.data_ref();
1444
1445 (data.kind(), data.pixel_kind(), data.mip_level_data(0).len())
1446 },
1447 );
1448
1449 let (width, height) = match kind {
1450 TextureKind::Rectangle { width, height } => (width, height),
1451 _ => return Err(SkyBoxError::UnsupportedTextureKind(kind)),
1452 };
1453
1454 let mut data = Vec::<u8>::with_capacity(bytes_per_face * 6);
1455 for face in self.textures().iter() {
1456 if let Some(f) = face.clone() {
1457 data.extend(f.data_ref().mip_level_data(0));
1458 } else {
1459 let black_face_data = vec![0; bytes_per_face];
1460 data.extend(black_face_data);
1461 }
1462 }
1463
1464 let cubemap = TextureResource::from_bytes(
1465 TextureKind::Cube { width, height },
1466 pixel_kind,
1467 data,
1468 ResourceKind::Embedded,
1469 )
1470 .ok_or(SkyBoxError::UnableToBuildCubeMap)?;
1471
1472 let mut cubemap_ref = cubemap.data_ref();
1473 cubemap_ref.set_s_wrap_mode(TextureWrapMode::ClampToEdge);
1474 cubemap_ref.set_t_wrap_mode(TextureWrapMode::ClampToEdge);
1475 drop(cubemap_ref);
1476
1477 self.cubemap = Some(cubemap);
1478
1479 Ok(())
1480 }
1481
1482 pub fn textures(&self) -> [Option<TextureResource>; 6] {
1491 [
1492 self.left.clone(),
1493 self.right.clone(),
1494 self.top.clone(),
1495 self.bottom.clone(),
1496 self.front.clone(),
1497 self.back.clone(),
1498 ]
1499 }
1500
1501 pub fn set_left(&mut self, texture: Option<TextureResource>) -> Option<TextureResource> {
1503 let prev = std::mem::replace(&mut self.left, texture);
1504 Log::verify(self.create_cubemap());
1505 prev
1506 }
1507
1508 pub fn left(&self) -> Option<TextureResource> {
1514 self.left.clone()
1515 }
1516
1517 pub fn set_right(&mut self, texture: Option<TextureResource>) -> Option<TextureResource> {
1519 let prev = std::mem::replace(&mut self.right, texture);
1520 Log::verify(self.create_cubemap());
1521 prev
1522 }
1523
1524 pub fn right(&self) -> Option<TextureResource> {
1530 self.right.clone()
1531 }
1532
1533 pub fn set_top(&mut self, texture: Option<TextureResource>) -> Option<TextureResource> {
1535 let prev = std::mem::replace(&mut self.top, texture);
1536 Log::verify(self.create_cubemap());
1537 prev
1538 }
1539
1540 pub fn top(&self) -> Option<TextureResource> {
1546 self.top.clone()
1547 }
1548
1549 pub fn set_bottom(&mut self, texture: Option<TextureResource>) -> Option<TextureResource> {
1551 let prev = std::mem::replace(&mut self.bottom, texture);
1552 Log::verify(self.create_cubemap());
1553 prev
1554 }
1555
1556 pub fn bottom(&self) -> Option<TextureResource> {
1562 self.bottom.clone()
1563 }
1564
1565 pub fn set_front(&mut self, texture: Option<TextureResource>) -> Option<TextureResource> {
1567 let prev = std::mem::replace(&mut self.front, texture);
1568 Log::verify(self.create_cubemap());
1569 prev
1570 }
1571
1572 pub fn front(&self) -> Option<TextureResource> {
1578 self.front.clone()
1579 }
1580
1581 pub fn set_back(&mut self, texture: Option<TextureResource>) -> Option<TextureResource> {
1583 let prev = std::mem::replace(&mut self.back, texture);
1584 Log::verify(self.create_cubemap());
1585 prev
1586 }
1587
1588 pub fn back(&self) -> Option<TextureResource> {
1594 self.back.clone()
1595 }
1596}