kitgit

tirbofish/dropbear · diff

1aca576 · Thribhu K

wip: resetting computer, creating backup

Unverified

diff --git a/crates/dropbear-engine/src/camera.rs b/crates/dropbear-engine/src/camera.rs
index 4de9409..efb966a 100644
--- a/crates/dropbear-engine/src/camera.rs
+++ b/crates/dropbear-engine/src/camera.rs
@@ -80,14 +80,6 @@ pub struct Camera {
     pub view_mat: DMat4,
     /// Projection Matrix
     pub proj_mat: DMat4,
-    
-    /// Smooth damping factor for camera position updates (0.0 = no smoothing, higher = more smoothing)
-    /// This helps prevent jittering when camera follows physics-driven objects
-    pub position_smoothing: f64,
-    /// Internal: previous eye position for smoothing
-    smooth_eye: DVec3,
-    /// Internal: previous target position for smoothing  
-    smooth_target: DVec3,
 }
 
 /// A simple builder/struct that allows you to build a [`Camera`]
@@ -102,7 +94,7 @@ pub struct CameraBuilder {
 }
 
 impl Camera {
-    pub const CAMERA_BIND_GROUP_LAYOUT: wgpu::BindGroupLayoutDescriptor<'_> = 
+    pub const CAMERA_BIND_GROUP_LAYOUT: wgpu::BindGroupLayoutDescriptor<'_> =
         BindGroupLayoutDescriptor {
             entries: &[BindGroupLayoutEntry {
                 binding: 0,
@@ -173,9 +165,6 @@ impl Camera {
             },
             view_mat,
             proj_mat,
-            position_smoothing: 10.0,  // Default smoothing value
-            smooth_eye: builder.eye,
-            smooth_target: builder.target,
         };
 
         log::debug!("Created new camera{}", if let Some(l) = label { format!(" with the label {}", l) } else { String::new() } );
@@ -228,17 +217,7 @@ impl Camera {
     }
 
     fn build_vp(&mut self) -> DMat4 {
-        // Apply smooth interpolation to camera position if smoothing is enabled
-        if self.position_smoothing > 0.0 {
-            let lerp_factor = (1.0 / (1.0 + self.position_smoothing)).clamp(0.0, 1.0);
-            self.smooth_eye = self.smooth_eye.lerp(self.eye, lerp_factor);
-            self.smooth_target = self.smooth_target.lerp(self.target, lerp_factor);
-        } else {
-            self.smooth_eye = self.eye;
-            self.smooth_target = self.target;
-        }
-        
-        let view = DMat4::look_at_lh(self.smooth_eye, self.smooth_target, self.up);
+        let view = DMat4::look_at_lh(self.eye, self.target, self.up);
         let proj = DMat4::perspective_infinite_reverse_lh(
             self.settings.fov_y.to_radians(),
             self.aspect,
diff --git a/crates/dropbear-engine/src/lib.rs b/crates/dropbear-engine/src/lib.rs
index f8b38cc..32f0558 100644
--- a/crates/dropbear-engine/src/lib.rs
+++ b/crates/dropbear-engine/src/lib.rs
@@ -62,6 +62,7 @@ pub use gilrs;
 pub use wgpu;
 pub use winit;
 use winit::window::{WindowAttributes, WindowId};
+use crate::pipelines::hdr::HdrPipeline;
 use crate::scene::Scene;
 
 pub struct BindGroupLayouts {
@@ -94,6 +95,7 @@ pub struct State {
     pub texture_id: Arc<TextureId>,
     pub future_queue: Arc<FutureQueue>,
     pub mipmapper: Arc<MipMapper>,
+    pub hdr: Arc<HdrPipeline>,
 
     physics_accumulator: Duration,
 
@@ -243,8 +245,8 @@ Hardware:
 
         let mut egui_renderer = Arc::new(Mutex::new(EguiRenderer::new(
             &device,
-            config.format,
-            None,
+            Texture::TEXTURE_FORMAT,
+            Some(Texture::DEPTH_FORMAT),
             1,
             &window,
         )));
@@ -333,6 +335,8 @@ Hardware:
         let device = Arc::new(device);
         let queue = Arc::new(queue);
 
+        let hdr = Arc::new(HdrPipeline::new(&device, &config));
+
         // let yakui_renderer = Arc::new(Mutex::new(yakui_wgpu::YakuiWgpu::new(
         //     &device,
         //     &queue
@@ -375,6 +379,7 @@ Hardware:
             supports_storage: supports_storage_resources,
             // yakui_renderer,
             // yakui_texture,
+            hdr,
         };
 
         Ok(result)
@@ -390,6 +395,8 @@ Hardware:
             }
             self.surface.configure(&self.device, &self.config.read());
             self.is_surface_configured = true;
+            Arc::get_mut(&mut self.hdr)
+                .resize(&self.device, width, height);
         }
 
         self.depth_texture =
@@ -466,7 +473,7 @@ Hardware:
                 let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                     label: Some("surface clear pass"),
                     color_attachments: &[Some(wgpu::RenderPassColorAttachment {
-                        view: &view,
+                        view: &self.hdr.view(),
                         depth_slice: None,
                         resolve_target: None,
                         ops: wgpu::Operations {
diff --git a/crates/dropbear-engine/src/pipelines/hdr.rs b/crates/dropbear-engine/src/pipelines/hdr.rs
index 23d53bb..b4d6501 100644
--- a/crates/dropbear-engine/src/pipelines/hdr.rs
+++ b/crates/dropbear-engine/src/pipelines/hdr.rs
@@ -1,158 +1,166 @@
-// use crate::texture::Texture;
-//
-// pub struct HdrPipeline {
-//     pipeline: wgpu::RenderPipeline,
-//     bind_group: wgpu::BindGroup,
-//     texture: Texture,
-//     width: u32,
-//     height: u32,
-//     format: wgpu::TextureFormat,
-//     layout: wgpu::BindGroupLayout,
-// }
-//
-// impl HdrPipeline {
-//     pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self {
-//         let width = config.width;
-//         let height = config.height;
-//
-//         // We could use `Rgba32Float`, but that requires some extra
-//         // features to be enabled for rendering.
-//         let format = wgpu::TextureFormat::Rgba16Float;
-//
-//         let texture = Texture(
-//             device,
-//             width,
-//             height,
-//             format,
-//             wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
-//             wgpu::FilterMode::Nearest,
-//             Some("Hdr::texture"),
-//         );
-//
-//         let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
-//             label: Some("Hdr::layout"),
-//             entries: &[
-//                 // This is the HDR texture
-//                 wgpu::BindGroupLayoutEntry {
-//                     binding: 0,
-//                     visibility: wgpu::ShaderStages::FRAGMENT,
-//                     ty: wgpu::BindingType::Texture {
-//                         sample_type: wgpu::TextureSampleType::Float { filterable: true },
-//                         view_dimension: wgpu::TextureViewDimension::D2,
-//                         multisampled: false,
-//                     },
-//                     count: None,
-//                 },
-//                 wgpu::BindGroupLayoutEntry {
-//                     binding: 1,
-//                     visibility: wgpu::ShaderStages::FRAGMENT,
-//                     ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
-//                     count: None,
-//                 },
-//             ],
-//         });
-//         let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
-//             label: Some("Hdr::bind_group"),
-//             layout: &layout,
-//             entries: &[
-//                 wgpu::BindGroupEntry {
-//                     binding: 0,
-//                     resource: wgpu::BindingResource::TextureView(&texture.view),
-//                 },
-//                 wgpu::BindGroupEntry {
-//                     binding: 1,
-//                     resource: wgpu::BindingResource::Sampler(&texture.sampler),
-//                 },
-//             ],
-//         });
-//
-//         // We'll cover the shader next
-//         let shader = wgpu::include_wgsl!("hdr.wgsl");
-//         let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
-//             label: None,
-//             bind_group_layouts: &[&layout],
-//             push_constant_ranges: &[],
-//         });
-//
-//         let pipeline = create_render_pipeline(
-//             device,
-//             &pipeline_layout,
-//             config.format.add_srgb_suffix(),
-//             None,
-//             // We'll use some math to generate the vertex data in
-//             // the shader, so we don't need any vertex buffers
-//             &[],
-//             wgpu::PrimitiveTopology::TriangleList,
-//             shader,
-//         );
-//
-//         Self {
-//             pipeline,
-//             bind_group,
-//             layout,
-//             texture,
-//             width,
-//             height,
-//             format,
-//         }
-//     }
-//
-//     /// Resize the HDR texture
-//     pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
-//         self.texture = texture::Texture::create_2d_texture(
-//             device,
-//             width,
-//             height,
-//             wgpu::TextureFormat::Rgba16Float,
-//             wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
-//             wgpu::FilterMode::Nearest,
-//             Some("Hdr::texture"),
-//         );
-//         self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
-//             label: Some("Hdr::bind_group"),
-//             layout: &self.layout,
-//             entries: &[
-//                 wgpu::BindGroupEntry {
-//                     binding: 0,
-//                     resource: wgpu::BindingResource::TextureView(&self.texture.view),
-//                 },
-//                 wgpu::BindGroupEntry {
-//                     binding: 1,
-//                     resource: wgpu::BindingResource::Sampler(&self.texture.sampler),
-//                 },
-//             ],
-//         });
-//         self.width = width;
-//         self.height = height;
-//     }
-//
-//     /// Exposes the HDR texture
-//     pub fn view(&self) -> &wgpu::TextureView {
-//         &self.texture.view
-//     }
-//
-//     /// The format of the HDR texture
-//     pub fn format(&self) -> wgpu::TextureFormat {
-//         self.format
-//     }
-//
-//     /// This renders the internal HDR texture to the [TextureView]
-//     /// supplied as parameter.
-//     pub fn process(&self, encoder: &mut wgpu::CommandEncoder, output: &wgpu::TextureView) {
-//         let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
-//             label: Some("Hdr::process"),
-//             color_attachments: &[Some(wgpu::RenderPassColorAttachment {
-//                 view: &output,
-//                 resolve_target: None,
-//                 ops: Operations {
-//                     load: wgpu::LoadOp::Load,
-//                     store: wgpu::StoreOp::Store,
-//                 },
-//             })],
-//             depth_stencil_attachment: None,
-//         });
-//         pass.set_pipeline(&self.pipeline);
-//         pass.set_bind_group(0, &self.bind_group, &[]);
-//         pass.draw(0..3, 0..1);
-//     }
-// }
\ No newline at end of file
+use wgpu::Operations;
+use crate::pipelines::create_render_pipeline;
+use crate::texture::Texture;
+
+pub struct HdrPipeline {
+    pipeline: wgpu::RenderPipeline,
+    bind_group: wgpu::BindGroup,
+    texture: Texture,
+    width: u32,
+    height: u32,
+    format: wgpu::TextureFormat,
+    layout: wgpu::BindGroupLayout,
+}
+
+impl HdrPipeline {
+    pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self {
+        let width = config.width;
+        let height = config.height;
+
+        // We could use `Rgba32Float`, but that requires some extra
+        // features to be enabled for rendering.
+        let format = wgpu::TextureFormat::Rgba16Float;
+
+        let texture = Texture::create_2d_texture(
+            device,
+            width,
+            height,
+            format,
+            wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
+            wgpu::FilterMode::Nearest,
+            Some("Hdr::texture"),
+        );
+
+        
+
+        let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
+            label: Some("Hdr::layout"),
+            entries: &[
+                // This is the HDR texture
+                wgpu::BindGroupLayoutEntry {
+                    binding: 0,
+                    visibility: wgpu::ShaderStages::FRAGMENT,
+                    ty: wgpu::BindingType::Texture {
+                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
+                        view_dimension: wgpu::TextureViewDimension::D2,
+                        multisampled: false,
+                    },
+                    count: None,
+                },
+                wgpu::BindGroupLayoutEntry {
+                    binding: 1,
+                    visibility: wgpu::ShaderStages::FRAGMENT,
+                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
+                    count: None,
+                },
+            ],
+        });
+        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
+            label: Some("Hdr::bind_group"),
+            layout: &layout,
+            entries: &[
+                wgpu::BindGroupEntry {
+                    binding: 0,
+                    resource: wgpu::BindingResource::TextureView(&texture.view),
+                },
+                wgpu::BindGroupEntry {
+                    binding: 1,
+                    resource: wgpu::BindingResource::Sampler(&texture.sampler),
+                },
+            ],
+        });
+
+        // We'll cover the shader next
+        let shader = wgpu::include_wgsl!("shaders/hdr.wgsl");
+        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
+            label: None,
+            bind_group_layouts: &[&layout],
+            push_constant_ranges: &[],
+        });
+
+        let pipeline = create_render_pipeline(
+            Some("hdr render pipeline"),
+            device,
+            &pipeline_layout,
+            config.format.add_srgb_suffix(),
+            None,
+            // We'll use some math to generate the vertex data in
+            // the shader, so we don't need any vertex buffers
+            &[],
+            wgpu::PrimitiveTopology::TriangleList,
+            shader,
+        );
+
+        Self {
+            pipeline,
+            bind_group,
+            layout,
+            texture,
+            width,
+            height,
+            format,
+        }
+    }
+
+    /// Resize the HDR texture
+    pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
+        self.texture = Texture::create_2d_texture(
+            device,
+            width,
+            height,
+            wgpu::TextureFormat::Rgba16Float,
+            wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
+            wgpu::FilterMode::Nearest,
+            Some("Hdr::texture"),
+        );
+        self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
+            label: Some("Hdr::bind_group"),
+            layout: &self.layout,
+            entries: &[
+                wgpu::BindGroupEntry {
+                    binding: 0,
+                    resource: wgpu::BindingResource::TextureView(&self.texture.view),
+                },
+                wgpu::BindGroupEntry {
+                    binding: 1,
+                    resource: wgpu::BindingResource::Sampler(&self.texture.sampler),
+                },
+            ],
+        });
+        self.width = width;
+        self.height = height;
+    }
+
+    /// Exposes the HDR texture
+    pub fn view(&self) -> &wgpu::TextureView {
+        &self.texture.view
+    }
+
+    /// The format of the HDR texture
+    pub fn format(&self) -> wgpu::TextureFormat {
+        self.format
+    }
+
+    /// This renders the internal HDR texture to the [TextureView]
+    /// supplied as parameter.
+    pub fn process(&self, encoder: &mut wgpu::CommandEncoder, output: &wgpu::TextureView) {
+        let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
+            label: Some("Hdr::process"),
+            color_attachments: &[Some(wgpu::RenderPassColorAttachment {
+                view: &output,
+                depth_slice: None,
+                resolve_target: None,
+                ops: Operations {
+                    load: wgpu::LoadOp::Load,
+                    store: wgpu::StoreOp::Store,
+                },
+            })],
+            depth_stencil_attachment: None,
+            timestamp_writes: None,
+            occlusion_query_set: None,
+        });
+        pass.set_pipeline(&self.pipeline);
+        pass.set_bind_group(0, &self.bind_group, &[]);
+        pass.draw(0..3, 0..1);
+    }
+}
\ No newline at end of file
diff --git a/crates/dropbear-engine/src/pipelines/mod.rs b/crates/dropbear-engine/src/pipelines/mod.rs
index 9689e3e..13c0f4f 100644
--- a/crates/dropbear-engine/src/pipelines/mod.rs
+++ b/crates/dropbear-engine/src/pipelines/mod.rs
@@ -5,7 +5,7 @@ use crate::shader::Shader;
 pub mod shader;
 pub mod light_cube;
 pub mod globals;
