#![allow(dead_code)]
use std::sync::Arc;
use ambient_core::{asset_cache, mesh, transform::translation};
use ambient_ecs::{components, query, Entity, SystemGroup};
use ambient_gpu::{
gpu::GpuKey,
shader_module::{BindGroupDesc, Shader, ShaderModule},
typed_buffer::TypedBuffer,
};
use ambient_meshes::QuadMeshKey;
use ambient_renderer::{self, *};
use ambient_std::{asset_cache::*, cb, friendly_id, include_file};
use glam::*;
use noise::OpenSimplex;
use wgpu::{BindGroup, BufferUsages};
use self::tree::*;
mod tree;
pub use ambient_ecs::generated::components::core::rendering::sky;
pub const ATMOSPHERIC_SCATTERING_SOURCE: &str = include_str!("atmospheric_scattering.wgsl");
components!("rendering", {
cloud_state: CloudState,
});
#[derive(Debug, Clone)]
pub struct Clouds {}
#[derive(Clone)]
pub struct CloudState {
tree: Octree,
}
const MAX_DEPTH: u32 = 20;
const DENSITY_THRESHOLD: f32 = 0.2;
const VOXEL_SIZE: f32 = 0.05;
impl CloudState {
pub fn new(half_size: f32) -> Self {
let generator = OpenSimplex::new(); let tree = OctreeInfo {
max_depth: MAX_DEPTH,
half_size,
generator: Arc::new(generator),
..OctreeInfo::default()
}
.build();
Self { tree }
}
}
pub fn systems() -> SystemGroup {
SystemGroup::new(
"sky",
vec![
query(sky())
.excl(renderer_shader())
.to_system(|q, world, qs, _| {
let assets = world.resource(asset_cache()).clone();
for (id, _) in q.collect_cloned(world, qs) {
let clouds = CloudState::new(100.0);
let material = CloudMaterial::new(assets.clone(), &clouds);
let data = Entity::new()
.with(
renderer_shader(),
cb(|assets, config| {
CloudShaderKey {
shadow_cascades: config.shadow_cascades,
}
.get(assets)
}),
)
.with(ambient_renderer::material(), SharedMaterial::new(material))
.with(cloud_state(), clouds)
.with(overlay(), ())
.with(mesh(), QuadMeshKey.get(&assets))
.with(primitives(), vec![])
.with_default(gpu_primitives_mesh())
.with_default(gpu_primitives_lod())
.with(translation(), vec3(0.0, 0.0, -1.0));
world.add_components(id, data).unwrap();
}
}),
],
)
}
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, PartialEq, Eq)]
struct CloudParams {
count: i32,
}
#[derive(Debug)]
pub struct CloudMaterial {
id: String,
pub bind_group: wgpu::BindGroup,
cloud_buffer: TypedBuffer<Node>,
}
impl CloudMaterial {
pub fn new(assets: AssetCache, state: &CloudState) -> Self {
let gpu = GpuKey.get(&assets);
let cloud_buffer = TypedBuffer::new(
gpu.clone(),
"Cloud Buffer",
state.tree.len().max(64) as u64,
0,
BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,
);
Self {
id: friendly_id(),
bind_group: gpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &get_cloud_shader_layout().get(&assets),
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: cloud_buffer.buffer().as_entire_binding(),
}],
label: Some("CloudMaterial.bind_group"),
}),
cloud_buffer,
}
}
}
impl Material for CloudMaterial {
fn bind_group(&self) -> &BindGroup {
&self.bind_group
}
fn id(&self) -> &str {
&self.id
}
}
pub fn get_scatter_module() -> Arc<ShaderModule> {
Arc::new(ShaderModule::new("Scatter", ATMOSPHERIC_SCATTERING_SOURCE))
}
fn get_cloud_shader_layout() -> BindGroupDesc<'static> {
BindGroupDesc {
entries: vec![wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
label: MATERIAL_BIND_GROUP.into(),
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct CloudShaderKey {
shadow_cascades: u32,
}
impl SyncAssetKey<Arc<RendererShader>> for CloudShaderKey {
fn load(&self, assets: AssetCache) -> Arc<RendererShader> {
let layout = get_cloud_shader_layout();
let shader = include_file!("clouds.wgsl");
let id = "cloud shader".to_string();
Arc::new(RendererShader {
shader: Shader::new(
&assets,
"clouds",
&[GLOBALS_BIND_GROUP, MATERIAL_BIND_GROUP],
&ShaderModule::new("clouds", shader)
.with_binding_desc(layout)
.with_dependencies(get_overlay_modules(&assets, self.shadow_cascades))
.with_dependency(get_scatter_module()),
)
.unwrap(),
id,
vs_main: "vs_main".to_string(),
fs_forward_main: "fs_forward_main".to_string(),
fs_shadow_main: "fs_shadow_main".to_string(),
fs_outline_main: "fs_outlines_main".to_string(),
transparent: true,
double_sided: false,
depth_write_enabled: true,
transparency_group: 0,
})
}
}