use bevy::ecs::system::lifetimeless::SRes;
use bevy::ecs::system::SystemParamItem;
use bevy::pbr::{MaterialPipeline, SpecializedMaterial};
use bevy::prelude::*;
use bevy::reflect::TypeUuid;
use bevy::render::render_asset::{PrepareAssetError, RenderAsset};
use bevy::render::render_resource::std140::{AsStd140, Std140};
use bevy::render::render_resource::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingType, Buffer, BufferBindingType, BufferInitDescriptor, BufferSize,
BufferUsages, CompareFunction, RenderPipelineDescriptor, ShaderStages,
};
use bevy::render::renderer::RenderDevice;
pub const SKY_VERTEX_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 17795653402514319180);
pub const SKY_FRAGMENT_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 13775252721647315361);
#[derive(Debug, TypeUuid, Clone, AsStd140)]
#[uuid = "a57878c4-569e-4511-be7c-b0e5b2c983e2"]
pub struct AtmosphereMat {
pub ray_origin: Vec3,
pub sun_position: Vec3,
pub sun_intensity: f32,
pub planet_radius: f32,
pub atmosphere_radius: f32,
pub rayleigh_coefficient: Vec3,
pub rayleigh_scale_height: f32,
pub mie_coefficient: f32,
pub mie_scale_height: f32,
pub mie_direction: f32,
}
#[allow(dead_code)]
impl AtmosphereMat {
pub fn set_ray_origin(&mut self, ray_origin: Vec3) {
self.ray_origin = ray_origin;
}
pub fn set_sun_position(&mut self, sun_position: Vec3) {
self.sun_position = sun_position;
}
pub fn set_sun_intensity(&mut self, sun_intensity: f32) {
self.sun_intensity = sun_intensity;
}
pub fn set_planet_radius(&mut self, planet_radius: f32) {
self.planet_radius = planet_radius;
}
pub fn set_atmosphere_radius(&mut self, atmosphere_radius: f32) {
self.atmosphere_radius = atmosphere_radius;
}
pub fn set_rayleigh_scattering_coefficient(&mut self, coefficient: Vec3) {
self.rayleigh_coefficient.x = coefficient.x;
self.rayleigh_coefficient.y = coefficient.y;
self.rayleigh_coefficient.z = coefficient.z;
}
pub fn set_rayleigh_scale_height(&mut self, scale: f32) {
self.rayleigh_scale_height = scale;
}
pub fn set_mie_scattering_coefficient(&mut self, coefficient: f32) {
self.mie_coefficient = coefficient;
}
pub fn set_mie_scale_height(&mut self, scale: f32) {
self.mie_scale_height = scale;
}
pub fn set_mie_scattering_direction(&mut self, direction: f32) {
self.mie_direction = direction;
}
}
impl Default for AtmosphereMat {
fn default() -> Self {
Self {
ray_origin: Vec3::new(0.0, 6372e3, 0.0),
sun_position: Vec3::new(0.0, 1.0, 1.0),
sun_intensity: 22.0,
planet_radius: 6371e3,
atmosphere_radius: 6471e3,
rayleigh_coefficient: Vec3::new(5.5e-6, 13.0e-6, 22.4e-6),
rayleigh_scale_height: 8e3,
mie_coefficient: 21e-6,
mie_scale_height: 1.2e3,
mie_direction: 0.758,
}
}
}
#[derive(Clone)]
pub struct GpuAtmosphereMat {
_buffer: Buffer,
bind_group: BindGroup,
}
impl RenderAsset for AtmosphereMat {
type ExtractedAsset = AtmosphereMat;
type PreparedAsset = GpuAtmosphereMat;
type Param = (SRes<RenderDevice>, SRes<MaterialPipeline<Self>>);
fn extract_asset(&self) -> Self {
self.clone()
}
fn prepare_asset(
extracted_asset: Self,
(render_device, material_pipeline): &mut SystemParamItem<Self::Param>,
) -> Result<Self::PreparedAsset, PrepareAssetError<Self>> {
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
contents: extracted_asset.as_std140().as_bytes(),
label: None,
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
});
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
}],
label: None,
layout: &material_pipeline.material_layout,
});
Ok(Self::PreparedAsset {
_buffer: buffer,
bind_group,
})
}
}
impl SpecializedMaterial for AtmosphereMat {
type Key = ();
fn key(_: &<AtmosphereMat as RenderAsset>::PreparedAsset) -> Self::Key {}
fn specialize(_: Self::Key, descriptor: &mut RenderPipelineDescriptor) {
descriptor.vertex.entry_point = "main".into();
descriptor.fragment.as_mut().unwrap().entry_point = "main".into();
if let Some(depth_stencil_state) = &mut descriptor.depth_stencil {
depth_stencil_state.depth_compare = CompareFunction::GreaterEqual;
depth_stencil_state.depth_write_enabled = false;
}
}
fn vertex_shader(_: &AssetServer) -> Option<Handle<Shader>> {
Some(SKY_VERTEX_SHADER_HANDLE.typed())
}
fn fragment_shader(_: &AssetServer) -> Option<Handle<Shader>> {
Some(SKY_FRAGMENT_SHADER_HANDLE.typed())
}
fn bind_group(render_asset: &<Self as RenderAsset>::PreparedAsset) -> &BindGroup {
&render_asset.bind_group
}
fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout {
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: BufferSize::new(AtmosphereMat::std140_size_static() as u64),
},
count: None,
}],
label: None,
})
}
}