use crate::core::*;
use crate::renderer::*;
use std::sync::Arc;
pub struct Skybox {
context: Context,
vertex_buffer: VertexBuffer,
material: SkyboxMaterial,
}
impl Skybox {
pub fn new(
context: &Context,
right: &CpuTexture,
left: &CpuTexture,
top: &CpuTexture,
bottom: &CpuTexture,
front: &CpuTexture,
back: &CpuTexture,
) -> Self {
let convert = |cpu_texture: &CpuTexture| match &cpu_texture.data {
TextureData::RgbU8(_) | TextureData::RgbaU8(_) => {
let mut cpu_texture = cpu_texture.clone();
cpu_texture.data.to_linear_srgb();
Some(cpu_texture)
}
_ => None,
};
Self::new_with_texture(
context,
Arc::new(TextureCubeMap::new(
context,
convert(right).as_ref().unwrap_or(right),
convert(left).as_ref().unwrap_or(left),
convert(top).as_ref().unwrap_or(top),
convert(bottom).as_ref().unwrap_or(bottom),
convert(front).as_ref().unwrap_or(front),
convert(back).as_ref().unwrap_or(back),
)),
)
}
pub fn new_from_equirectangular(context: &Context, cpu_texture: &CpuTexture) -> Self {
let texture = match cpu_texture.data {
TextureData::RgbaU8(_) | TextureData::RgbU8(_) => {
let mut cpu_texture = cpu_texture.clone();
cpu_texture.data.to_linear_srgb();
TextureCubeMap::new_from_equirectangular::<u8>(context, &cpu_texture)
}
TextureData::RgU8(_) | TextureData::RU8(_) => {
TextureCubeMap::new_from_equirectangular::<u8>(context, cpu_texture)
}
TextureData::RgbaF16(_)
| TextureData::RgbF16(_)
| TextureData::RgF16(_)
| TextureData::RF16(_) => {
TextureCubeMap::new_from_equirectangular::<f16>(context, cpu_texture)
}
TextureData::RgbaF32(_)
| TextureData::RgbF32(_)
| TextureData::RgF32(_)
| TextureData::RF32(_) => {
TextureCubeMap::new_from_equirectangular::<f32>(context, cpu_texture)
}
};
Self::new_with_texture(context, Arc::new(texture))
}
pub fn new_with_texture(context: &Context, texture: Arc<TextureCubeMap>) -> Self {
let vertex_buffer = VertexBuffer::new_with_data(
context,
&[
vec3(1.0, 1.0, -1.0),
vec3(-1.0, 1.0, -1.0),
vec3(1.0, 1.0, 1.0),
vec3(-1.0, 1.0, 1.0),
vec3(1.0, 1.0, 1.0),
vec3(-1.0, 1.0, -1.0),
vec3(-1.0, -1.0, -1.0),
vec3(1.0, -1.0, -1.0),
vec3(1.0, -1.0, 1.0),
vec3(1.0, -1.0, 1.0),
vec3(-1.0, -1.0, 1.0),
vec3(-1.0, -1.0, -1.0),
vec3(1.0, -1.0, -1.0),
vec3(-1.0, -1.0, -1.0),
vec3(1.0, 1.0, -1.0),
vec3(-1.0, 1.0, -1.0),
vec3(1.0, 1.0, -1.0),
vec3(-1.0, -1.0, -1.0),
vec3(-1.0, -1.0, 1.0),
vec3(1.0, -1.0, 1.0),
vec3(1.0, 1.0, 1.0),
vec3(1.0, 1.0, 1.0),
vec3(-1.0, 1.0, 1.0),
vec3(-1.0, -1.0, 1.0),
vec3(1.0, -1.0, -1.0),
vec3(1.0, 1.0, -1.0),
vec3(1.0, 1.0, 1.0),
vec3(1.0, 1.0, 1.0),
vec3(1.0, -1.0, 1.0),
vec3(1.0, -1.0, -1.0),
vec3(-1.0, 1.0, -1.0),
vec3(-1.0, -1.0, -1.0),
vec3(-1.0, 1.0, 1.0),
vec3(-1.0, -1.0, 1.0),
vec3(-1.0, 1.0, 1.0),
vec3(-1.0, -1.0, -1.0),
],
);
Skybox {
context: context.clone(),
vertex_buffer,
material: SkyboxMaterial { texture },
}
}
pub fn texture(&self) -> &Arc<TextureCubeMap> {
&self.material.texture
}
}
impl<'a> IntoIterator for &'a Skybox {
type Item = &'a dyn Object;
type IntoIter = std::iter::Once<&'a dyn Object>;
fn into_iter(self) -> Self::IntoIter {
std::iter::once(self)
}
}
impl Geometry for Skybox {
fn draw(
&self,
camera: &Camera,
program: &Program,
render_states: RenderStates,
_attributes: FragmentAttributes,
) {
program.use_uniform("view", camera.view());
program.use_uniform("projection", camera.projection());
program.use_vertex_attribute("position", &self.vertex_buffer);
program.draw_arrays(render_states, camera.viewport(), 36);
}
fn vertex_shader_source(&self, _required_attributes: FragmentAttributes) -> String {
include_str!("shaders/skybox.vert").to_owned()
}
fn id(&self, _required_attributes: FragmentAttributes) -> u16 {
0b1u16 << 15 | 0b1u16
}
fn aabb(&self) -> AxisAlignedBoundingBox {
AxisAlignedBoundingBox::INFINITE
}
fn render_with_material(
&self,
material: &dyn Material,
camera: &Camera,
lights: &[&dyn Light],
) {
render_with_material(&self.context, camera, &self, material, lights)
}
fn render_with_effect(
&self,
material: &dyn Effect,
camera: &Camera,
lights: &[&dyn Light],
color_texture: Option<ColorTexture>,
depth_texture: Option<DepthTexture>,
) {
render_with_effect(
&self.context,
camera,
self,
material,
lights,
color_texture,
depth_texture,
)
}
}
impl Object for Skybox {
fn render(&self, camera: &Camera, lights: &[&dyn Light]) {
render_with_material(&self.context, camera, self, &self.material, lights)
}
fn material_type(&self) -> MaterialType {
MaterialType::Opaque
}
}