tirbofish/dropbear · commit
42723a07486b7f05d3840e71f9cb0353261081f4
fix: egui kept on dragging/highlighting all of my characters (which was really frustrating).
perf: improving performance and looks
wip: outline shader for selection (as if selection worked in the first place LMAOOOO)
refactor: removed old ColliderWireframePipeline code (since we changed the backend to use DebugDraw).
perf: changed compilation back to mold instead of lld to improve build times.
im so tired hopefully the tabuteau will be happy :/
Signature present but could not be verified.
Unverified
@@ -1,7 +1,8 @@ [target.x86_64-unknown-linux-gnu] linker = "clang" # rustflags = ["-C", "link-arg=-fuse-ld=mold", "-C", "relocation-model=pic"] -rustflags = ["-C", "link-arg=-fuse-ld=lld"] + rustflags = ["-C", "link-arg=-fuse-ld=mold"] +#rustflags = ["-C", "link-arg=-fuse-ld=lld"] # note: if you get a compile error such as "Recompile with RPIC", just run `cargo build` # instead of `cargo build -p eucalyptus-core -p eucalyptus-editor`. @@ -52,6 +52,7 @@ float-derive.workspace = true rkyv.workspace = true bevy_mikktspace.workspace = true wesl.workspace = true +egui_extras.workspace = true [target.'cfg(not(target_os = "android"))'.dependencies] rfd.workspace = true @@ -2,6 +2,9 @@ use std::marker::PhantomData; use bytemuck::NoUninit; +use dropbear_utils::Dirty; + +pub trait BufferType {} #[derive(Debug, Clone)] pub struct ResizableBuffer<T> { @@ -12,6 +15,8 @@ pub struct ResizableBuffer<T> { _marker: PhantomData<T>, } +impl<T> BufferType for ResizableBuffer<T> {} + impl<T: NoUninit> ResizableBuffer<T> { pub fn new( device: &wgpu::Device, @@ -79,13 +84,15 @@ impl<T: NoUninit> ResizableBuffer<T> { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct UniformBuffer<T> { buffer: wgpu::Buffer, label: String, _marker: PhantomData<T>, } +impl<T> BufferType for UniformBuffer<T> {} + impl<T: NoUninit> UniformBuffer<T> { pub fn new(device: &wgpu::Device, label: &str) -> Self { let size = (std::mem::size_of::<T>() as wgpu::BufferAddress).max(16); @@ -125,6 +132,8 @@ pub struct StorageBuffer<T> { _marker: PhantomData<T>, } +impl<T> BufferType for StorageBuffer<T> {} + impl<T: NoUninit> StorageBuffer<T> { pub fn new_read_only(device: &wgpu::Device, label: &str) -> Self { Self::new(device, label, true) @@ -200,3 +209,32 @@ impl<T: NoUninit> StorageBuffer<T> { queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(values)); } } + +#[derive(Clone, PartialEq)] +pub struct MutableDataBuffer<T> { + data: Dirty<T>, + pub buffer: UniformBuffer<T>, +} + +impl<T: NoUninit> MutableDataBuffer<T> { + pub fn new(data: Dirty<T>, buffer: UniformBuffer<T>) -> Self { + Self { + data, + buffer, + } + } + + pub fn write(&mut self, queue: &wgpu::Queue) { + if let Some(value) = self.data.get_if_dirty() { + self.buffer.write(queue, value); + } + } + + pub fn get_data(&self) -> &T { + self.data.get() + } + + pub fn set_data(&mut self, value: T) { + self.data.set(value); + } +} @@ -1,4 +1,4 @@ -use egui::Context; +use egui::{Context, FullOutput}; use egui_wgpu::wgpu::{CommandEncoder, Device, Queue, StoreOp, TextureFormat, TextureView}; use egui_wgpu::{Renderer, RendererOptions, ScreenDescriptor, wgpu}; use egui_winit::State; @@ -67,6 +67,58 @@ impl EguiRenderer { self.frame_started = true; } + pub fn take_input(&mut self, window: &Window) -> (egui::RawInput, egui::Context) { + egui_extras::install_image_loaders(self.state.egui_ctx()); + let raw_input = self.state.take_egui_input(window); + let ctx = self.state.egui_ctx().clone(); + (raw_input, ctx) + } + + pub fn process_output( + &mut self, + full_output: egui::FullOutput, + device: &Device, + queue: &Queue, + window: &Window, + window_surface_view: &TextureView, + screen_descriptor: ScreenDescriptor, + ) -> CommandEncoder { + let mut encoder = device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("egui render encoder"), + }); + + self.handle_full_output( + full_output, + device, + queue, + &mut encoder, + window, + window_surface_view, + screen_descriptor, + ); + + encoder + } + + pub fn run_ui( + &mut self, + run_ui: impl FnMut(&mut egui::Ui), + + device: &Device, + queue: &Queue, + window: &Window, + window_surface_view: &TextureView, + screen_descriptor: ScreenDescriptor, + ) -> CommandEncoder { + let (raw_input, ctx) = self.take_input(window); + self.frame_started = true; + let full_output = ctx.run_ui(raw_input, run_ui); + self.frame_started = false; + + self.process_output(full_output, device, queue, window, window_surface_view, screen_descriptor) + } + pub fn end_frame_and_draw( &mut self, device: &Device, @@ -77,14 +129,36 @@ impl EguiRenderer { screen_descriptor: ScreenDescriptor, ) { puffin::profile_function!(); + if !self.frame_started { panic!("begin_frame must be called before end_frame_and_draw can be called!"); } - self.ppp(screen_descriptor.pixels_per_point); - let full_output = self.state.egui_ctx().end_pass(); + self.handle_full_output( + full_output, + device, + queue, + encoder, + window, + window_surface_view, + screen_descriptor, + ); + } + + fn handle_full_output( + &mut self, + full_output: FullOutput, + device: &Device, + queue: &Queue, + encoder: &mut CommandEncoder, + window: &Window, + window_surface_view: &TextureView, + screen_descriptor: ScreenDescriptor, + ) { + self.ppp(screen_descriptor.pixels_per_point); + self.state .handle_platform_output(window, full_output.platform_output); @@ -38,12 +38,6 @@ pub struct SharedGraphicsContext { } impl SharedGraphicsContext { - pub fn get_egui_context(&self) -> Context { - self.egui_renderer.lock().context().clone() - } -} - -impl SharedGraphicsContext { pub(crate) fn from_state(state: &State) -> Self { SharedGraphicsContext { future_queue: state.future_queue.clone(), @@ -779,50 +779,57 @@ Hardware: } } - self.egui_renderer.lock().begin_frame(&self.window); + let mut window_commands = Vec::new(); - let mut scene_manager = std::mem::replace(&mut self.scene_manager, scene::Manager::new()); - let physics_dt = Duration::from_secs_f32(1.0 / PHYSICS_STEP_RATE as f32); - let frame_dt = Duration::from_secs_f32(previous_dt).min(Duration::from_millis(250)); - let mut physics_accumulator = self.physics_accumulator + frame_dt; + let (raw_input, egui_ctx) = self.egui_renderer.lock().take_input(&self.window); - let window_commands = { - let mut steps = 0usize; - while physics_accumulator >= physics_dt && steps < MAX_PHYSICS_STEPS_PER_FRAME { - scene_manager.physics_update(physics_dt.as_secs_f32(), graphics.clone()); - physics_accumulator -= physics_dt; - steps += 1; - } + let full_output = egui_ctx.run_ui(raw_input, |ui| { + let mut scene_manager = std::mem::replace(&mut self.scene_manager, scene::Manager::new()); - if steps == MAX_PHYSICS_STEPS_PER_FRAME && physics_accumulator >= physics_dt { - physics_accumulator = physics_accumulator.min(physics_dt); - } + let physics_dt = Duration::from_secs_f32(1.0 / PHYSICS_STEP_RATE as f32); + let frame_dt = Duration::from_secs_f32(previous_dt).min(Duration::from_millis(250)); + let mut physics_accumulator = self.physics_accumulator + frame_dt; - let commands = scene_manager.update(previous_dt, graphics.clone(), event_loop); - scene_manager.render(graphics.clone()); - commands - }; + let commands = { + let mut steps = 0usize; + while physics_accumulator >= physics_dt && steps < MAX_PHYSICS_STEPS_PER_FRAME { + scene_manager.physics_update(physics_dt.as_secs_f32(), graphics.clone(), ui); + physics_accumulator -= physics_dt; + steps += 1; + } - self.physics_accumulator = physics_accumulator; + if steps == MAX_PHYSICS_STEPS_PER_FRAME && physics_accumulator >= physics_dt { + physics_accumulator = physics_accumulator.min(physics_dt); + } - self.scene_manager = scene_manager; + let commands = scene_manager.update(previous_dt, graphics.clone(), event_loop, ui); + scene_manager.render(graphics.clone(), ui); + commands + }; - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("egui render encoder"), - }); + window_commands.extend(commands); - { - let hdr = self.hdr.read(); - hdr.process(&mut encoder, &self.viewport_texture.view); - } + self.physics_accumulator = physics_accumulator; + + self.scene_manager = scene_manager; + + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("egui render encoder"), + }); + + { + let hdr = self.hdr.read(); + hdr.process(&mut encoder, &self.viewport_texture.view); + } + }); - self.egui_renderer.lock().end_frame_and_draw( + let encoder = self.egui_renderer.lock().process_output( + full_output, &self.device, &self.queue, - &mut encoder, &self.window, &view, screen_descriptor, @@ -1,5 +1,5 @@ use crate::asset::{AssetRegistry, Handle}; -use crate::buffer::UniformBuffer; +use crate::buffer::{MutableDataBuffer, UniformBuffer}; use crate::texture::TextureBuilder; use crate::{ graphics::SharedGraphicsContext, @@ -16,10 +16,12 @@ use serde::{Deserialize, Serialize}; use std::hash::{DefaultHasher, Hash, Hasher}; use std::sync::Arc; use std::{mem, ops::Range}; +use std::cmp::PartialEq; use wgpu::{ BufferAddress, FilterMode, MipmapFilterMode, VertexAttribute, VertexBufferLayout, util::DeviceExt, }; +use dropbear_utils::Dirty; // note to self: do not derive clone otherwise it wil take too much memory // #[derive(Clone)] @@ -49,28 +51,39 @@ pub struct Mesh { pub morph_default_weights: Vec<f32>, } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub struct Material { pub name: String, + pub texture_tag: Option<String>, + pub diffuse_texture: Handle<Texture>, - pub normal_texture: Option<Handle<Texture>>, - pub emissive_texture: Option<Handle<Texture>>, + pub base_colour: [f32; 4], + pub metallic_roughness_texture: Option<Handle<Texture>>, - pub occlusion_texture: Option<Handle<Texture>>, - pub tint: [f32; 4], - pub emissive_factor: [f32; 3], pub metallic_factor: f32, pub roughness_factor: f32, - pub alpha_mode: AlphaMode, - pub alpha_cutoff: Option<f32>, - pub double_sided: bool, - pub occlusion_strength: f32, + + pub normal_texture: Option<Handle<Texture>>, pub normal_scale: f32, + + pub emissive_texture: Option<Handle<Texture>>, + pub emissive_factor: [f32; 3], + pub emissive_strength: f32, + + pub occlusion_texture: Option<Handle<Texture>>, + pub occlusion_strength: f32, + pub uv_tiling: [f32; 2], - pub tint_buffer: UniformBuffer<MaterialUniform>, - pub bind_group: wgpu::BindGroup, - pub texture_tag: Option<String>, + pub uv_offset: [f32; 2], pub wrap_mode: TextureWrapMode, + + pub alpha_mode: AlphaMode, + pub alpha_cutoff: Option<f32>, + + pub uniform: UniformBuffer<MaterialUniform>, + pub bind_group: wgpu::BindGroup, + + // pub double_sided: bool, // not sure as to why its here } #[derive( @@ -86,11 +99,12 @@ pub struct Material { rkyv::Deserialize, Default, )] +#[repr(u32)] pub enum AlphaMode { #[default] Opaque = 1, - Mask, - Blend, + Mask = 2, + Blend = 3, } impl Into<AlphaMode> for gltf::material::AlphaMode { @@ -332,22 +346,32 @@ impl Material { let name = name.into(); let uv_tiling = [1.0, 1.0]; + let uniform = MaterialUniform { base_colour: tint, - emissive: [0.0, 0.0, 0.0], - emissive_strength: 1.0, - metallic: 1.0, - roughness: 1.0, + + metallic_factor: 1.0, + roughness_factor: 1.0, + normal_scale: 1.0, + + _padding: 0.0, + emissive_factor: [0.0, 0.0, 0.0], + emissive_strength: 1.0, + occlusion_strength: 1.0, - alpha_cutoff: 0.5, + + _padding2: 0.0, uv_tiling, + uv_offset: [0.0; 2], + + alpha_cutoff: 0.5, + alpha_mode: AlphaMode::default() as u32, + has_normal_texture: normal_texture.is_some() as u32, has_emissive_texture: emissive_texture.is_some() as u32, has_metallic_texture: metallic_roughness_texture.is_some() as u32, has_occlusion_texture: occlusion_texture.is_some() as u32, - pad: 0, - _pad: 0, }; let tint_buffer = UniformBuffer::new(&graphics.device, "material_tint_uniform"); @@ -367,17 +391,18 @@ impl Material { name, diffuse_texture, normal_texture, - tint, + base_colour: tint, emissive_factor: [0.0, 0.0, 0.0], + emissive_strength: 1.0, metallic_factor: 1.0, roughness_factor: 1.0, alpha_mode: AlphaMode::Opaque, alpha_cutoff: None, - double_sided: false, occlusion_strength: 1.0, normal_scale: 1.0, uv_tiling, - tint_buffer, + uv_offset: [0.0; 2], + uniform: tint_buffer, bind_group, texture_tag, wrap_mode: TextureWrapMode::Repeat, @@ -387,26 +412,28 @@ impl Material { } } - pub fn sync_uniform(&self, graphics: &SharedGraphicsContext) { + pub fn sync_uniform(&self, graphics: &SharedGraphicsContext) { let uniform = MaterialUniform { - base_colour: self.tint, - emissive: self.emissive_factor, - emissive_strength: 1.0, - metallic: self.metallic_factor, - roughness: self.roughness_factor, + base_colour: self.base_colour, + emissive_factor: self.emissive_factor, + emissive_strength: self.emissive_strength, + metallic_factor: self.metallic_factor, + roughness_factor: self.roughness_factor, normal_scale: self.normal_scale, occlusion_strength: self.occlusion_strength, alpha_cutoff: self.alpha_cutoff.unwrap_or(0.5), + alpha_mode: self.alpha_mode as u32, uv_tiling: self.uv_tiling, + uv_offset: self.uv_offset, + has_normal_texture: self.normal_texture.is_some() as u32, has_emissive_texture: self.emissive_texture.is_some() as u32, has_metallic_texture: self.metallic_roughness_texture.is_some() as u32, has_occlusion_texture: self.occlusion_texture.is_some() as u32, - pad: 0, - has_normal_texture: self.normal_texture.is_some() as u32, - _pad: 0, + _padding: 0.0, + _padding2: 0.0, }; - self.tint_buffer.write(&graphics.queue, &uniform); + self.uniform.write(&graphics.queue, &uniform); } pub fn rebuild_bind_group( @@ -417,7 +444,7 @@ impl Material { self.bind_group = Self::build_bind_group( registry, graphics, - &self.tint_buffer, + &self.uniform, self.diffuse_texture, self.normal_texture, self.emissive_texture, @@ -607,7 +634,6 @@ struct GLTFMaterialInformation { roughness_factor: f32, alpha_mode: gltf::material::AlphaMode, alpha_cutoff: Option<f32>, - double_sided: bool, occlusion_strength: f32, normal_scale: f32, } @@ -625,7 +651,6 @@ struct ProcessedMaterialTextures { roughness_factor: f32, alpha_mode: gltf::material::AlphaMode, alpha_cutoff: Option<f32>, - double_sided: bool, occlusion_strength: f32, normal_scale: f32, } @@ -736,7 +761,6 @@ impl Model { let roughness_factor = pbr.roughness_factor(); let alpha_mode = material.alpha_mode(); let alpha_cutoff = material.alpha_cutoff(); - let double_sided = material.double_sided(); let occlusion_strength = occlusion_texture .as_ref() .map(|info| info.strength()) @@ -759,7 +783,6 @@ impl Model { roughness_factor, alpha_mode, alpha_cutoff, - double_sided, occlusion_strength, normal_scale, }); @@ -779,7 +802,6 @@ impl Model { roughness_factor: 1.0, alpha_mode: gltf::material::AlphaMode::Opaque, alpha_cutoff: None, - double_sided: false, occlusion_strength: 1.0, normal_scale: 1.0, }); @@ -1267,7 +1289,6 @@ impl Model { let roughness_factor = material_info.roughness_factor; let alpha_mode = material_info.alpha_mode; let alpha_cutoff = material_info.alpha_cutoff; - let double_sided = material_info.double_sided; let occlusion_strength = material_info.occlusion_strength; let normal_scale = material_info.normal_scale; @@ -1284,7 +1305,6 @@ impl Model { roughness_factor, alpha_mode, alpha_cutoff, - double_sided, occlusion_strength, normal_scale, } @@ -1363,7 +1383,6 @@ impl Model { material.roughness_factor = processed.roughness_factor; material.alpha_mode = processed.alpha_mode.into(); material.alpha_cutoff = processed.alpha_cutoff; - material.double_sided = processed.double_sided; material.occlusion_strength = processed.occlusion_strength; material.normal_scale = processed.normal_scale; material.sync_uniform(&graphics); @@ -1824,21 +1843,32 @@ impl Hash for ModelVertex { } #[repr(C)] -#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +#[derive(Copy, Clone, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] pub struct MaterialUniform { - pub base_colour: [f32; 4], // colour - pub emissive: [f32; 3], - pub emissive_strength: f32, - pub metallic: f32, - pub roughness: f32, + pub base_colour: [f32; 4], + + pub metallic_factor: f32, + pub roughness_factor: f32, + pub normal_scale: f32, + + pub _padding: f32, // 4 byte padding to align emissive_factor to offset 32 + + pub emissive_factor: [f32; 3], + pub emissive_strength: f32, + pub occlusion_strength: f32, - pub alpha_cutoff: f32, - pub _pad: u32, + + pub _padding2: f32, // 4 byte padding to align uv_tiling to offset 56 + pub uv_tiling: [f32; 2], + pub uv_offset: [f32; 2], + + pub alpha_cutoff: f32, + pub alpha_mode: u32, + pub has_normal_texture: u32, pub has_emissive_texture: u32, pub has_metallic_texture: u32, pub has_occlusion_texture: u32, - pub pad: u32, -} +} @@ -0,0 +1,261 @@ +use std::num::NonZeroU32; + +use anyhow::*; +use crate::model::Vertex; + +pub struct RenderPipelineBuilder<'a> { + layout: Option<&'a wgpu::PipelineLayout>, + vertex_shader: Option<wgpu::ShaderModuleDescriptor<'a>>, + fragment_shader: Option<wgpu::ShaderModuleDescriptor<'a>>, + front_face: wgpu::FrontFace, + cull_mode: Option<wgpu::Face>, + depth_bias: i32, + depth_bias_slope_scale: f32, + depth_bias_clamp: f32, + primitive_topology: wgpu::PrimitiveTopology, + color_states: Vec<Option<wgpu::ColorTargetState>>, + depth_stencil: Option<wgpu::DepthStencilState>, + index_format: wgpu::IndexFormat, + vertex_buffers: Vec<wgpu::VertexBufferLayout<'a>>, + sample_count: u32, + sample_mask: u64, + alpha_to_coverage_enabled: bool, + multiview_mask: Option<NonZeroU32>, + maybe_fragment_entry_point: Option<&'a str>, + maybe_vertex_entry_point: Option<&'a str>, +} + +impl<'a> RenderPipelineBuilder<'a> { + pub fn new() -> Self { + Self { + layout: None, + vertex_shader: None, + fragment_shader: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: Vec::new(), + depth_stencil: None, + index_format: wgpu::IndexFormat::Uint32, + vertex_buffers: Vec::new(), + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + multiview_mask: None, + maybe_vertex_entry_point: None, + maybe_fragment_entry_point: None, + } + } + + pub fn layout(&mut self, layout: &'a wgpu::PipelineLayout) -> &mut Self { + self.layout = Some(layout); + self + } + + pub fn vertex_shader(&mut self, src: wgpu::ShaderModuleDescriptor<'a>) -> &mut Self { + self.vertex_shader = Some(src); + self + } + + pub fn vertex_entry_point(&mut self, entry_point: &'a str) -> &mut Self { + self.maybe_vertex_entry_point = Some(entry_point); + self + } + + pub fn fragment_shader(&mut self, src: wgpu::ShaderModuleDescriptor<'a>) -> &mut Self { + self.fragment_shader = Some(src); + self + } + + pub fn fragment_entry_point(&mut self, entry_point: &'a str) -> &mut Self { + self.maybe_fragment_entry_point = Some(entry_point); + self + } + + #[allow(dead_code)] + pub fn front_face(&mut self, ff: wgpu::FrontFace) -> &mut Self { + self.front_face = ff; + self + } + + #[allow(dead_code)] + pub fn cull_mode(&mut self, cm: Option<wgpu::Face>) -> &mut Self { + self.cull_mode = cm; + self + } + + #[allow(dead_code)] + pub fn depth_bias(&mut self, db: i32) -> &mut Self { + self.depth_bias = db; + self + } + + #[allow(dead_code)] + pub fn depth_bias_slope_scale(&mut self, dbss: f32) -> &mut Self { + self.depth_bias_slope_scale = dbss; + self + } + + #[allow(dead_code)] + pub fn depth_bias_clamp(&mut self, dbc: f32) -> &mut Self { + self.depth_bias_clamp = dbc; + self + } + + #[allow(dead_code)] + pub fn primitive_topology(&mut self, pt: wgpu::PrimitiveTopology) -> &mut Self { + self.primitive_topology = pt; + self + } + + pub fn color_state(&mut self, cs: wgpu::ColorTargetState) -> &mut Self { + self.color_states.push(Some(cs)); + self + } + + /// Helper method for [RenderPipelineBuilder::color_state] + pub fn color_solid(&mut self, format: wgpu::TextureFormat) -> &mut Self { + self.color_state(wgpu::ColorTargetState { + format, + blend: None, + write_mask: wgpu::ColorWrites::ALL, + }) + } + + pub fn depth_stencil(&mut self, dss: wgpu::DepthStencilState) -> &mut Self { + self.depth_stencil = Some(dss); + self + } + + /// Helper method for [RenderPipelineBuilder::depth_stencil] + pub fn depth_no_stencil( + &mut self, + format: wgpu::TextureFormat, + depth_write_enabled: bool, + depth_compare: wgpu::CompareFunction, + ) -> &mut Self { + self.depth_stencil(wgpu::DepthStencilState { + format, + depth_write_enabled: Some(depth_write_enabled), + depth_compare: Some(depth_compare), + stencil: Default::default(), + bias: wgpu::DepthBiasState::default(), + }) + } + + /// Helper method for [RenderPipelineBuilder::depth_no_stencil] + pub fn depth_format(&mut self, format: wgpu::TextureFormat) -> &mut Self { + self.depth_no_stencil(format, true, wgpu::CompareFunction::Less) + } + + #[allow(dead_code)] + pub fn index_format(&mut self, ifmt: wgpu::IndexFormat) -> &mut Self { + self.index_format = ifmt; + self + } + + pub fn vertex_buffer<V: Vertex>(&mut self) -> &mut Self { + self.vertex_buffers.push(V::desc()); + self + } + + pub fn vertex_buffer_desc(&mut self, vb: wgpu::VertexBufferLayout<'a>) -> &mut Self { + self.vertex_buffers.push(vb); + self + } + + #[allow(dead_code)] + pub fn sample_count(&mut self, sc: u32) -> &mut Self { + self.sample_count = sc; + self + } + + #[allow(dead_code)] + pub fn sample_mask(&mut self, sm: u64) -> &mut Self { + self.sample_mask = sm; + self + } + + #[allow(dead_code)] + pub fn alpha_to_coverage_enabled(&mut self, atce: bool) -> &mut Self { + self.alpha_to_coverage_enabled = atce; + self + } + + pub fn multiview(&mut self, value: Option<NonZeroU32>) -> &mut Self { + self.multiview_mask = value; + self + } + + pub fn build(&mut self, device: &wgpu::Device) -> Result<wgpu::RenderPipeline> { + let layout = self.layout.clone(); + + // Render pipelines always have a vertex shader, but due + // to the way the builder pattern works, we can't + // guarantee that the user will specify one, so we'll + // just return an error if they forgot. + // + // We could supply a default one, but a "default" vertex + // could take on many forms. An error is much more + // explicit. + if self.vertex_shader.is_none() { + bail!("No vertex shader supplied!") + } + let vs = create_shader_module( + device, + self.vertex_shader + .take() + .context("Please include a vertex shader")?, + ); + + let frag_module = self + .fragment_shader + .clone() + .map(|src| create_shader_module(device, src)); + let frag_state = frag_module.as_ref().map(|module| wgpu::FragmentState { + module, + entry_point: self.maybe_fragment_entry_point, + compilation_options: Default::default(), + targets: &self.color_states, + }); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout, + vertex: wgpu::VertexState { + module: &vs, + entry_point: self.maybe_vertex_entry_point, + buffers: &self.vertex_buffers, + compilation_options: Default::default(), + }, + fragment: frag_state, + primitive: wgpu::PrimitiveState { + topology: self.primitive_topology, + front_face: self.front_face, + cull_mode: self.cull_mode, + strip_index_format: None, + polygon_mode: wgpu::PolygonMode::Fill, + ..Default::default() + }, + depth_stencil: self.depth_stencil.clone(), + multisample: wgpu::MultisampleState { + count: self.sample_count, + mask: self.sample_mask, + alpha_to_coverage_enabled: self.alpha_to_coverage_enabled, + }, + multiview_mask: self.multiview_mask, + cache: None, + }); + Ok(pipeline) + } +} + +fn create_shader_module( + device: &wgpu::Device, + spirv: wgpu::ShaderModuleDescriptor, +) -> wgpu::ShaderModule { + device.create_shader_module(spirv) +} @@ -7,6 +7,7 @@ pub mod hdr; pub mod light_cube; pub mod shader; pub mod animation; +pub mod builder; pub use globals::{Globals, GlobalsUniform}; @@ -10,12 +10,13 @@ use crate::multisampling::AntiAliasingMode; use crate::{WindowData, graphics::SharedGraphicsContext, input}; use parking_lot::RwLock; use std::{collections::HashMap, rc::Rc, sync::Arc}; +use egui::Ui; pub trait Scene { - fn load(&mut self, graphics: Arc<SharedGraphicsContext>); - fn physics_update(&mut self, dt: f32, graphics: Arc<SharedGraphicsContext>); - fn update(&mut self, dt: f32, graphics: Arc<SharedGraphicsContext>); - fn render<'a>(&mut self, graphics: Arc<SharedGraphicsContext>); + fn load(&mut self, graphics: Arc<SharedGraphicsContext>, ui: &mut Ui); + fn physics_update(&mut self, dt: f32, graphics: Arc<SharedGraphicsContext>, ui: &mut Ui); + fn update(&mut self, dt: f32, graphics: Arc<SharedGraphicsContext>, ui: &mut Ui); + fn render<'a>(&mut self, graphics: Arc<SharedGraphicsContext>, ui: &mut Ui); fn exit(&mut self, event_loop: &ActiveEventLoop); fn handle_event(&mut self, _event: &WindowEvent) {} /// By far a mess of a trait however it works. @@ -95,6 +96,7 @@ impl Manager { dt: f32, graphics: Arc<SharedGraphicsContext>, event_loop: &ActiveEventLoop, + ui: &mut Ui, ) -> Vec<SceneCommand> { puffin::profile_function!(); // transition scene @@ -110,7 +112,7 @@ impl Manager { if let Some(scene) = self.scenes.get_mut(&next_scene_name) { { puffin::profile_scope!("load new scene", &next_scene_name); - scene.write().load(graphics.clone()); + scene.write().load(graphics.clone(), ui); } } self.current_scene = Some(next_scene_name); @@ -122,7 +124,7 @@ impl Manager { { { puffin::profile_scope!("update new scene", &scene_name); - scene.write().update(dt, graphics.clone()); + scene.write().update(dt, graphics.clone(), ui); } let command = scene.write().run_command(); match command { @@ -133,7 +135,7 @@ impl Manager { if let Some(scene) = self.scenes.get_mut(current) { puffin::profile_scope!("reload the scene", ¤t); scene.write().exit(event_loop); - scene.write().load(graphics.clone()); + scene.write().load(graphics.clone(), ui); log::debug!("Reloaded scene: {}", current); } @@ -162,23 +164,23 @@ impl Manager { Vec::new() } - pub fn physics_update(&mut self, dt: f32, graphics: Arc<SharedGraphicsContext>) { + pub fn physics_update(&mut self, dt: f32, graphics: Arc<SharedGraphicsContext>, ui: &mut Ui) { puffin::profile_function!(); if let Some(scene_name) = &self.current_scene && let Some(scene) = self.scenes.get_mut(scene_name) { puffin::profile_scope!("physics-update the scene", &scene_name); - scene.write().physics_update(dt, graphics.clone()) + scene.write().physics_update(dt, graphics.clone(), ui) } } - pub fn render<'a>(&mut self, graphics: Arc<SharedGraphicsContext>) { + pub fn render<'a>(&mut self, graphics: Arc<SharedGraphicsContext>, ui: &mut Ui) { puffin::profile_function!(); if let Some(scene_name) = &self.current_scene && let Some(scene) = self.scenes.get_mut(scene_name) { puffin::profile_scope!("render the scene", &scene_name); - scene.write().render(graphics.clone()) + scene.write().render(graphics.clone(), ui) } } @@ -54,8 +54,6 @@ impl Shader { log::debug!("Created new shaders under the label: {:?}", label); - CompiledSlangShader::from_bytes("light cube", slank::include_slang!("light_cube")); - Self { label: match label { Some(label) => label.into(), @@ -0,0 +1,109 @@ +import super::super::shader::u_globals; + +@group(0) @binding(2) var<storage, read> s_light_array: array<Light>; + +struct Light { + position: vec4<f32>, + direction: vec4<f32>, // x, y, z, outer_cutoff_angle + color: vec4<f32>, // r, g, b, light_type (0=directional, 1=point, 2=spot) + constant: f32, + lin: f32, + quadratic: f32, + cutoff: f32, // inner cutoff (cos of angle) +} + +const LIGHT_DIRECTIONAL: u32 = 0u; +const LIGHT_POINT: u32 = 1u; +const LIGHT_SPOT: u32 = 2u; + +fn diffuse( + light_dir: vec3<f32>, + normal: vec3<f32>, + light_color: vec3<f32>, + object_color: vec3<f32>, +) -> vec3<f32> { + let diff = max(dot(normal, light_dir), 0.0); + return diff * light_color * object_color; +} + +// Blinn-Phong specular +fn specular( + light_dir: vec3<f32>, + normal: vec3<f32>, + view_dir: vec3<f32>, + light_color: vec3<f32>, + shininess: f32, +) -> vec3<f32> { + let halfway = normalize(light_dir + view_dir); + let spec = pow(max(dot(normal, halfway), 0.0), shininess); + return light_color * spec; +} + +fn attenuation(light: Light, world_pos: vec3<f32>) -> f32 { + let d = length(light.position.xyz - world_pos); + return 1.0 / (light.constant + light.lin * d + light.quadratic * d * d); +} + +fn calculate_light( + light: Light, + world_pos: vec3<f32>, + normal: vec3<f32>, // normalised world-space surface normal + view_dir: vec3<f32>, // normalised direction from fragment to camera + object_color: vec3<f32>, + shininess: f32, + spec_scale: f32, +) -> vec3<f32> { + let light_type = u32(light.color.w); + let light_color = light.color.xyz; + + var light_dir: vec3<f32>; + var atten: f32 = 1.0; + var intensity: f32 = 1.0; + + if light_type == LIGHT_DIRECTIONAL { + light_dir = normalize(-light.direction.xyz); + } else { + // point and spot included + light_dir = normalize(light.position.xyz - world_pos); + atten = attenuation(light, world_pos); + + if light_type == LIGHT_SPOT { + // smooth edge + let theta = dot(light_dir, normalize(-light.direction.xyz)); + let inner_cutoff = light.cutoff; + let outer_cutoff = light.direction.w; + let epsilon = inner_cutoff - outer_cutoff; + intensity = clamp((theta - outer_cutoff) / epsilon, 0.0, 1.0); + } + } + + let d = diffuse(light_dir, normal, light_color, object_color) * intensity; + let s = specular(light_dir, normal, view_dir, light_color, shininess) * intensity * spec_scale; + + return (d + s) * atten; +} + + +fn calculate_lighting( + world_pos: vec3<f32>, + normal: vec3<f32>, + view_dir: vec3<f32>, + object_color: vec3<f32>, + shininess: f32, + spec_scale: f32, +) -> vec3<f32> { + var result = u_globals.ambient_strength * object_color; + let num_lights = u_globals.num_lights; + for (var i = 0u; i < num_lights; i++) { + result += calculate_light( + s_light_array[i], + world_pos, + normal, + view_dir, + object_color, + shininess, + spec_scale, + ); + } + return result; +} @@ -1,20 +1,32 @@ struct MaterialUniform { - base_colour: vec4<f32>, - emissive: vec3<f32>, + base_colour: vec4<f32>, + + metallic: f32, + roughness: f32, + + normal_scale: f32, + + emissive_factor: vec3<f32>, emissive_strength: f32, - metallic: f32, - roughness: f32, - normal_scale: f32, + occlusion_strength: f32, - alpha_cutoff: f32, - uv_tiling: vec2<f32>, - has_normal_texture: u32, + uv_tiling: vec2<f32>, + uv_offset: vec2<f32>, + + alpha_cutoff: f32, + alpha_mode: u32, + + has_normal_texture: u32, has_emissive_texture: u32, has_metallic_texture: u32, has_occlusion_texture: u32, } +const ALPHAMODE_OPAQUE: u32 = 1; +const ALPHAMODE_MASK: u32 = 2; +const ALPHAMODE_BLEND: u32 = 3; + @group(1) @binding(0) var<uniform> u_material: MaterialUniform; @group(1) @binding(1) var t_diffuse: texture_2d<f32>; @group(1) @binding(2) var s_diffuse: sampler; @@ -0,0 +1,39 @@ +struct VertexOutput { + @builtin(position) clip_position: vec4<f32>, + @location(0) uv: vec2<f32>, +} + +@group(0) @binding(0) +var s_mask: sampler; +@group(0) @binding(1) +var t_mask: texture_2d<f32>; + +@vertex +fn vs_main( + @builtin(vertex_index) in_vertex_index: u32, +) -> VertexOutput { + var out: VertexOutput; + + // draw fullscreen triangle + let x = f32((in_vertex_index << 1u) & 2u); + let y = f32(in_vertex_index & 2u); + out.clip_position = vec4<f32>(x * 2.0 - 1.0, y * 2.0 - 1.0, 0.0, 1.0); + out.uv = vec2<f32>(x, 1.0-y); + return out; +} + +@fragment +fn fs_mask(in: VertexOutput) { + let sample = textureSample(t_mask, s_mask, in.uv); + // We invert this check so that the mask will render objects in + // the center + if (sample.a > 0.1) { + discard; + } +} + +@fragment +fn fs_colour(in: VertexOutput) -> @location(0) vec4<f32> { + let sample = textureSample(t_mask, s_mask, in.uv); + return sample; +} @@ -1,6 +1,7 @@ import super::common::animation; import super::common::material; import super::common::environment; +import super::common::light; struct Globals { num_lights: u32, @@ -15,20 +16,10 @@ struct CameraUniform { inv_view: mat4x4<f32>, } -struct Light { - position: vec4<f32>, - direction: vec4<f32>, // x, y, z, outer_cutoff_angle - color: vec4<f32>, // r, g, b, light_type (0=directional, 1=point, 2=spot) - constant: f32, - lin: f32, - quadratic: f32, - cutoff: f32, -} // per-frame @group(0) @binding(0) var<uniform> u_globals: Globals; @group(0) @binding(1) var<uniform> u_camera: CameraUniform; -@group(0) @binding(2) var<storage, read> s_light_array: array<Light>; struct InstanceInput { @location(8) model_matrix_0: vec4<f32>, @@ -64,7 +55,6 @@ struct VertexOutput { }; - @vertex fn vs_main(model: VertexInput, instance: InstanceInput) -> VertexOutput { let model_matrix = mat4x4<f32>( @@ -106,7 +96,53 @@ fn vs_main(model: VertexInput, instance: InstanceInput) -> VertexOutput { @fragment fn s_fs_main(in: VertexOutput) -> @location(0) vec4<f32> { - let uv = in.tex_coords * material::u_material.uv_tiling; - - return textureSample(material::t_diffuse, material::s_diffuse, uv); + let uv = in.tex_coords * material::u_material.uv_tiling + material::u_material.uv_offset; + + let albedo = textureSample(material::t_diffuse, material::s_diffuse, uv) + * material::u_material.base_colour; + + if material::u_material.alpha_mode == material::ALPHAMODE_MASK && albedo.a < material::u_material.alpha_cutoff { + discard; + } + + // normal + var N = normalize(in.world_normal); + if material::u_material.has_normal_texture != 0u { + let tbn = mat3x3<f32>( + normalize(in.world_tangent), + normalize(in.world_bitangent), + N, + ); + let n_sample = textureSample(material::t_normal, material::s_normal, uv).xyz; + let n_ts = (n_sample * 2.0 - 1.0) * vec3<f32>(material::u_material.normal_scale, material::u_material.normal_scale, 1.0); + N = normalize(tbn * n_ts); + } + + // occlusion + var occlusion = 1.0; + if material::u_material.has_occlusion_texture != 0u { + let occ_sample = textureSample(material::t_occlusion, material::s_occlusion, uv).r; + occlusion = 1.0 + material::u_material.occlusion_strength * (occ_sample - 1.0); + } + + let roughness = material::u_material.roughness; + let shininess = max(2.0 / max(roughness * roughness, 0.001) - 2.0, 2.0); + let spec_scale = (1.0 - roughness) * (1.0 - roughness); + + let view_dir = normalize(in.world_view_position - in.world_position); + let lit = light::calculate_lighting( + in.world_position, + N, + view_dir, + albedo.rgb, + shininess, + spec_scale, + ) * occlusion; + + var emissive = material::u_material.emissive_factor * material::u_material.emissive_strength; + if material::u_material.has_emissive_texture != 0u { + emissive *= textureSample(material::t_emissive, material::s_emissive, uv).rgb; + } + + return vec4<f32>(lit + emissive, albedo.a); } @@ -1,4 +1,4 @@ -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default, PartialEq)] pub struct Dirty<T> { value: T, dirty: bool, @@ -52,8 +52,8 @@ impl<T> Dirty<T> { self.dirty = true; } - /// Takes the value and clears the dirty flag if dirty, returning None if clean. - pub fn take_if_dirty(&mut self) -> Option<&T> { + /// Fetches the value and clears the dirty flag if dirty, returning None if clean. + pub fn get_if_dirty(&mut self) -> Option<&T> { if self.dirty { self.dirty = false; Some(&self.value) @@ -115,7 +115,6 @@ pub struct NMaterial { pub metallic_factor: f32, pub roughness_factor: f32, pub alpha_cutoff: Option<f32>, - pub double_sided: bool, pub occlusion_strength: f32, pub normal_scale: f32, pub uv_tiling: NVector2, @@ -161,7 +160,6 @@ impl ToJObject for NMaterial { JValue::Double(self.metallic_factor as f64), JValue::Double(self.roughness_factor as f64), JValue::Object(&alpha_cutoff), - JValue::Bool(self.double_sided), JValue::Double(self.occlusion_strength as f64), JValue::Double(self.normal_scale as f64), JValue::Object(&uv_tiling), @@ -181,7 +179,6 @@ impl ToJObject for NMaterial { com.dropbear.math.Vector3d, double, double, java.lang.Double, - boolean, double, double, com.dropbear.math.Vector2d, com.dropbear.asset.Texture, @@ -542,12 +539,11 @@ fn map_material( name: material.name.clone(), diffuse_texture: material.diffuse_texture.id, normal_texture: material.normal_texture.and_then(|v| Some(v.id)), - tint: NVector4::from(material.tint), + tint: NVector4::from(material.base_colour), emissive_factor: NVector3::from(material.emissive_factor), metallic_factor: material.metallic_factor, roughness_factor: material.roughness_factor, alpha_cutoff: material.alpha_cutoff, - double_sided: material.double_sided, occlusion_strength: material.occlusion_strength, normal_scale: material.normal_scale, uv_tiling: NVector2::from(material.uv_tiling), @@ -900,13 +900,12 @@ impl Component for MeshRenderer { for (label, m) in &ser.texture_override { if let Some(mat) = renderer.material_snapshot.get_mut(&label.clone()) { - mat.tint = m.tint; + mat.base_colour = m.tint; mat.emissive_factor = m.emissive_factor; mat.metallic_factor = m.metallic_factor; mat.roughness_factor = m.roughness_factor; mat.alpha_mode = m.alpha_mode; mat.alpha_cutoff = m.alpha_cutoff; - mat.double_sided = m.double_sided; mat.occlusion_strength = m.occlusion_strength; mat.normal_scale = m.normal_scale; mat.uv_tiling = m.uv_tiling; @@ -1124,13 +1123,12 @@ impl Component for MeshRenderer { SerializedMaterialCustomisation { label: label.clone(), diffuse_texture, - tint: mat.tint, + tint: mat.base_colour, emissive_factor: mat.emissive_factor, metallic_factor: mat.metallic_factor, roughness_factor: mat.roughness_factor, alpha_mode: mat.alpha_mode, alpha_cutoff: mat.alpha_cutoff, - double_sided: mat.double_sided, occlusion_strength: mat.occlusion_strength, normal_scale: mat.normal_scale, uv_tiling: mat.uv_tiling, @@ -1506,18 +1504,20 @@ impl InspectableComponent for MeshRenderer { apply_cuboid(self, size, false); for (name, saved_mat) in saved_materials { if let Some(mat) = self.material_snapshot.get_mut(&name) { - mat.tint = saved_mat.tint; + mat.base_colour = saved_mat.base_colour; mat.emissive_factor = saved_mat.emissive_factor; + mat.emissive_strength = saved_mat.emissive_strength; mat.metallic_factor = saved_mat.metallic_factor; mat.roughness_factor = saved_mat.roughness_factor; mat.alpha_mode = saved_mat.alpha_mode; mat.alpha_cutoff = saved_mat.alpha_cutoff; - mat.double_sided = saved_mat.double_sided; mat.occlusion_strength = saved_mat.occlusion_strength; mat.normal_scale = saved_mat.normal_scale; mat.uv_tiling = saved_mat.uv_tiling; + mat.uv_offset = saved_mat.uv_offset; mat.wrap_mode = saved_mat.wrap_mode; mat.texture_tag = saved_mat.texture_tag.clone(); + mat.sync_uniform(&graphics); } } } @@ -1630,16 +1630,17 @@ impl InspectableComponent for MeshRenderer { material.metallic_roughness_texture = default.metallic_roughness_texture; material.occlusion_texture = default.occlusion_texture; - material.tint = default.tint; + material.base_colour = default.base_colour; material.emissive_factor = default.emissive_factor; + material.emissive_strength = default.emissive_strength; material.metallic_factor = default.metallic_factor; material.roughness_factor = default.roughness_factor; material.alpha_mode = default.alpha_mode; material.alpha_cutoff = default.alpha_cutoff; - material.double_sided = default.double_sided; material.occlusion_strength = default.occlusion_strength; material.normal_scale = default.normal_scale; material.uv_tiling = default.uv_tiling; + material.uv_offset = default.uv_offset; material.texture_tag = default.texture_tag.clone(); material.wrap_mode = default.wrap_mode; { @@ -1651,207 +1652,13 @@ impl InspectableComponent for MeshRenderer { } }); - ui.add_space(4.0); - ui.label( - RichText::new("Colors") - .strong() - .color(ui.visuals().text_color()), - ); - Grid::new(format!("material_colors_{}", material_name)) - .num_columns(2) - .spacing([12.0, 6.0]) - .striped(true) - .show(ui, |ui| { - ui.label("Tint"); - let mut tint_rgb = - [material.tint[0], material.tint[1], material.tint[2]]; - if egui::color_picker::color_edit_button_rgb( - ui, - &mut tint_rgb, - ) - .changed() - { - material.tint[0] = tint_rgb[0]; - material.tint[1] = tint_rgb[1]; - material.tint[2] = tint_rgb[2]; - } - ui.end_row(); - - ui.label("Emissive"); - let mut emissive_rgb = [ - material.emissive_factor[0], - material.emissive_factor[1], - material.emissive_factor[2], - ]; - if egui::color_picker::color_edit_button_rgb( - ui, - &mut emissive_rgb, - ) - .changed() - { - material.emissive_factor[0] = emissive_rgb[0]; - material.emissive_factor[1] = emissive_rgb[1]; - material.emissive_factor[2] = emissive_rgb[2]; - } - ui.end_row(); - }); - - ui.add_space(6.0); - ui.label(RichText::new("Surface").strong()); - Grid::new(format!("material_surface_{}", material_name)) - .num_columns(2) - .spacing([12.0, 6.0]) - .striped(true) - .show(ui, |ui| { - ui.label("Metallic"); - ui.add( - DragValue::new(&mut material.metallic_factor) - .speed(0.01) - .range(0.0..=1.0), - ); - ui.end_row(); - - ui.label("Roughness"); - ui.add( - DragValue::new(&mut material.roughness_factor) - .speed(0.01) - .range(0.0..=1.0), - ); - ui.end_row(); - - ui.label("Occlusion Strength"); - ui.add( - DragValue::new(&mut material.occlusion_strength) - .speed(0.01) - .range(0.0..=1.0), - ); - ui.end_row(); - - ui.label("Normal Scale"); - ui.add( - DragValue::new(&mut material.normal_scale) - .speed(0.01) - .range(0.0..=10.0), - ); - ui.end_row(); - }); + let mut uniform_dirty = false; - ui.add_space(6.0); - ui.label(RichText::new("Alpha").strong()); - Grid::new(format!("material_alpha_{}", material_name)) - .num_columns(2) - .spacing([12.0, 6.0]) - .striped(true) - .show(ui, |ui| { - ui.label("Alpha Mode"); - egui::ComboBox::from_id_salt(format!( - "alpha_mode_{}", - material_name - )) - .selected_text(match material.alpha_mode { - dropbear_engine::model::AlphaMode::Opaque => { - "Opaque" - } - dropbear_engine::model::AlphaMode::Mask => { - "Mask" - } - dropbear_engine::model::AlphaMode::Blend => { - "Blend" - } - }) - .show_ui(ui, |ui| { - ui.selectable_value( - &mut material.alpha_mode, - dropbear_engine::model::AlphaMode::Opaque, - "Opaque", - ); - ui.selectable_value( - &mut material.alpha_mode, - dropbear_engine::model::AlphaMode::Mask, - "Mask", - ); - ui.selectable_value( - &mut material.alpha_mode, - dropbear_engine::model::AlphaMode::Blend, - "Blend", - ); - }); - ui.end_row(); - - ui.label("Alpha Cutoff"); - let mut cutoff = material.alpha_cutoff.unwrap_or(0.5); - if ui - .add( - DragValue::new(&mut cutoff) - .speed(0.01) - .range(0.0..=1.0), - ) - .changed() - { - material.alpha_cutoff = Some(cutoff); - } - ui.end_row(); - - ui.label("Double Sided"); - ui.checkbox(&mut material.double_sided, ""); - ui.end_row(); - }); - - ui.add_space(6.0); - ui.label(RichText::new("UV & Wrap").strong()); - Grid::new(format!("material_uv_{}", material_name)) - .num_columns(2) - .spacing([12.0, 6.0]) - .striped(true) - .show(ui, |ui| { - ui.label("Wrap"); - egui::ComboBox::from_id_salt(format!( - "wrap_mode_{}", - material_name - )) - .selected_text(match material.wrap_mode { - dropbear_engine::texture::TextureWrapMode::Repeat => { - "Repeat" - } - dropbear_engine::texture::TextureWrapMode::Clamp => { - "Clamp" - } - }) - .show_ui(ui, |ui| { - ui.selectable_value( - &mut material.wrap_mode, - dropbear_engine::texture::TextureWrapMode::Repeat, - "Repeat", - ); - ui.selectable_value( - &mut material.wrap_mode, - dropbear_engine::texture::TextureWrapMode::Clamp, - "Clamp", - ); - }); - ui.end_row(); - - ui.label("UV Tiling"); - ui.horizontal(|ui| { - ui.add( - DragValue::new(&mut material.uv_tiling[0]) - .speed(0.05) - .range(0.01..=10_000.0), - ); - ui.label("x"); - ui.add( - DragValue::new(&mut material.uv_tiling[1]) - .speed(0.05) - .range(0.01..=10_000.0), - ); - }); - ui.end_row(); - }); - - ui.add_space(8.0); - ui.label(RichText::new("Textures").strong()); + // helpers let texture_matches = - |current: Option<Handle<Texture>>, candidate: Option<Handle<Texture>>| { + |current: Option<Handle<Texture>>, + candidate: Option<Handle<Texture>>| + -> bool { match (current, candidate) { (None, None) => true, (Some(cur), Some(def)) => cur == def, @@ -1877,22 +1684,28 @@ impl InspectableComponent for MeshRenderer { } format!("Texture {:016x}", handle.id) }; - let original_label = |original: Option<Handle<Texture>>| -> String { - original - .map(handle_label) - .unwrap_or_else(|| "Original (None)".to_string()) - }; + let original_label = + |original: Option<Handle<Texture>>| -> String { + original + .map(handle_label) + .unwrap_or_else(|| "Original (None)".to_string()) + }; let texture_combo = |ui: &mut egui::Ui, - slot_label: &str, - slot_id: &str, - current_texture: Option<Handle<Texture>>, - original_texture: Option<Handle<Texture>>, - default_texture: Option<Handle<Texture>>| - -> Option<Option<Handle<Texture>>> { + slot_id: &str, + current_texture: Option<Handle<Texture>>, + original_texture: Option<Handle<Texture>>, + default_texture: Option<Handle<Texture>>| + -> Option<Option<Handle<Texture>>> { let mut updated = None; - let is_original = texture_matches(current_texture, original_texture); - let is_default = texture_matches(current_texture, default_texture); + let is_original = texture_matches( + current_texture, + original_texture, + ); + let is_default = texture_matches( + current_texture, + default_texture, + ); let selected_text = if is_original { original_label(original_texture) } else if is_default { @@ -1902,62 +1715,57 @@ impl InspectableComponent for MeshRenderer { } else { "None".to_string() }; - - ui.horizontal(|ui| { - ui.label(slot_label); - ComboBox::from_id_salt(format!( - "texture_slot_{}_{}", - material_name, slot_id - )) - .selected_text(selected_text) - .show_ui(ui, |ui| { - if ui - .selectable_label( - false, - original_label(original_texture), - ) - .clicked() - { - updated = Some(original_texture); - } - - ui.separator(); - + ComboBox::from_id_salt(format!( + "texture_slot_{}_{}", + material_name, slot_id + )) + .selected_text(selected_text) + .show_ui(ui, |ui| { + if ui + .selectable_label( + false, + original_label(original_texture), + ) + .clicked() + { + updated = Some(original_texture); + } + ui.separator(); + if ui.selectable_label(false, "Default").clicked() + { + updated = Some(default_texture); + } + ui.separator(); + for (handle, label) in &texture_options { if ui - .selectable_label(false, "Default") + .selectable_label(false, label) .clicked() + && ASSET_REGISTRY + .read() + .get_texture(*handle) + .is_some() { - updated = Some(default_texture); - } - - ui.separator(); - - for (handle, label) in &texture_options { - if ui - .selectable_label(false, label) - .clicked() - { - if let Some(texture) = ASSET_REGISTRY - .read() - .get_texture(*handle) - { - let _ = texture; - updated = Some(Some(*handle)); - } - } + updated = Some(Some(*handle)); } - }); + } }); - updated }; + let ( + default_diffuse, + default_normal, + default_emissive, + default_mr, + default_occ, + ) = default_textures.clone(); let original_diffuse = default_material.as_ref().map(|m| m.diffuse_texture); let original_normal = default_material.as_ref().and_then(|m| m.normal_texture); - let original_emissive = - default_material.as_ref().and_then(|m| m.emissive_texture); + let original_emissive = default_material + .as_ref() + .and_then(|m| m.emissive_texture); let original_mr = default_material .as_ref() .and_then(|m| m.metallic_roughness_texture); @@ -1965,90 +1773,480 @@ impl InspectableComponent for MeshRenderer { .as_ref() .and_then(|m| m.occlusion_texture); - let (default_diffuse, default_normal, default_emissive, default_mr, default_occ) = - default_textures.clone(); + CollapsingHeader::new("Diffuse") + .id_salt(format!( + "diffuse_{}_{}", + material_id, + entity.to_bits() + )) + .default_open(true) + .show(ui, |ui| { + Grid::new(format!( + "diffuse_grid_{}", + material_name + )) + .num_columns(2) + .spacing([12.0, 6.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Texture"); + if let Some(new_tex) = texture_combo( + ui, + "diffuse", + Some(material.diffuse_texture), + original_diffuse, + default_diffuse, + ) { + if let Some(tex) = new_tex { + material.diffuse_texture = tex; + } + let mut registry = ASSET_REGISTRY.write(); + material.rebuild_bind_group( + &mut registry, + &graphics, + ); + uniform_dirty = true; + } + ui.end_row(); + + ui.label("Tint"); + let mut tint_rgb = [ + material.base_colour[0], + material.base_colour[1], + material.base_colour[2], + ]; + if egui::color_picker::color_edit_button_rgb( + ui, + &mut tint_rgb, + ) + .changed() + { + uniform_dirty = true; + } + material.base_colour[0] = tint_rgb[0]; + material.base_colour[1] = tint_rgb[1]; + material.base_colour[2] = tint_rgb[2]; + ui.end_row(); + + ui.label("Opacity"); + if ui + .add( + DragValue::new( + &mut material.base_colour[3], + ) + .speed(0.01) + .range(0.0..=1.0), + ) + .changed() + { + uniform_dirty = true; + } + ui.end_row(); + }); + }); - if let Some(new_diffuse) = texture_combo( - ui, - "Diffuse", - "diffuse", - Some(material.diffuse_texture), - original_diffuse, - default_diffuse, - ) { - if let Some(tex) = new_diffuse { - material.diffuse_texture = tex; - { - let mut registry = ASSET_REGISTRY.write(); - material.rebuild_bind_group(&mut registry, &graphics); - } - material.sync_uniform(&graphics); - } - } + CollapsingHeader::new("Normal") + .id_salt(format!( + "normal_{}_{}", + material_id, + entity.to_bits() + )) + .default_open(true) + .show(ui, |ui| { + Grid::new(format!( + "normal_grid_{}", + material_name + )) + .num_columns(2) + .spacing([12.0, 6.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Texture"); + if let Some(new_tex) = texture_combo( + ui, + "normal", + material.normal_texture, + original_normal, + default_normal, + ) { + material.normal_texture = new_tex; + let mut registry = ASSET_REGISTRY.write(); + material.rebuild_bind_group( + &mut registry, + &graphics, + ); + uniform_dirty = true; + } + ui.end_row(); - if let Some(new_normal) = texture_combo( - ui, - "Normal", - "normal", - material.normal_texture, - original_normal, - default_normal, - ) { - if let Some(tex) = new_normal { - material.normal_texture = Some(tex); - { - let mut registry = ASSET_REGISTRY.write(); - material.rebuild_bind_group(&mut registry, &graphics); - } - material.sync_uniform(&graphics); - } - } + ui.label("Scale"); + if ui + .add( + DragValue::new( + &mut material.normal_scale, + ) + .speed(0.01) + .range(0.0..=10.0), + ) + .changed() + { + uniform_dirty = true; + } + ui.end_row(); + }); + }); - if let Some(new_emissive) = texture_combo( - ui, - "Emissive", - "emissive", - material.emissive_texture, - original_emissive, - default_emissive, - ) { - material.emissive_texture = new_emissive; - { - let mut registry = ASSET_REGISTRY.write(); - material.rebuild_bind_group(&mut registry, &graphics); - } - material.sync_uniform(&graphics); - } + CollapsingHeader::new("Metallic-Roughness") + .id_salt(format!( + "mr_{}_{}", + material_id, + entity.to_bits() + )) + .default_open(true) + .show(ui, |ui| { + Grid::new(format!("mr_grid_{}", material_name)) + .num_columns(2) + .spacing([12.0, 6.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Texture"); + if let Some(new_tex) = texture_combo( + ui, + "metal_rough", + material.metallic_roughness_texture, + original_mr, + default_mr, + ) { + material.metallic_roughness_texture = + new_tex; + let mut registry = + ASSET_REGISTRY.write(); + material.rebuild_bind_group( + &mut registry, + &graphics, + ); + uniform_dirty = true; + } + ui.end_row(); - if let Some(new_mr) = texture_combo( - ui, - "Metal/Rough", - "metal_rough", - material.metallic_roughness_texture, - original_mr, - default_mr, - ) { - material.metallic_roughness_texture = new_mr; - { - let mut registry = ASSET_REGISTRY.write(); - material.rebuild_bind_group(&mut registry, &graphics); - } - material.sync_uniform(&graphics); - } + ui.label("Metallic"); + if ui + .add( + DragValue::new( + &mut material.metallic_factor, + ) + .speed(0.01) + .range(0.0..=1.0), + ) + .changed() + { + uniform_dirty = true; + } + ui.end_row(); - if let Some(new_occ) = texture_combo( - ui, - "Occlusion", - "occlusion", - material.occlusion_texture, - original_occ, - default_occ, - ) { - material.occlusion_texture = new_occ; - { - let mut registry = ASSET_REGISTRY.write(); - material.rebuild_bind_group(&mut registry, &graphics); - } + ui.label("Roughness"); + if ui + .add( + DragValue::new( + &mut material.roughness_factor, + ) + .speed(0.01) + .range(0.0..=1.0), + ) + .changed() + { + uniform_dirty = true; + } + ui.end_row(); + }); + }); + + CollapsingHeader::new("Emissive") + .id_salt(format!( + "emissive_{}_{}", + material_id, + entity.to_bits() + )) + .default_open(true) + .show(ui, |ui| { + Grid::new(format!( + "emissive_grid_{}", + material_name + )) + .num_columns(2) + .spacing([12.0, 6.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Texture"); + if let Some(new_tex) = texture_combo( + ui, + "emissive", + material.emissive_texture, + original_emissive, + default_emissive, + ) { + material.emissive_texture = new_tex; + let mut registry = ASSET_REGISTRY.write(); + material.rebuild_bind_group( + &mut registry, + &graphics, + ); + uniform_dirty = true; + } + ui.end_row(); + + ui.label("Colour"); + if egui::color_picker::color_edit_button_rgb( + ui, + &mut material.emissive_factor, + ) + .changed() + { + uniform_dirty = true; + } + ui.end_row(); + + ui.label("Strength"); + if ui + .add( + DragValue::new( + &mut material.emissive_strength, + ) + .speed(0.01) + .range(0.0..=100.0), + ) + .changed() + { + uniform_dirty = true; + } + ui.end_row(); + }); + }); + + CollapsingHeader::new("Occlusion") + .id_salt(format!( + "occlusion_{}_{}", + material_id, + entity.to_bits() + )) + .default_open(true) + .show(ui, |ui| { + Grid::new(format!( + "occlusion_grid_{}", + material_name + )) + .num_columns(2) + .spacing([12.0, 6.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Texture"); + if let Some(new_tex) = texture_combo( + ui, + "occlusion", + material.occlusion_texture, + original_occ, + default_occ, + ) { + material.occlusion_texture = new_tex; + let mut registry = ASSET_REGISTRY.write(); + material.rebuild_bind_group( + &mut registry, + &graphics, + ); + uniform_dirty = true; + } + ui.end_row(); + + ui.label("Strength"); + if ui + .add( + DragValue::new( + &mut material.occlusion_strength, + ) + .speed(0.01) + .range(0.0..=1.0), + ) + .changed() + { + uniform_dirty = true; + } + ui.end_row(); + }); + }); + + CollapsingHeader::new("UV") + .id_salt(format!( + "uv_{}_{}", + material_id, + entity.to_bits() + )) + .default_open(true) + .show(ui, |ui| { + Grid::new(format!("uv_grid_{}", material_name)) + .num_columns(2) + .spacing([12.0, 6.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Tiling"); + ui.horizontal(|ui| { + if ui + .add( + DragValue::new( + &mut material.uv_tiling[0], + ) + .speed(0.05) + .range(0.01..=10_000.0), + ) + .changed() + | ui.add( + DragValue::new( + &mut material.uv_tiling[1], + ) + .speed(0.05) + .range(0.01..=10_000.0), + ) + .changed() + { + uniform_dirty = true; + } + }); + ui.end_row(); + + ui.label("Offset"); + ui.horizontal(|ui| { + if ui + .add( + DragValue::new( + &mut material.uv_offset[0], + ) + .speed(0.005), + ) + .changed() + | ui.add( + DragValue::new( + &mut material.uv_offset[1], + ) + .speed(0.005), + ) + .changed() + { + uniform_dirty = true; + } + }); + ui.end_row(); + + ui.label("Wrap"); + egui::ComboBox::from_id_salt(format!( + "wrap_mode_{}", + material_name + )) + .selected_text( + match material.wrap_mode { + dropbear_engine::texture::TextureWrapMode::Repeat => "Repeat", + dropbear_engine::texture::TextureWrapMode::Clamp => "Clamp", + }, + ) + .show_ui(ui, |ui| { + if ui + .selectable_value( + &mut material.wrap_mode, + dropbear_engine::texture::TextureWrapMode::Repeat, + "Repeat", + ) + .changed() + | ui.selectable_value( + &mut material.wrap_mode, + dropbear_engine::texture::TextureWrapMode::Clamp, + "Clamp", + ) + .changed() + { + let mut registry = ASSET_REGISTRY.write(); + material.rebuild_bind_group(&mut registry, &graphics); + uniform_dirty = true; + } + }); + ui.end_row(); + }); + }); + + CollapsingHeader::new("Alpha") + .id_salt(format!( + "alpha_{}_{}", + material_id, + entity.to_bits() + )) + .default_open(true) + .show(ui, |ui| { + Grid::new(format!( + "alpha_grid_{}", + material_name + )) + .num_columns(2) + .spacing([12.0, 6.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Mode"); + egui::ComboBox::from_id_salt(format!( + "alpha_mode_{}", + material_name + )) + .selected_text(match material.alpha_mode { + dropbear_engine::model::AlphaMode::Opaque => { + "Opaque" + } + dropbear_engine::model::AlphaMode::Mask => { + "Mask" + } + dropbear_engine::model::AlphaMode::Blend => { + "Blend" + } + }) + .show_ui(ui, |ui| { + if ui + .selectable_value( + &mut material.alpha_mode, + dropbear_engine::model::AlphaMode::Opaque, + "Opaque", + ) + .changed() + | ui.selectable_value( + &mut material.alpha_mode, + dropbear_engine::model::AlphaMode::Mask, + "Mask", + ) + .changed() + | ui.selectable_value( + &mut material.alpha_mode, + dropbear_engine::model::AlphaMode::Blend, + "Blend", + ) + .changed() + { + uniform_dirty = true; + } + }); + ui.end_row(); + + ui.label("Cutoff"); + let mut cutoff = + material.alpha_cutoff.unwrap_or(0.5); + if ui + .add( + DragValue::new(&mut cutoff) + .speed(0.01) + .range(0.0..=1.0), + ) + .changed() + { + material.alpha_cutoff = Some(cutoff); + uniform_dirty = true; + } + ui.end_row(); + }); + }); + + if uniform_dirty { material.sync_uniform(&graphics); } }); @@ -235,7 +235,7 @@ fn set_material_tint( .get_mut(&material_name) .ok_or(DropbearNativeError::InvalidArgument)?; - material.tint = [r, g, b, a]; + material.base_colour = [r, g, b, a]; material.sync_uniform(graphics); Ok(()) } @@ -14,7 +14,6 @@ //! - `-colonly` (invisible collision mesh) pub mod collider_group; -pub mod shader; use crate::component::{ Component, ComponentDescriptor, DisabilityFlags, InspectableComponent, SerializedComponent, @@ -1,222 +0,0 @@ -//! Shader code relating to displaying physics collider rendering. - -use std::mem::size_of; -use std::sync::Arc; - -use crate::physics::collider::{ColliderShape, WireframeGeometry}; -use dropbear_engine::graphics::SharedGraphicsContext; -use dropbear_engine::pipelines::DropbearShaderPipeline; -use dropbear_engine::shader::Shader; -use dropbear_engine::wgpu::{ - BlendState, BufferAddress, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, - DepthStencilState, FragmentState, FrontFace, MultisampleState, PipelineLayout, - PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology, RenderPipeline, - RenderPipelineDescriptor, StencilState, VertexAttribute, VertexBufferLayout, VertexFormat, - VertexState, VertexStepMode, -}; -use dropbear_engine::{entity::Transform, texture::Texture}; -use glam::Mat4; - -pub struct ColliderWireframePipeline { - pub shader: Shader, - pub pipeline_layout: PipelineLayout, - pub pipeline: RenderPipeline, -} - -impl DropbearShaderPipeline for ColliderWireframePipeline { - fn new(graphics: Arc<SharedGraphicsContext>) -> Self { - let shader = Shader::new( - graphics.clone(), - include_str!("shaders/collider.wgsl"), - Some("collider wireframe shaders"), - ); - - let pipeline_layout = graphics - .device - .create_pipeline_layout(&PipelineLayoutDescriptor { - label: Some("collider wireframe pipeline layout descriptor"), - bind_group_layouts: &[ - Some(&graphics.layouts.camera_bind_group_layout), // @group(0) - ], - immediate_size: 0, - }); - - let hdr_format = graphics.hdr.read().format(); - let sample_count: u32 = (*graphics.antialiasing.read()).into(); - let pipeline = graphics - .device - .create_render_pipeline(&RenderPipelineDescriptor { - label: Some("Collider Wireframe Pipeline"), - layout: Some(&pipeline_layout), - vertex: VertexState { - module: &shader.module, - entry_point: Some("vs_main"), - buffers: &[ - VertexBufferLayout { - array_stride: size_of::<[f32; 3]>() as BufferAddress, - step_mode: VertexStepMode::Vertex, - attributes: &[VertexAttribute { - offset: 0, - shader_location: 0, - format: VertexFormat::Float32x3, - }], - }, - ColliderInstanceRaw::desc(), - ], - compilation_options: Default::default(), - }, - fragment: Some(FragmentState { - module: &shader.module, - entry_point: Some("fs_main"), - targets: &[Some(ColorTargetState { - format: hdr_format, - blend: Some(BlendState::ALPHA_BLENDING), - write_mask: ColorWrites::ALL, - })], - compilation_options: Default::default(), - }), - primitive: PrimitiveState { - topology: PrimitiveTopology::LineList, - strip_index_format: None, - front_face: FrontFace::Ccw, - cull_mode: None, - unclipped_depth: false, - polygon_mode: PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(DepthStencilState { - format: Texture::DEPTH_FORMAT, - depth_write_enabled: Some(false), - depth_compare: Some(CompareFunction::Always), - stencil: StencilState::default(), - bias: DepthBiasState::default(), - }), - multisample: MultisampleState { - count: sample_count, - mask: !0, - alpha_to_coverage_enabled: false, - }, - cache: None, - multiview_mask: None, - }); - - Self { - shader, - pipeline_layout, - pipeline, - } - } - - fn shader(&self) -> &Shader { - &self.shader - } - - fn pipeline_layout(&self) -> &PipelineLayout { - &self.pipeline_layout - } - - fn pipeline(&self) -> &RenderPipeline { - &self.pipeline - } -} - -#[derive(Debug, Copy, Clone, Default, bytemuck::Pod, bytemuck::Zeroable)] -#[repr(C)] -pub struct ColliderUniform { - model_matrix: [[f32; 4]; 4], - color: [f32; 4], -} - -impl ColliderUniform { - pub fn new(transform: &Transform, color: [f32; 4]) -> Self { - Self { - model_matrix: transform.matrix().as_mat4().to_cols_array_2d(), - color, - } - } - - pub fn from_matrix(matrix: glam::Mat4, color: [f32; 4]) -> Self { - Self { - model_matrix: matrix.to_cols_array_2d(), - color, - } - } -} - -#[repr(C)] -#[derive(Copy, Clone, Default, bytemuck::Pod, bytemuck::Zeroable)] -pub struct ColliderInstanceRaw { - pub model: [[f32; 4]; 4], - pub color: [f32; 4], -} - -impl ColliderInstanceRaw { - pub fn from_matrix(matrix: Mat4, color: [f32; 4]) -> Self { - Self { - model: matrix.to_cols_array_2d(), - color, - } - } - - pub fn desc() -> VertexBufferLayout<'static> { - const ATTRIBS: [VertexAttribute; 5] = [ - VertexAttribute { - offset: 0, - shader_location: 1, - format: VertexFormat::Float32x4, - }, - VertexAttribute { - offset: size_of::<[f32; 4]>() as BufferAddress, - shader_location: 2, - format: VertexFormat::Float32x4, - }, - VertexAttribute { - offset: size_of::<[f32; 8]>() as BufferAddress, - shader_location: 3, - format: VertexFormat::Float32x4, - }, - VertexAttribute { - offset: size_of::<[f32; 12]>() as BufferAddress, - shader_location: 4, - format: VertexFormat::Float32x4, - }, - VertexAttribute { - offset: size_of::<[f32; 16]>() as BufferAddress, - shader_location: 5, - format: VertexFormat::Float32x4, - }, - ]; - - VertexBufferLayout { - array_stride: size_of::<ColliderInstanceRaw>() as BufferAddress, - step_mode: VertexStepMode::Instance, - attributes: &ATTRIBS, - } - } -} - -pub fn create_wireframe_geometry( - graphics: Arc<SharedGraphicsContext>, - shape: &ColliderShape, -) -> WireframeGeometry { - match shape { - ColliderShape::Box { half_extents } => { - WireframeGeometry::box_wireframe(graphics, half_extents.to_float_array()) - } - ColliderShape::Sphere { radius } => { - WireframeGeometry::sphere_wireframe(graphics, *radius, 16, 16) - } - ColliderShape::Capsule { - half_height, - radius, - } => WireframeGeometry::capsule_wireframe(graphics, *half_height, *radius, 16), - ColliderShape::Cylinder { - half_height, - radius, - } => WireframeGeometry::cylinder_wireframe(graphics, *half_height, *radius, 16), - ColliderShape::Cone { - half_height, - radius, - } => WireframeGeometry::cone_wireframe(graphics, *half_height, *radius, 16), - } -} @@ -1,48 +0,0 @@ -struct CameraUniform { - view_pos: vec4<f32>, - view: mat4x4<f32>, - view_proj: mat4x4<f32>, - inv_proj: mat4x4<f32>, - inv_view: mat4x4<f32>, -}; - -@group(0) @binding(0) -var <uniform> camera: CameraUniform; - -struct VertexInput { - @location(0) position: vec3<f32>, - @location(1) model_0: vec4<f32>, - @location(2) model_1: vec4<f32>, - @location(3) model_2: vec4<f32>, - @location(4) model_3: vec4<f32>, - @location(5) color: vec4<f32>, -} - -struct VertexOutput { - @builtin(position) clip_position: vec4<f32>, - @location(0) color: vec4<f32>, -} - -@vertex -fn vs_main(input: VertexInput) -> VertexOutput { - var output: VertexOutput; - - let model = mat4x4<f32>( - input.model_0, - input.model_1, - input.model_2, - input.model_3, - ); - - let world_position = model * vec4<f32>(input.position, 1.0); - output.clip_position = camera.view_proj * world_position; - - output.color = input.color; - - return output; -} - -@fragment -fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> { - return input.color; -} @@ -206,7 +206,7 @@ impl RendererCommon { &graphics.device, instances.len().max(1), wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - "Runtime Instance Buffer", + &format!("resizable buffer<handle={}>", handle_id), )); instance_buffer.write(&graphics.device, &graphics.queue, &instances); @@ -218,7 +218,6 @@ pub struct EucalyptusMaterial { pub roughness_factor: f32, pub alpha_mode: AlphaMode, pub alpha_cutoff: Option<f32>, - pub double_sided: bool, pub occlusion_strength: f32, pub normal_scale: f32, pub uv_tiling: [f32; 2], @@ -253,13 +252,12 @@ impl From<Material> for EucalyptusMaterial { emissive_texture: get_texture(value.emissive_texture), metallic_roughness_texture: get_texture(value.metallic_roughness_texture), occlusion_texture: get_texture(value.occlusion_texture), - tint: value.tint, + tint: value.base_colour, emissive_factor: value.emissive_factor, metallic_factor: value.metallic_factor, roughness_factor: value.roughness_factor, alpha_mode: value.alpha_mode, alpha_cutoff: value.alpha_cutoff, - double_sided: value.double_sided, occlusion_strength: value.occlusion_strength, normal_scale: value.normal_scale, uv_tiling: value.uv_tiling, @@ -379,7 +377,6 @@ impl EucalyptusMaterial { material.roughness_factor = self.roughness_factor; material.alpha_mode = self.alpha_mode; material.alpha_cutoff = self.alpha_cutoff; - material.double_sided = self.double_sided; material.occlusion_strength = self.occlusion_strength; material.normal_scale = self.normal_scale; material.uv_tiling = self.uv_tiling; @@ -483,7 +483,6 @@ pub struct SerializedMaterialCustomisation { pub roughness_factor: f32, pub alpha_mode: AlphaMode, pub alpha_cutoff: Option<f32>, - pub double_sided: bool, pub occlusion_strength: f32, pub normal_scale: f32, pub uv_tiling: [f32; 2], @@ -404,7 +404,6 @@ impl AsFile for ResourceReference { roughness_factor: 1.0, alpha_mode: dropbear_engine::model::AlphaMode::Opaque, alpha_cutoff: None, - double_sided: false, occlusion_strength: 1.0, normal_scale: 1.0, uv_tiling: [1.0, 1.0], @@ -59,6 +59,7 @@ downcast-rs.workspace = true puffin.workspace = true image.workspace = true rkyv.workspace = true +wesl.workspace = true [target.'cfg(unix)'.dependencies] daemonize = "0.5.0" @@ -2,7 +2,7 @@ use dropbear_engine::input::{Controller, Keyboard, Mouse}; use dropbear_engine::scene::{Scene, SceneCommand}; -use egui::CentralPanel; +use egui::{CentralPanel, Ui}; use eucalyptus_core::input::InputState; use gilrs::{Button, GamepadId}; use winit::dpi::PhysicalPosition; @@ -28,7 +28,7 @@ impl AboutWindow { } impl Scene for AboutWindow { - fn load(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn load(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, _ui: &mut Ui,) { self.window = Some(graphics.window.id()); } @@ -36,6 +36,7 @@ impl Scene for AboutWindow { &mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } @@ -43,10 +44,9 @@ impl Scene for AboutWindow { &mut self, _dt: f32, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ui: &mut Ui, ) { - let mut ui = egui::Ui::new(graphics.get_egui_context(), egui::Id::new("about window ui"), egui::UiBuilder::default()); - - CentralPanel::default().show_inside(&mut ui, |ui| { + CentralPanel::default().show_inside(ui, |ui| { ui.vertical_centered(|ui| { ui.add_space(8.0); ui.heading("eucalyptus editor"); @@ -79,6 +79,7 @@ impl Scene for AboutWindow { fn render<'a>( &mut self, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } @@ -2,7 +2,7 @@ //! //! Can also be technically used as a template for other windowed scenes (as its essentially the basics) -use egui::{CentralPanel, UiBuilder}; +use egui::{CentralPanel, Ui}; use gilrs::{Button, GamepadId}; use winit::dpi::PhysicalPosition; use winit::event::MouseButton; @@ -31,7 +31,7 @@ impl DebugWindow { } impl Scene for DebugWindow { - fn load(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn load(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, _ui: &mut Ui,) { self.window = Some(graphics.window.id()); } @@ -39,6 +39,7 @@ impl Scene for DebugWindow { &mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } @@ -46,13 +47,9 @@ impl Scene for DebugWindow { &mut self, _dt: f32, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ui: &mut Ui, ) { - let mut ui = egui::Ui::new( - graphics.get_egui_context(), - egui::Id::from("egui ui for debug window"), - UiBuilder::default(), - ); - CentralPanel::default().show_inside(&mut ui, |ui| { + CentralPanel::default().show_inside(ui, |ui| { ui.label("Hello Debug Window!"); }); @@ -62,6 +59,7 @@ impl Scene for DebugWindow { fn render<'a>( &mut self, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } @@ -1464,7 +1464,7 @@ impl Editor { { let mut project_path = self.project_path.lock(); crate::utils::show_new_project_window( - &graphics.get_egui_context(), + ui.ctx(), &mut self.show_new_project, &mut self.project_name, &mut project_path, @@ -1480,13 +1480,13 @@ impl Editor { self.pending_scene_switch = false; } - self.show_nerd_stats_window(&graphics.get_egui_context()); + self.show_nerd_stats_window(ui.ctx()); let mut open_flag = self.open_new_scene_window; let mut close_requested = false; if open_flag { egui::Window::new("New Scene").open(&mut open_flag).show( - &graphics.get_egui_context(), + ui.ctx(), |ui| { ui.vertical(|ui| { ui.label("Name: "); @@ -31,7 +31,7 @@ use winit::{event::WindowEvent, event_loop::ActiveEventLoop, keyboard::KeyCode}; use eucalyptus_core::rendering::RendererCommon; impl Scene for Editor { - fn load(&mut self, graphics: Arc<SharedGraphicsContext>) { + fn load(&mut self, graphics: Arc<SharedGraphicsContext>, _ui: &mut Ui) { { let src_path = { let project = PROJECT.read(); @@ -143,6 +143,7 @@ impl Scene for Editor { &mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } @@ -150,6 +151,7 @@ impl Scene for Editor { &mut self, dt: f32, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ui: &mut Ui, ) { self.dt = dt; @@ -168,7 +170,7 @@ impl Scene for Editor { } if let Some(mut receiver) = self.world_receiver.take() { - self.show_project_loading_window(&graphics.get_egui_context()); + self.show_project_loading_window(ui.ctx()); if let Ok(loaded_world) = receiver.try_recv() { self.world = Box::new(loaded_world); self.is_world_loaded.mark_project_loaded(); @@ -341,7 +343,7 @@ impl Scene for Editor { } } - let _ = self.run_signal(graphics.clone()); + let _ = self.run_signal(graphics.clone(), ui.ctx()); if let Some(e) = self.previously_selected_entity && let Ok(entity) = self.world.query_one::<&mut MeshRenderer>(e).get() @@ -381,7 +383,7 @@ impl Scene for Editor { .record_stats(dt, self.world.len() as u32); } - let open_ui_editor = graphics.get_egui_context().data_mut(|d| { + let open_ui_editor = ui.ctx().data_mut(|d: &mut egui::util::IdTypeMap| { d.get_temp::<Option<Entity>>(egui::Id::new("open_ui_editor")) .flatten() .inspect(|_| d.remove::<Option<Entity>>(egui::Id::new("open_ui_editor"))) @@ -448,8 +450,8 @@ impl Scene for Editor { self.input_state.mouse_delta = None; } - fn render(&mut self, graphics: Arc<SharedGraphicsContext>) { - self.editor_specific_render(&graphics); + fn render(&mut self, graphics: Arc<SharedGraphicsContext>, ui: &mut Ui) { + self.editor_specific_render(&graphics, ui); let hdr = graphics.hdr.read(); let mut encoder = CommandEncoder::new(graphics.clone(), Some("runtime viewport encoder")); @@ -716,18 +718,12 @@ impl Editor { } } - fn editor_specific_render(&mut self, graphics: &Arc<SharedGraphicsContext>) { + fn editor_specific_render(&mut self, graphics: &Arc<SharedGraphicsContext>, ui: &mut Ui) { self.size = graphics.viewport_texture.size; self.texture_id = Some(*graphics.texture_id.clone()); self.window = Some(graphics.window.clone()); - let mut ui = Ui::new( - graphics.get_egui_context(), - egui::Id::new("ui"), - UiBuilder::default(), - ); - - self.show_ui(&mut ui, graphics.clone()); - eucalyptus_core::logging::render(&mut ui); + self.show_ui(ui, graphics.clone()); + eucalyptus_core::logging::render(ui); } } @@ -5,7 +5,7 @@ use app_dirs2::AppDataType; use dropbear_engine::input::{Controller, Keyboard, Mouse}; use dropbear_engine::multisampling::AntiAliasingMode; use dropbear_engine::scene::{Scene, SceneCommand}; -use egui::{CentralPanel, ComboBox, Id, Panel, Slider, SliderClamping}; +use egui::{CentralPanel, ComboBox, Id, Panel, Slider, SliderClamping, Ui}; use egui_dock::DockState; use egui_ltreeview::{Action, NodeBuilder}; use eucalyptus_core::input::InputState; @@ -138,7 +138,7 @@ impl EditorSettingsWindow { } impl Scene for EditorSettingsWindow { - fn load(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn load(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, _ui: &mut Ui,) { self.window = Some(graphics.window.id()); } @@ -146,6 +146,7 @@ impl Scene for EditorSettingsWindow { &mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } @@ -153,10 +154,9 @@ impl Scene for EditorSettingsWindow { &mut self, _dt: f32, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ui: &mut Ui, ) { - let mut ui = egui::Ui::new(graphics.get_egui_context(), egui::Id::new("editor settings window ui"), egui::UiBuilder::default()); - - CentralPanel::default().show_inside(&mut ui, |ui| { + CentralPanel::default().show_inside(ui, |ui| { let mut editor = EDITOR_SETTINGS.write(); Panel::left("editor_settings_tree_panel") @@ -265,6 +265,7 @@ impl Scene for EditorSettingsWindow { fn render<'a>( &mut self, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } @@ -2,7 +2,7 @@ use dropbear_engine::input::{Controller, Keyboard, Mouse}; use dropbear_engine::scene::{Scene, SceneCommand}; -use egui::{CentralPanel, Color32, Id, RichText, Slider, SliderClamping}; +use egui::{CentralPanel, Color32, Id, RichText, Slider, SliderClamping, Ui}; use egui_ltreeview::{Action, NodeBuilder}; use eucalyptus_core::input::InputState; use eucalyptus_core::states::PROJECT; @@ -44,7 +44,7 @@ impl ProjectSettingsWindow { } impl Scene for ProjectSettingsWindow { - fn load(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>) { + fn load(&mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, _ui: &mut Ui,) { self.window = Some(graphics.window.id()); } @@ -52,6 +52,7 @@ impl Scene for ProjectSettingsWindow { &mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } @@ -59,9 +60,9 @@ impl Scene for ProjectSettingsWindow { &mut self, _dt: f32, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ui: &mut Ui, ) { - let mut ui = egui::Ui::new(graphics.get_egui_context(), egui::Id::new("project settings window ui"), egui::UiBuilder::default()); - CentralPanel::default().show_inside(&mut ui, |ui| { + CentralPanel::default().show_inside(ui, |ui| { let mut project = PROJECT.write(); egui::Panel::left("project_settings_tree_panel") @@ -201,6 +202,7 @@ impl Scene for ProjectSettingsWindow { fn render<'a>( &mut self, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } @@ -10,6 +10,7 @@ pub mod signal; pub mod spawn; pub mod stats; pub mod utils; +// pub mod outline; use editor::docks::asset_viewer::AssetViewerDock; use editor::docks::build_console::BuildConsoleDock; @@ -6,7 +6,7 @@ use dropbear_engine::{ input::{Controller, Keyboard, Mouse}, scene::{Scene, SceneCommand}, }; -use egui::{self, FontId, Frame, RichText, UiBuilder}; +use egui::{self, FontId, Frame, RichText, Ui, UiBuilder}; use egui_toast::{ToastOptions, Toasts}; use eucalyptus_core::config::ProjectConfig; use eucalyptus_core::states::PROJECT; @@ -243,6 +243,7 @@ impl Scene for MainMenu { fn load( &mut self, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui ) { log::info!("Loaded main menu scene"); } @@ -251,6 +252,7 @@ impl Scene for MainMenu { &mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } @@ -258,12 +260,14 @@ impl Scene for MainMenu { &mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } fn render<'a>( &mut self, graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ui: &mut Ui, ) { #[allow(clippy::collapsible_if)] if let Some(handle) = self.project_creation_handle.as_ref() { @@ -303,23 +307,17 @@ impl Scene for MainMenu { } } - let mut ui = egui::Ui::new( - graphics.get_egui_context(), - egui::Id::new("main menu ui"), - UiBuilder::default(), - ); - let screen_size: (f32, f32) = ( graphics.window.inner_size().width as f32 - 100.0, graphics.window.inner_size().height as f32 - 100.0, ); - let egui_ctx = graphics.get_egui_context(); + let egui_ctx = ui.ctx().clone(); let mut local_open_project = false; let mut local_select_project = false; egui::CentralPanel::default() .frame(Frame::new()) - .show_inside(&mut ui, |ui| { + .show_inside(ui, |ui| { ui.vertical_centered(|ui| { ui.add_space(64.0); ui.label(RichText::new("Eucalyptus").font(FontId::proportional(32.0))); @@ -512,7 +510,7 @@ impl Scene for MainMenu { }); } - self.toast.show(&mut ui); + self.toast.show(ui); } fn exit(&mut self, _event_loop: &ActiveEventLoop) { @@ -0,0 +1,146 @@ +//! Outline shader +//! +//! General idea: +//! - 1. get that one entity as a sort of map +//! - 2. run an edge detection, and apply an orange outline for the edge (perhaps a specific width) + +use std::sync::Arc; +use dropbear_engine::graphics::{CommandEncoder, SharedGraphicsContext}; +use dropbear_engine::pipelines::builder::RenderPipelineBuilder; +use dropbear_engine::pipelines::DropbearShaderPipeline; +use dropbear_engine::texture::{Texture, TextureBuilder}; + +pub struct OutlineShader { + depth_stencil: Texture, + mask_bind_group: wgpu::BindGroup, + pub mask_pipeline: wgpu::RenderPipeline, +} + +impl OutlineShader { + pub fn new(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self> { + let size = graphics.window.inner_size(); + + let depth_stencil_format = wgpu::TextureFormat::Depth24PlusStencil8; + let depth_stencil = TextureBuilder::new(&graphics.device) + .label("depth stencil") + .size(size.width, size.height) + .format(depth_stencil_format) + .usage(wgpu::TextureUsages::RENDER_ATTACHMENT) + .build(); + + let source = wesl::Wesl::new("src/shaders") + .add_package(&dropbear_engine::shader::code::PACKAGE) + .compile(&"dropbear_shaders::mask".parse()?) + .inspect_err(|e| { + panic!("{e}"); + })? + .to_string(); + + let mask_shader = wgpu::ShaderModuleDescriptor { + label: Some("mask shader"), + source: wgpu::ShaderSource::Wgsl(source.into()), + }; + + let mask_bind_group_layout = graphics.device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("mask bind group layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + ], + }); + + let mask_bind_group = graphics.device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("mask_bind_group"), + layout: &mask_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Sampler(&mask_texture.sampler), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(&mask_texture.view), + }, + ], + }); + + let mask_pipeline_layout = graphics.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("mask pipeline layout"), + bind_group_layouts: &[ + Some(&mask_bind_group_layout) + ], + immediate_size: 0, + }); + + let mask_pipeline = RenderPipelineBuilder::new() + .vertex_shader(mask_shader.clone()) + .fragment_shader(mask_shader.clone()) + .fragment_entry_point("fs_mask") + .cull_mode(Some(wgpu::Face::Back)) + .depth_stencil(wgpu::DepthStencilState { + format: depth_stencil_format, + depth_write_enabled: Some(false), + depth_compare: Some(wgpu::CompareFunction::Always), + stencil: wgpu::StencilState { + write_mask: 0xFF, + read_mask: 0xFF, + front: wgpu::StencilFaceState { + compare: wgpu::CompareFunction::Always, + pass_op: wgpu::StencilOperation::Replace, + ..Default::default() + }, + back: wgpu::StencilFaceState::IGNORE, + }, + bias: wgpu::DepthBiasState::default(), + }) + .layout(&mask_pipeline_layout) + .build(&graphics.device)?; + + Self { + depth_stencil, + mask_bind_group: (), + mask_pipeline, + } + } + + pub fn draw(&self, graphics: Arc<SharedGraphicsContext>, encoder: &mut CommandEncoder) { + { + let mut draw_mask_stencil = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("draw mask stencil"), + color_attachments: &[], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &self.depth_stencil.view, // draw onto depth_stencil + depth_ops: None, + stencil_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(0), + store: wgpu::StoreOp::Store, + }), + }), + timestamp_writes: None, + multiview_mask: None, + occlusion_query_set: None, + }); + + draw_mask_stencil.set_stencil_reference(0xFF); + draw_mask_stencil.set_pipeline(&self.mask_pipeline); + draw_mask_stencil.set_bind_group(0, &self.mask_bind_group, &[]); + draw_mask_stencil.draw(0..3, 0..1); + } + } +} @@ -17,11 +17,11 @@ use std::sync::Arc; use winit::keyboard::KeyCode; pub trait SignalController { - fn run_signal(&mut self, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<()>; + fn run_signal(&mut self, graphics: Arc<SharedGraphicsContext>, ctx: &egui::Context) -> anyhow::Result<()>; } impl SignalController for Editor { - fn run_signal(&mut self, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<()> { + fn run_signal(&mut self, graphics: Arc<SharedGraphicsContext>, ctx: &egui::Context) -> anyhow::Result<()> { let mut requeue = vec![]; while let Some(signal) = self.signal.pop_front() { let local_signal: Option<Signal> = None; @@ -384,7 +384,7 @@ impl SignalController for Editor { .fixed_size([500.0, 400.0]) .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) .open(&mut window_open) - .show(&graphics.get_egui_context(), |ui| { + .show(ctx, |ui| { ui.vertical_centered(|ui| { ui.heading("Gradle Build Progress"); ui.add_space(10.0); @@ -491,7 +491,7 @@ impl SignalController for Editor { .fixed_size([700.0, 500.0]) .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) .open(&mut window_open) - .show(&graphics.get_egui_context(), |ui| { + .show(ctx, |ui| { ui.vertical(|ui| { ui.heading("Build Failed"); ui.add_space(5.0); @@ -313,14 +313,10 @@ impl NerdStats { } /// Shows the egui window as a CentralPanel, typically used for another window. - pub fn show_window(&mut self, ctx: &Context) { - let mut nerd_stats_ui = egui::Ui::new(ctx.clone(), egui::Id::new("nerd-stats ui"), egui::UiBuilder::default()); - - egui::CentralPanel::default().show_inside(&mut nerd_stats_ui, |ui| { + pub fn show_window(&mut self, ui: &mut Ui) { + egui::CentralPanel::default().show_inside(ui, |ui| { self.content(ui); }); - - ctx.request_repaint(); } } @@ -328,26 +324,31 @@ impl Scene for NerdStats { fn load( &mut self, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } fn physics_update( &mut self, _dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { } fn update( &mut self, dt: f32, _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _ui: &mut Ui, ) { self.record_stats(dt, self.entity_count); } + fn render<'a>( &mut self, - graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + _graphics: std::sync::Arc<dropbear_engine::graphics::SharedGraphicsContext>, + ui: &mut Ui, ) { - self.show_window(&graphics.get_egui_context()); + self.show_window(ui); } fn exit(&mut self, _event_loop: &ActiveEventLoop) {} } @@ -24,11 +24,12 @@ use hecs::Entity; use kino_ui::WidgetTree; use kino_ui::rendering::KinoRenderTargetId; use std::collections::HashMap; +use egui::Ui; use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; impl Scene for PlayMode { - fn load(&mut self, graphics: Arc<SharedGraphicsContext>) { + fn load(&mut self, graphics: Arc<SharedGraphicsContext>, _ui: &mut Ui,) { if self.current_scene.is_none() { let initial_scene = if let Some(s) = &self.initial_scene { s.clone() @@ -48,7 +49,7 @@ impl Scene for PlayMode { } } - fn physics_update(&mut self, dt: f32, _graphics: Arc<SharedGraphicsContext>) { + fn physics_update(&mut self, dt: f32, _graphics: Arc<SharedGraphicsContext>, _ui: &mut Ui,) { if self.scripts_ready { let _ = self .script_manager @@ -273,7 +274,7 @@ impl Scene for PlayMode { } } - fn update(&mut self, dt: f32, graphics: Arc<SharedGraphicsContext>) { + fn update(&mut self, dt: f32, graphics: Arc<SharedGraphicsContext>, ui: &mut Ui,) { graphics.future_queue.poll(); self.poll(graphics.clone()); @@ -401,10 +402,8 @@ impl Scene for PlayMode { graphics.clone(), ); - let mut ui = egui::Ui::new(graphics.get_egui_context(), egui::Id::new("redback-runtime ui"), egui::UiBuilder::default()); - #[cfg(feature = "debug")] - egui::Panel::top("menu_bar").show_inside(&mut ui, |ui| { + egui::Panel::top("menu_bar").show_inside(ui, |ui| { egui::MenuBar::new().ui(ui, |ui| { use crate::WindowMode; ui.menu_button("Window", |ui| { @@ -473,11 +472,10 @@ impl Scene for PlayMode { }); }); - CentralPanel::default().show_inside(&mut ui, |ui| { + CentralPanel::default().show_inside(ui, |ui| { if let Some(p) = &self.scene_progress { if !p.is_everything_loaded() && p.is_first_scene { ui.centered_and_justified(|ui| { - egui_extras::install_image_loaders(&graphics.get_egui_context()); ui.add( egui::Image::new(egui::include_image!( "../../../resources/eucalyptus-editor.png" @@ -599,7 +597,7 @@ impl Scene for PlayMode { self.input_state.mouse_delta = None; } - fn render<'a>(&mut self, graphics: Arc<SharedGraphicsContext>) { + fn render<'a>(&mut self, graphics: Arc<SharedGraphicsContext>, _ui: &mut Ui,) { let hdr = graphics.hdr.read(); let mut encoder = CommandEncoder::new(graphics.clone(), Some("runtime viewport encoder")); @@ -317,7 +317,6 @@ typedef struct NMaterial { float metallic_factor; float roughness_factor; const float* alpha_cutoff; - bool double_sided; float occlusion_strength; float normal_scale; NVector2 uv_tiling;