Skip to main content

awsm_renderer/
lib.rs

1//! High-level renderer API and shared modules.
2
3#![allow(clippy::type_complexity)]
4#![allow(clippy::too_many_arguments)]
5#![allow(clippy::match_like_matches_macro)]
6#![allow(clippy::vec_init_then_push)]
7pub mod anti_alias;
8pub mod bind_group_layout;
9pub mod bind_groups;
10pub mod bounds;
11pub mod buffer;
12pub mod camera;
13pub mod debug;
14pub mod environment;
15pub mod error;
16pub mod frustum;
17pub mod instances;
18pub mod lights;
19pub mod materials;
20pub mod meshes;
21pub mod picker;
22pub mod pipeline_layouts;
23pub mod pipelines;
24pub mod post_process;
25pub mod render;
26pub mod render_passes;
27pub mod render_textures;
28pub mod renderable;
29pub mod shaders;
30pub mod textures;
31pub mod transforms;
32pub mod update;
33// re-export
34pub mod core {
35    pub use awsm_renderer_core::*;
36}
37#[cfg(feature = "gltf")]
38pub mod gltf;
39
40#[cfg(feature = "animation")]
41pub mod animation;
42
43use std::sync::LazyLock;
44
45use awsm_renderer_core::{
46    brdf_lut::generate::{BrdfLut, BrdfLutOptions},
47    command::color::Color,
48    compatibility::CompatibilityRequirements,
49    cubemap::images::CubemapBitmapColors,
50    renderer::{AwsmRendererWebGpu, AwsmRendererWebGpuBuilder},
51};
52use bind_groups::BindGroups;
53use camera::CameraBuffer;
54use instances::Instances;
55use lights::Lights;
56use materials::Materials;
57use meshes::Meshes;
58use pipelines::Pipelines;
59use shaders::Shaders;
60use textures::Textures;
61use transforms::Transforms;
62
63use crate::{
64    anti_alias::AntiAliasing,
65    bind_group_layout::BindGroupLayouts,
66    debug::AwsmRendererLogging,
67    environment::{Environment, Skybox},
68    lights::ibl::{Ibl, IblTexture},
69    picker::Picker,
70    pipeline_layouts::PipelineLayouts,
71    post_process::PostProcessing,
72    render_passes::{RenderPassInitContext, RenderPasses},
73    render_textures::{RenderTextureFormats, RenderTextures},
74};
75
76/// Main renderer state and GPU resources.
77pub struct AwsmRenderer {
78    pub gpu: core::renderer::AwsmRendererWebGpu,
79    pub bind_group_layouts: BindGroupLayouts,
80    pub bind_groups: BindGroups,
81    pub meshes: Meshes,
82    pub camera: CameraBuffer,
83    pub transforms: Transforms,
84    pub instances: Instances,
85    pub shaders: Shaders,
86    pub materials: Materials,
87    pub pipeline_layouts: PipelineLayouts,
88    pub pipelines: Pipelines,
89    pub lights: Lights,
90    pub textures: Textures,
91    pub logging: AwsmRendererLogging,
92    pub render_textures: RenderTextures,
93    pub render_passes: RenderPasses,
94    pub environment: Environment,
95    pub anti_aliasing: AntiAliasing,
96    pub post_processing: PostProcessing,
97    pub picker: Picker,
98    // we pick between these on the fly
99    _clear_color_perceptual_to_linear: Color,
100    _clear_color: Color,
101
102    #[cfg(feature = "gltf")]
103    gltf: gltf::cache::GltfCache,
104
105    #[cfg(feature = "animation")]
106    pub animations: animation::Animations,
107}
108
109/// Compatibility requirements for this renderer.
110pub static COMPATIBITLIY_REQUIREMENTS: LazyLock<CompatibilityRequirements> =
111    LazyLock::new(|| CompatibilityRequirements {
112        storage_buffers: Some(9),
113    });
114
115impl AwsmRenderer {
116    /// Removes all scene data by rebuilding the renderer state.
117    pub async fn remove_all(&mut self) -> crate::error::Result<()> {
118        // meh, just recreate the renderer, it's fine
119        let renderer = AwsmRendererBuilder::new(self.gpu.clone())
120            .with_logging(self.logging.clone())
121            .with_clear_color(self._clear_color.clone())
122            .with_render_texture_formats(self.render_textures.formats.clone())
123            .build()
124            .await?;
125
126        *self = renderer;
127        Ok(())
128    }
129}
130
131/// Builder for `AwsmRenderer`.
132pub struct AwsmRendererBuilder {
133    gpu: AwsmRendererGpuBuilderKind,
134    logging: AwsmRendererLogging,
135    render_texture_formats: Option<RenderTextureFormats>,
136    brdf_lut_options: BrdfLutOptions,
137    clear_color: Color,
138    // all these colors are typically replaced when loading external textures
139    // but we want something to show by default
140    skybox_colors: CubemapBitmapColors,
141    ibl_filtered_env_colors: CubemapBitmapColors,
142    ibl_irradiance_colors: CubemapBitmapColors,
143    anti_aliasing: AntiAliasing,
144    post_processing: PostProcessing,
145}
146
147/// WebGPU builder input for `AwsmRendererBuilder`.
148pub enum AwsmRendererGpuBuilderKind {
149    /// Build from a WebGPU builder.
150    WebGpuBuilder(AwsmRendererWebGpuBuilder),
151    /// Use an already-built WebGPU context.
152    WebGpuBuilt(AwsmRendererWebGpu),
153}
154
155impl From<AwsmRendererWebGpuBuilder> for AwsmRendererGpuBuilderKind {
156    fn from(builder: AwsmRendererWebGpuBuilder) -> Self {
157        AwsmRendererGpuBuilderKind::WebGpuBuilder(builder)
158    }
159}
160
161impl From<AwsmRendererWebGpu> for AwsmRendererGpuBuilderKind {
162    fn from(gpu: AwsmRendererWebGpu) -> Self {
163        AwsmRendererGpuBuilderKind::WebGpuBuilt(gpu)
164    }
165}
166
167impl AwsmRendererBuilder {
168    /// Creates a new renderer builder from a WebGPU builder or context.
169    pub fn new(gpu: impl Into<AwsmRendererGpuBuilderKind>) -> Self {
170        Self {
171            gpu: gpu.into(),
172            logging: AwsmRendererLogging::default(),
173            render_texture_formats: None,
174            clear_color: Color::BLACK,
175            brdf_lut_options: BrdfLutOptions::default(),
176            skybox_colors: CubemapBitmapColors {
177                z_positive: Color::BLACK,
178                z_negative: Color::BLACK,
179                x_positive: Color::BLACK,
180                x_negative: Color::BLACK,
181                y_positive: Color::BLACK,
182                y_negative: Color::BLACK,
183            },
184            // skybox_colors: CubemapBitmapColors {
185            //     z_positive: Color::from_hex_rgb(0xFF0000), // red
186            //     z_negative: Color::from_hex_rgb(0x00FF00), // green
187            //     x_positive: Color::from_hex_rgb(0x0000FF), // blue
188            //     x_negative: Color::from_hex_rgb(0xFFFF00), // yellow
189            //     y_positive: Color::from_hex_rgb(0xFF00FF), // magenta
190            //     y_negative: Color::from_hex_rgb(0x00FFFF), // cyan
191            // },
192            ibl_filtered_env_colors: CubemapBitmapColors {
193                z_positive: Color::WHITE,
194                z_negative: Color::WHITE,
195                x_positive: Color::WHITE,
196                x_negative: Color::WHITE,
197                y_positive: Color::WHITE,
198                y_negative: Color::WHITE,
199            },
200            ibl_irradiance_colors: CubemapBitmapColors {
201                z_positive: Color::WHITE,
202                z_negative: Color::WHITE,
203                x_positive: Color::WHITE,
204                x_negative: Color::WHITE,
205                y_positive: Color::WHITE,
206                y_negative: Color::WHITE,
207            },
208            anti_aliasing: AntiAliasing::default(),
209            post_processing: PostProcessing::default(),
210        }
211    }
212
213    /// Sets BRDF LUT generation options.
214    pub fn with_brdf_lut_options(mut self, options: BrdfLutOptions) -> Self {
215        self.brdf_lut_options = options;
216        self
217    }
218
219    /// Sets the filtered environment colors for IBL.
220    pub fn with_ibl_filtered_env_colors(mut self, colors: CubemapBitmapColors) -> Self {
221        self.ibl_filtered_env_colors = colors;
222        self
223    }
224
225    /// Sets the anti-aliasing configuration.
226    pub fn with_anti_aliasing(mut self, anti_aliasing: AntiAliasing) -> Self {
227        self.anti_aliasing = anti_aliasing;
228        self
229    }
230
231    /// Sets the irradiance colors for IBL.
232    pub fn with_ibl_irradiance_colors(mut self, colors: CubemapBitmapColors) -> Self {
233        self.ibl_irradiance_colors = colors;
234        self
235    }
236
237    /// Sets the skybox colors.
238    pub fn with_skybox_colors(mut self, colors: CubemapBitmapColors) -> Self {
239        self.skybox_colors = colors;
240        self
241    }
242
243    /// Sets logging options for the renderer.
244    pub fn with_logging(mut self, logging: AwsmRendererLogging) -> Self {
245        self.logging = logging;
246        self
247    }
248
249    /// Sets render texture formats.
250    pub fn with_render_texture_formats(mut self, formats: RenderTextureFormats) -> Self {
251        self.render_texture_formats = Some(formats);
252        self
253    }
254
255    /// Sets the clear color used for the main render pass.
256    pub fn with_clear_color(mut self, color: Color) -> Self {
257        self.clear_color = color;
258        self
259    }
260
261    /// Builds the renderer and initializes GPU resources.
262    pub async fn build(self) -> std::result::Result<AwsmRenderer, crate::error::AwsmError> {
263        let Self {
264            gpu,
265            logging,
266            render_texture_formats,
267            brdf_lut_options,
268            clear_color,
269            skybox_colors,
270            ibl_filtered_env_colors,
271            ibl_irradiance_colors,
272            anti_aliasing,
273            post_processing,
274        } = self;
275
276        let mut gpu = match gpu {
277            AwsmRendererGpuBuilderKind::WebGpuBuilder(builder) => builder.build().await?,
278            AwsmRendererGpuBuilderKind::WebGpuBuilt(gpu) => gpu,
279        };
280
281        let mut render_texture_formats = match render_texture_formats {
282            Some(formats) => formats,
283            None => RenderTextureFormats::new(&gpu.device).await,
284        };
285
286        // tracing::info!("Max bind groups: {}", gpu.device.limits().max_bind_groups());
287        // tracing::info!(
288        //     "Max texture size: {}",
289        //     gpu.device.limits().max_texture_dimension_2d()
290        // );
291
292        let mut pipeline_layouts = PipelineLayouts::new();
293        let mut bind_group_layouts = BindGroupLayouts::new();
294        let mut pipelines = Pipelines::new();
295        let mut shaders = Shaders::new();
296
297        let mut textures = Textures::new(&gpu)?;
298        let camera = camera::CameraBuffer::new(&gpu)?;
299        let lights = Lights::new(
300            &gpu,
301            Ibl::new(
302                IblTexture::new_colors(&gpu, &mut textures, ibl_filtered_env_colors).await?,
303                IblTexture::new_colors(&gpu, &mut textures, ibl_irradiance_colors).await?,
304            ),
305            BrdfLut::new(&gpu, brdf_lut_options).await?,
306        )?;
307        let meshes = Meshes::new(&gpu)?;
308        let transforms = Transforms::new(&gpu)?;
309        let instances = Instances::new(&gpu)?;
310        let materials = Materials::new(&gpu)?;
311        let environment =
312            Environment::new(Skybox::new_colors(&gpu, &mut textures, skybox_colors).await?);
313
314        // temporarily push into an init struct for creating render passes
315        // we'll then destructure it to get our values back
316        let mut render_pass_init = RenderPassInitContext {
317            gpu: &mut gpu,
318            bind_group_layouts: &mut bind_group_layouts,
319            pipeline_layouts: &mut pipeline_layouts,
320            pipelines: &mut pipelines,
321            shaders: &mut shaders,
322            render_texture_formats: &mut render_texture_formats,
323            textures: &mut textures,
324        };
325        let render_passes = RenderPasses::new(&mut render_pass_init).await?;
326
327        let bind_groups = BindGroups::new();
328        let render_textures = RenderTextures::new(&gpu, render_texture_formats).await?;
329
330        let picker = Picker::new(
331            &gpu,
332            &mut bind_group_layouts,
333            &mut pipeline_layouts,
334            &mut shaders,
335            &mut pipelines,
336        )
337        .await?;
338
339        #[cfg(feature = "gltf")]
340        let gltf = gltf::cache::GltfCache::default();
341        #[cfg(feature = "animation")]
342        let animations = animation::Animations::default();
343
344        let mut _self = AwsmRenderer {
345            gpu,
346            meshes,
347            camera,
348            transforms,
349            instances,
350            shaders,
351            bind_group_layouts,
352            bind_groups,
353            materials,
354            pipeline_layouts,
355            pipelines,
356            lights,
357            textures,
358            environment,
359            render_passes,
360            _clear_color: clear_color.clone(),
361            _clear_color_perceptual_to_linear: clear_color.perceptual_to_linear(),
362            logging,
363            render_textures,
364            anti_aliasing,
365            post_processing,
366            picker,
367            #[cfg(feature = "gltf")]
368            gltf,
369            #[cfg(feature = "animation")]
370            animations,
371        };
372
373        // need to call these to create pipelines
374        _self.set_anti_aliasing(_self.anti_aliasing.clone()).await?;
375        _self
376            .set_post_processing(_self.post_processing.clone())
377            .await?;
378
379        Ok(_self)
380    }
381}