ambient_renderer/
lib.rs

1use std::{f32::consts::PI, fmt::Debug, sync::Arc};
2
3use ambient_core::{
4    asset_cache, async_ecs::async_run, gpu_components, gpu_ecs::{ComponentToGpuSystem, GpuComponentFormat, GpuWorldShaderModuleKey, GpuWorldSyncEvent}, mesh, runtime, transform::get_world_rotation
5};
6use ambient_ecs::{components, query_mut, Debuggable, Entity, EntityId, Resource, SystemGroup, World};
7use ambient_gpu::{
8    mesh_buffer::GpuMesh, shader_module::{BindGroupDesc, Shader, ShaderIdent, ShaderModule}, wgsl_utils::wgsl_interpolate
9};
10use ambient_std::{asset_cache::*, asset_url::AbsAssetUrl, cb, include_file, Cb};
11use derive_more::*;
12use downcast_rs::{impl_downcast, DowncastSync};
13use glam::{uvec4, UVec2, UVec4, Vec3};
14use serde::{Deserialize, Serialize};
15
16pub mod bind_groups;
17mod collect;
18mod culling;
19mod globals;
20pub mod lod;
21pub mod materials;
22mod outlines;
23mod overlay_renderer;
24mod renderer;
25mod shaders;
26mod shadow_renderer;
27pub mod skinning;
28mod target;
29mod transparent_renderer;
30mod tree_renderer;
31use ambient_ecs::{query, Component};
32pub use collect::*;
33pub use culling::*;
34pub use globals::*;
35use materials::pbr_material::PbrMaterialFromUrl;
36pub use materials::*;
37use ordered_float::OrderedFloat;
38pub use outlines::*;
39pub use renderer::*;
40pub use shaders::*;
41pub use shadow_renderer::*;
42pub use target::*;
43pub use transparent_renderer::*;
44pub use tree_renderer::*;
45
46pub const MAX_PRIMITIVE_COUNT: usize = 16;
47
48pub use ambient_ecs::generated::components::core::rendering::{
49    cast_shadows, color, double_sided, fog_color, fog_density, fog_height_falloff, light_ambient, light_diffuse, overlay, pbr_material_from_url, sun, transparency_group
50};
51
52components!("rendering", {
53    @[Debuggable]
54    primitives: Vec<RenderPrimitive>,
55
56    /// The (cpu) primitives are split into an SoA on the gpu side
57    gpu_primitives_mesh: [u32; MAX_PRIMITIVE_COUNT],
58    gpu_primitives_lod: [u32; MAX_PRIMITIVE_COUNT],
59
60    renderer_shader: RendererShaderProducer,
61    material: SharedMaterial,
62    @[Resource]
63    renderer_stats: String,
64});
65gpu_components! {
66    color() => color: GpuComponentFormat::Vec4,
67    gpu_primitives_mesh() => gpu_primitives_mesh: GpuComponentFormat::Mat4,
68    gpu_primitives_lod() => gpu_primitives_lod: GpuComponentFormat::Mat4,
69}
70pub fn init_all_components() {
71    init_components();
72    init_gpu_components();
73    outlines::init_gpu_components();
74    culling::init_gpu_components();
75    lod::init_components();
76    lod::init_gpu_components();
77    skinning::init_components();
78    skinning::init_gpu_components();
79}
80
81pub fn systems() -> SystemGroup {
82    SystemGroup::new(
83        "renderer",
84        vec![
85            query(pbr_material_from_url().changed()).to_system(|q, world, qs, _| {
86                for (id, url) in q.collect_cloned(world, qs) {
87                    let url = match AbsAssetUrl::parse(url) {
88                        Ok(value) => value,
89                        Err(err) => {
90                            log::warn!("Failed to parse pbr_material_from_url url: {:?}", err);
91                            continue;
92                        }
93                    };
94                    let assets = world.resource(asset_cache()).clone();
95                    let async_run = world.resource(async_run()).clone();
96                    world.resource(runtime()).spawn(async move {
97                        match PbrMaterialFromUrl(url).get(&assets).await {
98                            Err(err) => {
99                                log::warn!("Failed to load pbr material from url: {:?}", err);
100                            }
101                            Ok(mat) => {
102                                async_run.run(move |world| {
103                                    world
104                                        .add_components(
105                                            id,
106                                            Entity::new()
107                                                .with(renderer_shader(), cb(pbr_material::get_pbr_shader))
108                                                .with(material(), mat.into()),
109                                        )
110                                        .ok();
111                                });
112                            }
113                        }
114                    });
115                }
116            }),
117            query_mut((primitives(),), (renderer_shader().changed(), material().changed(), mesh().changed())).to_system(
118                |q, world, qs, _| {
119                    for (_, (primitives,), (shader, material, mesh)) in q.iter(world, qs) {
120                        *primitives =
121                            vec![RenderPrimitive { shader: shader.clone(), material: material.clone(), mesh: mesh.clone(), lod: 0 }];
122                    }
123                },
124            ),
125            query_mut((gpu_primitives_mesh(), gpu_primitives_lod()), (primitives().changed(),)).to_system(|q, world, qs, _| {
126                for (id, (p_mesh, p_lod), (primitives,)) in q.iter(world, qs) {
127                    if primitives.len() > MAX_PRIMITIVE_COUNT {
128                        log::warn!("Entity {} has more than {MAX_PRIMITIVE_COUNT} primitives", id);
129                    }
130                    for (i, p) in primitives.iter().enumerate().take(MAX_PRIMITIVE_COUNT) {
131                        p_mesh[i] = p.mesh.index() as u32;
132                        p_lod[i] = p.lod as u32;
133                    }
134                }
135            }),
136            Box::new(outlines::systems()),
137        ],
138    )
139}
140
141pub fn gpu_world_systems() -> SystemGroup<GpuWorldSyncEvent> {
142    SystemGroup::new(
143        "renderer/gpu_world_update",
144        vec![
145            Box::new(outlines::gpu_world_systems()),
146            Box::new(ComponentToGpuSystem::new(GpuComponentFormat::Vec4, color(), gpu_components::color())),
147            Box::new(ComponentToGpuSystem::new(GpuComponentFormat::Mat4, gpu_primitives_mesh(), gpu_components::gpu_primitives_mesh())),
148            Box::new(ComponentToGpuSystem::new(GpuComponentFormat::Mat4, gpu_primitives_lod(), gpu_components::gpu_primitives_lod())),
149            Box::new(lod::gpu_world_system()),
150            Box::new(skinning::gpu_world_systems()),
151        ],
152    )
153}
154
155pub fn get_active_sun(world: &World, scene: Component<()>) -> Option<EntityId> {
156    query((scene, sun())).iter(world, None).max_by_key(|(_, (_, x))| OrderedFloat(**x)).map(|(id, _)| id)
157}
158pub fn get_sun_light_direction(world: &World, scene: Component<()>) -> Vec3 {
159    get_active_sun(world, scene)
160        .and_then(|sun| get_world_rotation(world, sun).ok())
161        .map(|rot| rot.mul_vec3(Vec3::X))
162        .unwrap_or(default_sun_direction())
163}
164
165#[derive(Clone, Debug)]
166pub struct RenderPrimitive {
167    pub material: SharedMaterial,
168    pub shader: RendererShaderProducer,
169    pub mesh: Arc<GpuMesh>,
170    pub lod: usize,
171}
172pub type PrimitiveIndex = usize;
173pub fn get_gpu_primitive_id(world: &World, id: EntityId, primitive_index: PrimitiveIndex, material_index: u32) -> UVec4 {
174    let loc = world.entity_loc(id).unwrap();
175    uvec4(loc.archetype as u32, loc.index as u32, primitive_index as u32, material_index)
176}
177
178#[repr(C)]
179#[derive(Debug, Clone, Copy, Default, bytemuck::Pod, bytemuck::Zeroable)]
180pub struct GpuRenderPrimitive {
181    pub mesh: u32,
182    pub lod: u32,
183    pub _padding: UVec2,
184}
185
186#[derive(Clone, Debug, Deref, DerefMut)]
187pub struct SharedMaterial(Arc<dyn Material + 'static>);
188
189impl<T: Material + 'static> From<Arc<T>> for SharedMaterial {
190    fn from(v: Arc<T>) -> Self {
191        Self(v as Arc<dyn Material>)
192    }
193}
194
195impl SharedMaterial {
196    pub fn replace(&mut self, value: impl Material + 'static) {
197        self.0 = Arc::new(value)
198    }
199
200    pub fn new(value: impl Material + 'static) -> Self {
201        Self(Arc::new(value))
202    }
203
204    pub fn borrow_downcast<T: Material>(&self) -> &T {
205        self.0.downcast_ref::<T>().unwrap()
206    }
207}
208
209impl<T> From<T> for SharedMaterial
210where
211    T: Material + 'static,
212{
213    fn from(v: T) -> Self {
214        Self::new(v)
215    }
216}
217
218/// No bind groups
219pub fn get_defs_module() -> Arc<ShaderModule> {
220    let iter = [("PI", PI)].iter();
221    #[cfg(not(target_os = "unknown"))]
222    let iter = iter.map(|(k, v)| format!("const {k}: f32 = {v};\n"));
223    #[cfg(target_os = "unknown")]
224    let iter = iter.map(|(k, v)| format!("const {k}: f32 = {v};\n"));
225
226    let iter = iter.chain([wgsl_interpolate(), include_file!("polyfill.wgsl")]).collect::<String>();
227
228    Arc::new(ShaderModule::new("defs", iter))
229}
230
231pub fn get_mesh_meta_module(bind_group_offset: u32) -> Arc<ShaderModule> {
232    Arc::new(
233        ShaderModule::new("mesh_meta", include_file!("mesh_meta.wgsl"))
234            .with_ident(ShaderIdent::constant("MESH_METADATA_BINDING", bind_group_offset + MESH_METADATA_BINDING))
235            .with_binding_desc(get_mesh_meta_layout(bind_group_offset)),
236    )
237}
238
239pub fn get_mesh_data_module(bind_group_offset: u32) -> Arc<ShaderModule> {
240    Arc::new(
241        ShaderModule::new("mesh_data", include_file!("mesh_data.wgsl"))
242            .with_ident(ShaderIdent::constant("MESH_BASE_BINDING", bind_group_offset + MESH_BASE_BINDING))
243            .with_ident(ShaderIdent::constant("MESH_SKIN_BINDING", bind_group_offset + MESH_SKIN_BINDING))
244            .with_ident(ShaderIdent::constant("SKINS_BINDING", bind_group_offset + SKINS_BINDING))
245            .with_binding_desc(get_mesh_data_layout(bind_group_offset))
246            .with_dependency(get_mesh_meta_module(bind_group_offset)),
247    )
248}
249
250pub fn primitives_layout() -> BindGroupDesc<'static> {
251    BindGroupDesc {
252        entries: vec![wgpu::BindGroupLayoutEntry {
253            binding: 0,
254            visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
255            ty: wgpu::BindingType::Buffer {
256                ty: wgpu::BufferBindingType::Storage { read_only: true },
257                has_dynamic_offset: false,
258                min_binding_size: None,
259            },
260            count: None,
261        }],
262        label: PRIMITIVES_BIND_GROUP.into(),
263    }
264}
265
266pub fn get_common_layout() -> BindGroupDesc<'static> {
267    BindGroupDesc {
268        entries: vec![wgpu::BindGroupLayoutEntry {
269            binding: 0,
270            visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
271            ty: wgpu::BindingType::Buffer {
272                ty: wgpu::BufferBindingType::Storage { read_only: true },
273                has_dynamic_offset: false,
274                min_binding_size: None,
275            },
276            count: None,
277        }],
278        label: PRIMITIVES_BIND_GROUP.into(),
279    }
280}
281
282/// Includes entity locs
283pub fn get_common_module(_: &AssetCache) -> Arc<ShaderModule> {
284    Arc::new(
285        ShaderModule::new("renderer_common", include_file!("renderer_common.wgsl"))
286            .with_binding_desc(get_common_layout())
287            .with_dependency(get_mesh_data_module(GLOBALS_BIND_GROUP_SIZE)),
288    )
289}
290
291/// Contains scene globals and shadow maps
292pub fn get_globals_module(_assets: &AssetCache, shadow_cascades: u32) -> Arc<ShaderModule> {
293    Arc::new(
294        ShaderModule::new("globals", include_file!("globals.wgsl"))
295            .with_ident(ShaderIdent::constant("SHADOW_CASCADES", shadow_cascades))
296            .with_binding_desc(globals_layout()),
297    )
298}
299
300pub fn get_forward_modules(assets: &AssetCache, shadow_cascades: u32) -> Vec<Arc<ShaderModule>> {
301    vec![
302        get_defs_module(),
303        get_mesh_data_module(GLOBALS_BIND_GROUP_SIZE),
304        get_globals_module(assets, shadow_cascades),
305        GpuWorldShaderModuleKey { read_only: true }.get(assets),
306        get_common_module(assets),
307    ]
308}
309
310pub fn get_overlay_modules(assets: &AssetCache, shadow_cascades: u32) -> Vec<Arc<ShaderModule>> {
311    vec![get_defs_module(), get_globals_module(assets, shadow_cascades), get_mesh_data_module(GLOBALS_BIND_GROUP_SIZE)]
312}
313
314pub struct MaterialShader {
315    pub id: String,
316    pub shader: Arc<ShaderModule>,
317}
318
319pub trait Material: Debug + Sync + Send + DowncastSync {
320    fn id(&self) -> &str;
321
322    fn name(&self) -> &str {
323        self.id()
324    }
325
326    fn update(&self, _: &World) {}
327
328    fn bind_group(&self) -> &wgpu::BindGroup;
329
330    fn transparent(&self) -> Option<bool> {
331        None
332    }
333
334    fn double_sided(&self) -> Option<bool> {
335        None
336    }
337    /// TODO: Apply to tree renderer too (only applies to transparent now)
338    fn depth_write_enabled(&self) -> Option<bool> {
339        None
340    }
341    fn transparency_group(&self) -> Option<i32> {
342        None
343    }
344}
345
346impl_downcast!(sync Material);
347
348#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
349#[repr(u32)]
350pub enum AlphaMode {
351    Opaque,
352    Mask,
353    Blend,
354}
355impl Default for AlphaMode {
356    fn default() -> Self {
357        Self::Opaque
358    }
359}
360
361#[derive(Debug, Clone, Copy)]
362pub enum FSMain {
363    Forward,
364    Shadow,
365    Outline,
366}
367
368pub struct RendererShader {
369    pub id: String,
370    pub shader: Arc<Shader>,
371    pub vs_main: String,
372    pub fs_shadow_main: String,
373    pub fs_forward_main: String,
374    pub fs_outline_main: String,
375    pub transparent: bool,
376    pub double_sided: bool,
377    /// TODO: Apply to tree renderer too (only applies to transparent now)
378    pub depth_write_enabled: bool,
379    pub transparency_group: i32,
380}
381impl RendererShader {
382    fn get_fs_main_name(&self, main: FSMain) -> &str {
383        match main {
384            FSMain::Forward => &self.fs_forward_main,
385            FSMain::Shadow => &self.fs_shadow_main,
386            FSMain::Outline => &self.fs_outline_main,
387        }
388    }
389}
390impl Debug for RendererShader {
391    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
392        f.debug_struct("RendererShader").field("id", &self.id).finish()
393    }
394}
395
396pub type RendererShaderProducer = Cb<dyn Fn(&AssetCache, &RendererConfig) -> Arc<RendererShader> + Sync + Send>;
397
398#[repr(C)]
399#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
400pub struct DrawIndexedIndirect {
401    pub vertex_count: u32,
402    pub instance_count: u32,
403    pub base_index: u32,
404    pub vertex_offset: i32,
405    pub base_instance: u32,
406}
407
408fn is_transparent(world: &World, id: EntityId, material: &SharedMaterial, shader: &RendererShader) -> bool {
409    world.get(id, transparency_group()).is_ok() || material.transparent().unwrap_or(shader.transparent)
410}