three_d/renderer/object/
skybox.rs

1use crate::core::*;
2use crate::renderer::*;
3use std::sync::Arc;
4
5///
6/// An illusion of a sky.
7///
8pub struct Skybox {
9    context: Context,
10    vertex_buffer: VertexBuffer<Vec3>,
11    material: SkyboxMaterial,
12}
13
14impl Skybox {
15    ///
16    /// Creates a new skybox with the given [CpuTexture]s placed at the indicated sides of the skybox.
17    /// All of the cpu textures must contain data with the same [TextureDataType].
18    ///
19    pub fn new(
20        context: &Context,
21        right: &CpuTexture,
22        left: &CpuTexture,
23        top: &CpuTexture,
24        bottom: &CpuTexture,
25        front: &CpuTexture,
26        back: &CpuTexture,
27    ) -> Self {
28        let convert = |cpu_texture: &CpuTexture| match &cpu_texture.data {
29            TextureData::RgbU8(_) | TextureData::RgbaU8(_) => {
30                let mut cpu_texture = cpu_texture.clone();
31                cpu_texture.data.to_linear_srgb();
32                Some(cpu_texture)
33            }
34            _ => None,
35        };
36        Self::new_with_texture(
37            context,
38            Arc::new(TextureCubeMap::new(
39                context,
40                convert(right).as_ref().unwrap_or(right),
41                convert(left).as_ref().unwrap_or(left),
42                convert(top).as_ref().unwrap_or(top),
43                convert(bottom).as_ref().unwrap_or(bottom),
44                convert(front).as_ref().unwrap_or(front),
45                convert(back).as_ref().unwrap_or(back),
46            )),
47        )
48    }
49
50    ///
51    /// Creates a new skybox with a cube texture generated from the equirectangular texture given as input.
52    ///
53    pub fn new_from_equirectangular(context: &Context, cpu_texture: &CpuTexture) -> Self {
54        let texture = match cpu_texture.data {
55            TextureData::RgbaU8(_) | TextureData::RgbU8(_) => {
56                let mut cpu_texture = cpu_texture.clone();
57                cpu_texture.data.to_linear_srgb();
58                TextureCubeMap::new_from_equirectangular::<u8>(context, &cpu_texture)
59            }
60            TextureData::RgU8(_) | TextureData::RU8(_) => {
61                TextureCubeMap::new_from_equirectangular::<u8>(context, cpu_texture)
62            }
63            TextureData::RgbaF16(_)
64            | TextureData::RgbF16(_)
65            | TextureData::RgF16(_)
66            | TextureData::RF16(_) => {
67                TextureCubeMap::new_from_equirectangular::<f16>(context, cpu_texture)
68            }
69            TextureData::RgbaF32(_)
70            | TextureData::RgbF32(_)
71            | TextureData::RgF32(_)
72            | TextureData::RF32(_) => {
73                TextureCubeMap::new_from_equirectangular::<f32>(context, cpu_texture)
74            }
75        };
76
77        Self::new_with_texture(context, Arc::new(texture))
78    }
79
80    ///
81    /// Creates a new skybox with the given [TextureCubeMap].
82    /// The colors are assumed to be in linear sRGB (`RgbU8`), linear sRGB with an alpha channel (`RgbaU8`) or HDR color space.
83    ///
84    pub fn new_with_texture(context: &Context, texture: Arc<TextureCubeMap>) -> Self {
85        let vertex_buffer = VertexBuffer::new_with_data(
86            context,
87            &[
88                vec3(1.0, 1.0, -1.0),
89                vec3(-1.0, 1.0, -1.0),
90                vec3(1.0, 1.0, 1.0),
91                vec3(-1.0, 1.0, 1.0),
92                vec3(1.0, 1.0, 1.0),
93                vec3(-1.0, 1.0, -1.0),
94                vec3(-1.0, -1.0, -1.0),
95                vec3(1.0, -1.0, -1.0),
96                vec3(1.0, -1.0, 1.0),
97                vec3(1.0, -1.0, 1.0),
98                vec3(-1.0, -1.0, 1.0),
99                vec3(-1.0, -1.0, -1.0),
100                vec3(1.0, -1.0, -1.0),
101                vec3(-1.0, -1.0, -1.0),
102                vec3(1.0, 1.0, -1.0),
103                vec3(-1.0, 1.0, -1.0),
104                vec3(1.0, 1.0, -1.0),
105                vec3(-1.0, -1.0, -1.0),
106                vec3(-1.0, -1.0, 1.0),
107                vec3(1.0, -1.0, 1.0),
108                vec3(1.0, 1.0, 1.0),
109                vec3(1.0, 1.0, 1.0),
110                vec3(-1.0, 1.0, 1.0),
111                vec3(-1.0, -1.0, 1.0),
112                vec3(1.0, -1.0, -1.0),
113                vec3(1.0, 1.0, -1.0),
114                vec3(1.0, 1.0, 1.0),
115                vec3(1.0, 1.0, 1.0),
116                vec3(1.0, -1.0, 1.0),
117                vec3(1.0, -1.0, -1.0),
118                vec3(-1.0, 1.0, -1.0),
119                vec3(-1.0, -1.0, -1.0),
120                vec3(-1.0, 1.0, 1.0),
121                vec3(-1.0, -1.0, 1.0),
122                vec3(-1.0, 1.0, 1.0),
123                vec3(-1.0, -1.0, -1.0),
124            ],
125        );
126
127        Skybox {
128            context: context.clone(),
129            vertex_buffer,
130            material: SkyboxMaterial { texture },
131        }
132    }
133
134    ///
135    /// Returns a reference to the cube map texture
136    ///
137    pub fn texture(&self) -> &Arc<TextureCubeMap> {
138        &self.material.texture
139    }
140}
141
142impl<'a> IntoIterator for &'a Skybox {
143    type Item = &'a dyn Object;
144    type IntoIter = std::iter::Once<&'a dyn Object>;
145
146    fn into_iter(self) -> Self::IntoIter {
147        std::iter::once(self)
148    }
149}
150
151impl Geometry for Skybox {
152    fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates) {
153        program.use_uniform("view", viewer.view());
154        program.use_uniform("projection", viewer.projection());
155        program.use_vertex_attribute("position", &self.vertex_buffer);
156        program.draw_arrays(render_states, viewer.viewport(), 36);
157    }
158
159    fn vertex_shader_source(&self) -> String {
160        include_str!("shaders/skybox.vert").to_owned()
161    }
162
163    fn id(&self) -> GeometryId {
164        GeometryId::Skybox
165    }
166
167    fn aabb(&self) -> AxisAlignedBoundingBox {
168        AxisAlignedBoundingBox::INFINITE
169    }
170
171    fn render_with_material(
172        &self,
173        material: &dyn Material,
174        viewer: &dyn Viewer,
175        lights: &[&dyn Light],
176    ) {
177        render_with_material(&self.context, viewer, &self, material, lights)
178    }
179
180    fn render_with_effect(
181        &self,
182        material: &dyn Effect,
183        viewer: &dyn Viewer,
184        lights: &[&dyn Light],
185        color_texture: Option<ColorTexture>,
186        depth_texture: Option<DepthTexture>,
187    ) {
188        render_with_effect(
189            &self.context,
190            viewer,
191            self,
192            material,
193            lights,
194            color_texture,
195            depth_texture,
196        )
197    }
198}
199
200impl Object for Skybox {
201    fn render(&self, viewer: &dyn Viewer, lights: &[&dyn Light]) {
202        render_with_material(&self.context, viewer, self, &self.material, lights)
203    }
204
205    fn material_type(&self) -> MaterialType {
206        MaterialType::Opaque
207    }
208}