use bevy::render::camera::{ActiveCameras, Camera, CameraPlugin};
use bevy::{pbr::NotShadowCaster, prelude::*};
use std::ops::Deref;
mod material;
pub use material::AtmosphereMat;
use material::{SKY_FRAGMENT_SHADER_HANDLE, SKY_VERTEX_SHADER_HANDLE};
use naga::ShaderStage;
const SKY_VERTEX_SHADER: &str = include_str!("shaders/sky.vert");
const SKY_FRAGMENT_SHADER: &str = include_str!("shaders/sky.frag");
pub struct AtmospherePlugin {
pub dynamic: bool,
pub sky_radius: f32,
}
impl Default for AtmospherePlugin {
fn default() -> Self {
Self {
dynamic: false,
sky_radius: 10.0,
}
}
}
pub struct SkyRadius(f32);
impl Plugin for AtmospherePlugin {
fn build(&self, app: &mut App) {
let mut shaders = app.world.get_resource_mut::<Assets<Shader>>().unwrap();
shaders.set_untracked(
SKY_VERTEX_SHADER_HANDLE,
Shader::from_glsl(SKY_VERTEX_SHADER, ShaderStage::Vertex),
);
shaders.set_untracked(
SKY_FRAGMENT_SHADER_HANDLE,
Shader::from_glsl(SKY_FRAGMENT_SHADER, ShaderStage::Fragment),
);
app.add_plugin(MaterialPlugin::<AtmosphereMat>::default());
app.add_startup_system(atmosphere_add_sky_sphere);
app.add_system_to_stage(
CoreStage::Last, atmosphere_sky_follow,
);
if self.dynamic {
app.add_system(atmosphere_dynamic_sky);
}
app.insert_resource(SkyRadius(self.sky_radius));
}
}
fn atmosphere_add_sky_sphere(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut sky_materials: ResMut<Assets<AtmosphereMat>>,
sky_radius: Res<SkyRadius>,
config: Option<Res<AtmosphereMat>>,
) {
let sky_material = match config {
None => AtmosphereMat::default(),
Some(c) => c.deref().clone(),
};
let sky_material = sky_materials.add(sky_material);
commands
.spawn_bundle(MaterialMeshBundle {
mesh: meshes.add(Mesh::from(shape::Icosphere {
radius: -sky_radius.0,
subdivisions: 2,
})),
material: sky_material,
..Default::default()
})
.insert(NotShadowCaster)
.insert(Name::new("Sky Sphere"));
}
fn atmosphere_sky_follow(
camera_transform_query: Query<&GlobalTransform, (With<Camera>, Without<Handle<AtmosphereMat>>)>,
mut sky_transform_query: Query<&mut GlobalTransform, With<Handle<AtmosphereMat>>>,
active_cameras: Res<ActiveCameras>,
) {
if let Some(camera_3d) = active_cameras.get(CameraPlugin::CAMERA_3D) {
if let Some(camera_3d_entity) = camera_3d.entity {
if let Ok(camera_transform) = camera_transform_query.get(camera_3d_entity) {
if let Some(mut sky_transform) = sky_transform_query.iter_mut().next() {
sky_transform.translation = camera_transform.translation;
}
}
}
}
}
fn atmosphere_dynamic_sky(
config: Res<AtmosphereMat>,
sky_mat_query: Query<&Handle<AtmosphereMat>>,
mut sky_materials: ResMut<Assets<AtmosphereMat>>,
) {
if config.is_changed() {
if let Some(sky_mat_handle) = sky_mat_query.iter().next() {
if let Some(sky_mat) = sky_materials.get_mut(sky_mat_handle) {
*sky_mat = config.deref().clone();
}
}
}
}