-mod hdr;
+pub mod hdr;
 
 pub use globals::{Globals, GlobalsUniform};
 
@@ -21,4 +21,64 @@ pub trait DropbearShaderPipeline {
     fn pipeline_layout(&self) -> &wgpu::PipelineLayout;
     /// Fetches the pipeline
     fn pipeline(&self) -> &wgpu::RenderPipeline;
-}
\ No newline at end of file
+}
+
+fn create_render_pipeline(
+    label: Option<&str>,
+    device: &wgpu::Device,
+    layout: &wgpu::PipelineLayout,
+    color_format: wgpu::TextureFormat,
+    depth_format: Option<wgpu::TextureFormat>,
+    vertex_layouts: &[wgpu::VertexBufferLayout],
+    topology: wgpu::PrimitiveTopology,
+    shader: wgpu::ShaderModuleDescriptor,
+) -> wgpu::RenderPipeline {
+    let shader = device.create_shader_module(shader);
+
+    device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
+        label,
+        layout: Some(layout),
+        vertex: wgpu::VertexState {
+            module: &shader,
+            entry_point: Some("vs_main"),
+            buffers: vertex_layouts,
+            compilation_options: Default::default(),
+        },
+        fragment: Some(wgpu::FragmentState {
+            module: &shader,
+            entry_point: Some("fs_main"),
+            targets: &[Some(wgpu::ColorTargetState {
+                format: color_format,
+                blend: None,
+                write_mask: wgpu::ColorWrites::ALL,
+            })],
+            compilation_options: Default::default(),
+        }),
+        primitive: wgpu::PrimitiveState {
+            topology, // NEW!
+            strip_index_format: None,
+            front_face: wgpu::FrontFace::Ccw,
+            cull_mode: Some(wgpu::Face::Back),
+            // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE
+            polygon_mode: wgpu::PolygonMode::Fill,
+            // Requires Features::DEPTH_CLIP_CONTROL
+            unclipped_depth: false,
+            // Requires Features::CONSERVATIVE_RASTERIZATION
+            conservative: false,
+        },
+        depth_stencil: depth_format.map(|format| wgpu::DepthStencilState {
+            format,
+            depth_write_enabled: true,
+            depth_compare: wgpu::CompareFunction::LessEqual, // UDPATED!
+            stencil: wgpu::StencilState::default(),
+            bias: wgpu::DepthBiasState::default(),
+        }),
+        multisample: wgpu::MultisampleState {
+            count: 1,
+            mask: !0,
+            alpha_to_coverage_enabled: false,
+        },
+        cache: None,
+        multiview: None,
+    })
+}
diff --git a/crates/dropbear-engine/src/pipelines/shaders/hdr.wgsl b/crates/dropbear-engine/src/pipelines/shaders/hdr.wgsl
new file mode 100644
index 0000000..2dc5562
--- /dev/null
+++ b/crates/dropbear-engine/src/pipelines/shaders/hdr.wgsl
@@ -0,0 +1,55 @@
+// Maps HDR values to linear values
+// Based on http://www.oscars.org/science-technology/sci-tech-projects/aces
+fn aces_tone_map(hdr: vec3<f32>) -> vec3<f32> {
+    let m1 = mat3x3(
+        0.59719, 0.07600, 0.02840,
+        0.35458, 0.90834, 0.13383,
+        0.04823, 0.01566, 0.83777,
+    );
+    let m2 = mat3x3(
+        1.60475, -0.10208, -0.00327,
+        -0.53108,  1.10813, -0.07276,
+        -0.07367, -0.00605,  1.07602,
+    );
+    let v = m1 * hdr;
+    let a = v * (v + 0.0245786) - 0.000090537;
+    let b = v * (0.983729 * v + 0.4329510) + 0.238081;
+    return clamp(m2 * (a / b), vec3(0.0), vec3(1.0));
+}
+
+struct VertexOutput {
+    @location(0) uv: vec2<f32>,
+    @builtin(position) clip_position: vec4<f32>,
+};
+
+@vertex
+fn vs_main(
+    @builtin(vertex_index) vi: u32,
+) -> VertexOutput {
+    var out: VertexOutput;
+    // Generate a triangle that covers the whole screen
+    out.uv = vec2<f32>(
+        f32((vi << 1u) & 2u),
+        f32(vi & 2u),
+    );
+    out.clip_position = vec4<f32>(out.uv * 2.0 - 1.0, 0.0, 1.0);
+    // We need to invert the y coordinate so the image
+    // is not upside down
+    out.uv.y = 1.0 - out.uv.y;
+    return out;
+}
+
+@group(0)
+@binding(0)
+var hdr_image: texture_2d<f32>;
+
+@group(0)
+@binding(1)
+var hdr_sampler: sampler;
+
+@fragment
+fn fs_main(vs: VertexOutput) -> @location(0) vec4<f32> {
+    let hdr = textureSample(hdr_image, hdr_sampler, vs.uv);
+    let sdr = aces_tone_map(hdr.rgb);
+    return vec4(sdr, hdr.a);
+}
diff --git a/crates/dropbear-engine/src/texture.rs b/crates/dropbear-engine/src/texture.rs
index 5cae83c..8922af3 100644
--- a/crates/dropbear-engine/src/texture.rs
+++ b/crates/dropbear-engine/src/texture.rs
@@ -75,6 +75,71 @@ impl Texture {
     pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
     pub const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;
 
+    pub fn create_2d_texture(
+        device: &wgpu::Device,
+        width: u32,
+        height: u32,
+        format: wgpu::TextureFormat,
+        usage: wgpu::TextureUsages,
+        mag_filter: wgpu::FilterMode,
+        label: Option<&str>,
+    ) -> Self {
+        let size = wgpu::Extent3d {
+            width,
+            height,
+            depth_or_array_layers: 1,
+        };
+        Self::create_texture(
+            device,
+            label,
+            size,
+            format,
+            usage,
+            wgpu::TextureDimension::D2,
+            mag_filter,
+        )
+    }
+
+    pub fn create_texture(
+        device: &wgpu::Device,
+        label: Option<&str>,
+        size: wgpu::Extent3d,
+        format: wgpu::TextureFormat,
+        usage: wgpu::TextureUsages,
+        dimension: wgpu::TextureDimension,
+        mag_filter: wgpu::FilterMode,
+    ) -> Self {
+        let texture = device.create_texture(&wgpu::TextureDescriptor {
+            label,
+            size,
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension,
+            format,
+            usage,
+            view_formats: &[],
+        });
+
+        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
+        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
+            address_mode_u: wgpu::AddressMode::ClampToEdge,
+            address_mode_v: wgpu::AddressMode::ClampToEdge,
+            address_mode_w: wgpu::AddressMode::ClampToEdge,
+            mag_filter,
+            min_filter: wgpu::FilterMode::Nearest,
+            mipmap_filter: wgpu::FilterMode::Nearest,
+            ..Default::default()
+        });
+
+        Self {
+            label: label.and_then(|v| Some(v.to_string())),
+            texture,
+            view,
+            sampler,
+            size,
+        }
+    }
+
     /// Creates a new depth texture. This is an internal function.
     pub fn depth_texture(
         config: &wgpu::SurfaceConfiguration,