,
}
@@ -28,8 +28,8 @@ impl GliumAssetManager {
impl ModelManager for GliumAssetManager {
fn load_gltf(&mut self, id: ModelID, path: &Path) {
- let models = load_gltf(path, self.facade.as_ref()).unwrap();
- self.models.insert(id, models);
+ let model = load_gltf(path, self.facade.as_ref()).unwrap();
+ self.models.insert(id, model);
}
fn unload_model(&mut self, id: ModelID) {
@@ -48,6 +48,6 @@ impl ModelManager for GliumAssetManager {
// }
fn get_model(&self, id: &ModelID) -> Option<&dyn Any> {
- self.models.get(id).map(|models| models as &dyn Any)
+ self.models.get(id).map(|model| model as &dyn Any)
}
}
diff --git a/glium_platform/src/gltf_loader.rs b/glium_platform/src/gltf_loader.rs
index fe4ec37..e41bbfb 100644
--- a/glium_platform/src/gltf_loader.rs
+++ b/glium_platform/src/gltf_loader.rs
@@ -1,4 +1,4 @@
-use anyhow::{bail, Context, Result};
+use anyhow::{Context, Result};
use glium::{backend::Facade, IndexBuffer, VertexBuffer};
use glium::index::PrimitiveType;
use std::{fmt::Debug, path::Path};
@@ -8,16 +8,31 @@ use glium::uniforms::{SamplerWrapFunction, MinifySamplerFilter, MagnifySamplerFi
use gltf::image::Format as GltfFormat;
use glam::Vec2;
-/// Load a glTF 2.0 file from disk and upload all primitives to the GPU.
-///
-/// Returns one [`Model`] per glTF primitive (across all meshes).
-pub fn load_gltf(path: P, facade: &dyn Facade) -> Result>
+/// Load a glTF 2.0 file from disk and upload the first primitive to the GPU.
+pub fn load_gltf(path: P, facade: &dyn Facade) -> Result
where
P: AsRef + Debug,
{
// -- parse the asset & bring buffer blobs into memory --
let (doc, buffers, images) = gltf::import(path.as_ref()).context("failed to import glTF file")?;
+ // -- grab the very first mesh / primitive --
+ let mesh = doc.meshes().next().context("glTF has no meshes")?;
+ let primitive = mesh.primitives().next().context("mesh has no primitives")?;
+
+ // ---------- MATERIAL ----------
+ let mut mat = Material::default();
+
+ let mat_idx = primitive.material().index().context("primitive has no material")?;
+ let material = doc.materials().nth(mat_idx).unwrap();
+ let pbr = material.pbr_metallic_roughness();
+
+ // Factors --------------------------------------------------
+ mat.base_color_factor = pbr.base_color_factor();
+ mat.metal_factor = pbr.metallic_factor();
+ mat.roughness_factor = pbr.roughness_factor();
+ mat.emissive_factor = material.emissive_factor();
+
// Helper to update sampler settings from glTF sampler
fn update_sampler(mat: &mut Material, t: &gltf::texture::Texture<'_>) {
let sampler_info = t.sampler();
@@ -49,92 +64,64 @@ where
}
}
- let mut out: Vec = Vec::new();
+ // Base-color texture (sRGB)
+ if let Some(info) = pbr.base_color_texture() {
+ update_sampler(&mut mat, &info.texture());
+ let view = info.texture().source().index();
+ mat.base_color = Some(glium_srgb_texture(facade, &images[view])?);
+ }
- for mesh in doc.meshes() {
- for primitive in mesh.primitives() {
- // ---------- MATERIAL ----------
- let mut mat = Material::default();
- let material = primitive.material();
- let pbr = material.pbr_metallic_roughness();
+ // Metallic-Roughness (linear)
+ if let Some(info) = pbr.metallic_roughness_texture() {
+ update_sampler(&mut mat, &info.texture());
+ let view = info.texture().source().index();
+ mat.metallic_roughness = Some(glium_linear_texture(facade, &images[view])?);
+ }
- // Factors --------------------------------------------------
- mat.base_color_factor = pbr.base_color_factor();
- mat.metal_factor = pbr.metallic_factor();
- mat.roughness_factor = pbr.roughness_factor();
- mat.emissive_factor = material.emissive_factor();
+ // Normal map (linear)
+ if let Some(info) = primitive.material().normal_texture() {
+ update_sampler(&mut mat, &info.texture());
+ let view = info.texture().source().index();
+ mat.normal = Some(glium_linear_texture(facade, &images[view])?);
+ }
- // Base-color texture (sRGB)
- if let Some(info) = pbr.base_color_texture() {
- update_sampler(&mut mat, &info.texture());
- let view = info.texture().source().index();
- mat.base_color = Some(glium_srgb_texture(facade, &images[view])?);
- }
+ // Occlusion (linear)
+ if let Some(info) = primitive.material().occlusion_texture() {
+ update_sampler(&mut mat, &info.texture());
+ let view = info.texture().source().index();
+ mat.occlusion = Some(glium_linear_texture(facade, &images[view])?);
+ }
- // Metallic-Roughness (linear)
- if let Some(info) = pbr.metallic_roughness_texture() {
- update_sampler(&mut mat, &info.texture());
- let view = info.texture().source().index();
- mat.metallic_roughness = Some(glium_linear_texture(facade, &images[view])?);
- }
+ // Emissive (sRGB)
+ if let Some(info) = primitive.material().emissive_texture() {
+ update_sampler(&mut mat, &info.texture());
+ let view = info.texture().source().index();
+ mat.emissive = Some(glium_srgb_texture(facade, &images[view])?);
+ }
- // Normal map (linear)
- if let Some(info) = material.normal_texture() {
- update_sampler(&mut mat, &info.texture());
- let view = info.texture().source().index();
- mat.normal = Some(glium_linear_texture(facade, &images[view])?);
- }
-
- // Occlusion (linear)
- if let Some(info) = material.occlusion_texture() {
- update_sampler(&mut mat, &info.texture());
- let view = info.texture().source().index();
- mat.occlusion = Some(glium_linear_texture(facade, &images[view])?);
- }
-
- // Emissive (sRGB)
- if let Some(info) = material.emissive_texture() {
- update_sampler(&mut mat, &info.texture());
- let view = info.texture().source().index();
- mat.emissive = Some(glium_srgb_texture(facade, &images[view])?);
- }
-
- // KHR_texture_transform (base color only, for now)
- if let Some(tex) = pbr.base_color_texture() {
- if let Some(xform) = tex.texture_transform() {
- mat.uv_offset = Vec2::new(xform.offset()[0], xform.offset()[1]);
- mat.uv_scale = Vec2::new(xform.scale()[0], xform.scale()[1]);
- }
- }
-
- // ---- Vertex/index data ----
- let reader = primitive.reader(|buf| Some(&buffers[buf.index()].0));
-
- let positions: Vec<[f32; 3]> = reader.read_positions().context("missing POSITION")?.collect();
- let normals: Vec<[f32; 3]> = reader.read_normals().context("missing NORMAL")?.collect();
- let tex_coords: Vec<[f32; 2]> = reader
- .read_tex_coords(0)
- .map(|tc| tc.into_f32().collect())
- .unwrap_or_else(|| vec![[0.0, 0.0]; positions.len()]);
- let indices: Vec = reader.read_indices().context("missing indices")?.into_u32().collect();
-
- // Interleave
- let vertices: Vec = (0..positions.len())
- .map(|i| Vertex { position: positions[i], normal: normals[i], tex_coords: tex_coords[i] })
- .collect();
-
- let vbuf = VertexBuffer::immutable(facade, &vertices)?;
- let ibuf = IndexBuffer::immutable(facade, PrimitiveType::TrianglesList, &indices)?;
-
- out.push(Model { mesh: Mesh { vbuf, ibuf }, material: mat });
+ // KHR_texture_transform
+ if let Some(tex) = pbr.base_color_texture() {
+ if let Some(xform) = tex.texture_transform() {
+ mat.uv_offset = Vec2::new(xform.offset()[0], xform.offset()[1]);
+ mat.uv_scale = Vec2::new(xform.scale()[0], xform.scale()[1]);
}
}
- if out.is_empty() {
- bail!("glTF has no mesh primitives");
- }
+ // ---- Vertex/index data ----
+ let reader = primitive.reader(|buf| Some(&buffers[buf.index()].0));
- Ok(out)
+ let positions: Vec<[f32; 3]> = reader.read_positions().context("missing POSITION")?.collect();
+ let normals: Vec<[f32; 3]> = reader.read_normals().context("missing NORMAL")?.collect();
+ let tex_coords: Vec<[f32; 2]> = reader.read_tex_coords(0).map(|tc| tc.into_f32().collect()).unwrap_or_else(|| vec![[0.0, 0.0]; positions.len()]);
+ let indices: Vec = reader.read_indices().context("missing indices")?.into_u32().collect();
+
+ // Interleave
+ let vertices: Vec = (0..positions.len()).map(|i| Vertex { position: positions[i], normal: normals[i], tex_coords: tex_coords[i] }).collect();
+
+ let vbuf = VertexBuffer::immutable(facade, &vertices)?;
+ let ibuf = IndexBuffer ::immutable(facade, PrimitiveType::TrianglesList, &indices)?;
+
+ Ok(Model { mesh: Mesh { vbuf, ibuf }, material: mat })
}
/// Linear-space texture (RGBA8) from glTF image data.
diff --git a/glium_platform/src/platform.rs b/glium_platform/src/platform.rs
index 8f00d92..db8e56e 100644
--- a/glium_platform/src/platform.rs
+++ b/glium_platform/src/platform.rs
@@ -1,7 +1,7 @@
-use std::cell::{RefCell, Cell};
+use std::cell::RefCell;
use std::rc::Rc;
-use std::sync::{Arc, Mutex, RwLock};
-use raidillon_platform::{Platform, PlatformContext, TimeContext, DebugWireframes, DebugWireframesRef};
+use std::sync::{Arc, Mutex};
+use raidillon_platform::{Platform, PlatformContext, TimeContext};
use glium::backend::glutin::Display;
use glium::backend::glutin::SimpleWindowBuilder;
use glium::glutin::surface::WindowSurface;
@@ -14,12 +14,11 @@ use raidillon_assets::ModelManagerRef;
use raidillon_core::engine::EngineTrait;
use raidillon_core::time;
use raidillon_core::time::Time;
-use crate::render::{BasicMeshRenderingSystem, DebugWireframeRenderingSystem, EguiRenderer, SkyboxRenderingSystem};
+use crate::render::debug_ui::ImguiBridge;
+use crate::render::{BasicMeshRenderingSystem, SkyboxRenderingSystem};
use crate::GliumAssetManager;
use glam::Vec3;
use winit::event::DeviceEvent::MouseMotion;
-use raidillon_core::EguiQueue;
-use raidillon_platform::settings::{Settings, default_config_path};
pub struct GliumPlatform> {
event_loop: EventLoop<()>,
@@ -29,10 +28,6 @@ pub struct GliumPlatform> {
asset_manager: ModelManagerRef,
engine: E,
time: time::Time,
- egui_queue: Rc>,
- settings: Arc>,
- debug_wireframes: DebugWireframesRef,
- should_egui_receive_input_events: Rc| >,
}
impl> Platform for GliumPlatform {
@@ -55,20 +50,9 @@ impl> Platform for GliumPlatfor
let window = Arc::new(Mutex::new(window));
// Install rendering systems in order
- rendering_system_manager.add::(&display, window.clone(), &event_loop);
- rendering_system_manager.add::(&display, window.clone(), &event_loop);
- rendering_system_manager.add::(&display, window.clone(), &event_loop);
- rendering_system_manager.add::(&display, window.clone(), &event_loop);
-
- let egui_queue = Rc::new(RefCell::new(EguiQueue::new()));
-
- let settings = Arc::new(
- RwLock::new(
- Settings::load_or_default(default_config_path()).unwrap()
- )
- );
- let debug_wireframes = Rc::new(RefCell::new(DebugWireframes::new()));
- let should_egui_receive_input_events = Rc::new(Cell::new(false));
+ rendering_system_manager.add::(&display, window.clone());
+ rendering_system_manager.add::(&display, window.clone());
+ rendering_system_manager.add::(&display, window.clone());
Self {
event_loop,
@@ -78,10 +62,6 @@ impl> Platform for GliumPlatfor
asset_manager,
engine,
time,
- egui_queue,
- settings,
- debug_wireframes,
- should_egui_receive_input_events,
}
}
@@ -97,21 +77,9 @@ impl> Platform for GliumPlatfor
frame_height: h as f32,
time_ctx: self.construct_time_ctx(),
window: self.window.clone(),
- egui_queue: self.egui_queue.clone(),
- settings: self.settings.clone(),
- debug_wireframes: self.debug_wireframes.clone(),
- should_egui_receive_input_events: self.should_egui_receive_input_events.clone(),
};
self.engine.initialize(ctx.clone());
- self.settings.read().unwrap().display_settings.apply(&*self.window.lock().unwrap());
-
let _ = &self.event_loop.run(move |event, el| {
- let settings_handle = self.settings.read().unwrap();
- if settings_handle.display_settings.dirty {
- settings_handle.display_settings.apply(&*self.window.lock().unwrap());
- }
- drop(settings_handle);
-
self.rendering_system_manager
.systems
.values_mut()
@@ -123,30 +91,24 @@ impl> Platform for GliumPlatfor
match event {
Event::WindowEvent { event, .. } => match event {
- WindowEvent::Resized(size) => {
- if size.width > 0 && size.height > 0 {
- self.display.resize((size.width, size.height));
- }
- },
WindowEvent::CloseRequested => {
// TODO: Run uninitialize on renderer and engine
- self.settings.read().unwrap().save_to_file(default_config_path());
el.exit();
},
WindowEvent::RedrawRequested => {
let mut target = self.display.draw();
target.clear_color_and_depth((0.1, 0.1, 0.15, 1.0), 1.0);
- let scene = self.engine.current_scene();
+ let (scene, debug_ui_buffer) = (
+ self.engine.current_scene(),
+ self.engine.get_debug_ui_buffer(),
+ );
let mut context = RenderingContext {
scene,
target: &mut target,
- display: &self.display,
asset_manager: self.asset_manager.clone(),
window: self.window.clone(),
- egui_queue: self.egui_queue.clone(),
- debug_wireframes: self.debug_wireframes.clone(),
+ debug_ui_buffer,
env_light_dir: Vec3::new(0.0, -1.0, 0.0),
- should_egui_receive_input_events: self.should_egui_receive_input_events.clone(),
};
self.rendering_system_manager
@@ -154,9 +116,6 @@ impl> Platform for GliumPlatfor
.values_mut()
.for_each(|system| system.render(&mut context));
- // clear debug wireframes after rendering
- self.debug_wireframes.borrow_mut().clear();
-
target.finish().unwrap();
}
_ => {},
diff --git a/glium_platform/src/render/basic.rs b/glium_platform/src/render/basic.rs
index 6553cc0..663b2dd 100644
--- a/glium_platform/src/render/basic.rs
+++ b/glium_platform/src/render/basic.rs
@@ -9,8 +9,6 @@ use raidillon_assets::include_shader;
pub use raidillon_platform::Camera;
use glam::Vec3;
use glium::uniforms::{MagnifySamplerFilter, MinifySamplerFilter, SamplerWrapFunction};
-use winit::event::Event;
-use winit::event_loop::EventLoop;
use raidillon_ecs::{Transform, ModelID};
use raidillon_ecs::components::ModelHandle;
use crate::model::Model;
@@ -23,7 +21,7 @@ pub struct BasicMeshRenderingSystem {
}
impl RenderingSystem for BasicMeshRenderingSystem {
- fn initialize(display: &Display, _window: Arc>, event_loop: &EventLoop<()>) -> Self {
+ fn initialize(display: &Display, _window: Arc>) -> Self {
const VERT_SRC: &str = include_shader!("gl_textured.vert");
const FRAG_SRC: &str = include_shader!("gl_textured.frag");
@@ -59,11 +57,7 @@ impl RenderingSystem for BasicMeshRenderingSystem {
};
// Use HDR-derived environment light direction if provided, otherwise default to downward
- let light_dir_world: Vec3 = if ctx.env_light_dir.length_squared() > 0.0 { ctx.env_light_dir.normalize() } else { Vec3::new(0.0, -1.0, 0.0) };
-
- // Transform light direction to view space (normals/positions are in view space)
- let view_mat3 = glam::Mat3::from_mat4(cam.view());
- let light_dir_view = (view_mat3 * light_dir_world).normalize();
+ let light_dir: Vec3 = if ctx.env_light_dir.length_squared() > 0.0 { ctx.env_light_dir.normalize() } else { Vec3::new(0.0, -1.0, 0.0) };
let asset_manager = ctx.asset_manager.borrow();
@@ -73,43 +67,41 @@ impl RenderingSystem for BasicMeshRenderingSystem {
_ => continue,
};
- let models = match model.downcast_ref::>() {
- Some(models) => models,
+ let model = match model.downcast_ref::() {
+ Some(model) => model,
None => continue,
};
- for model in models {
- let mesh = &model.mesh;
- let mat = &model.material;
+ let mesh = &model.mesh;
+ let mat = &model.material;
- let tex_ref: &SrgbTexture2d = mat.base_color.as_ref().unwrap_or(&self.white_tex);
+ let tex_ref: &SrgbTexture2d = mat.base_color.as_ref().unwrap_or(&self.white_tex);
- let mut sampler = tex_ref.sampled();
- sampler = sampler.wrap_function(SamplerWrapFunction::Repeat);
- sampler = sampler.minify_filter(MinifySamplerFilter::Linear);
- sampler = sampler.magnify_filter(MagnifySamplerFilter::Linear);
+ let mut sampler = tex_ref.sampled();
+ sampler = sampler.wrap_function(SamplerWrapFunction::Repeat);
+ sampler = sampler.minify_filter(MinifySamplerFilter::Linear);
+ sampler = sampler.magnify_filter(MagnifySamplerFilter::Linear);
- let c = mat.base_color_factor;
+ let c = mat.base_color_factor;
- let uniforms = uniform! {
- model: tr.matrix().to_cols_array_2d(),
- view: cam.view().to_cols_array_2d(),
- projection: cam.projection().to_cols_array_2d(),
- u_light: [light_dir_view.x, light_dir_view.y, light_dir_view.z],
- tex: sampler,
- color: [c[0], c[1], c[2]],
- uv_offset: [mat.uv_offset.x, mat.uv_offset.y],
- uv_scale: [mat.uv_scale.x, mat.uv_scale.y],
- };
+ let uniforms = uniform! {
+ model: tr.matrix().to_cols_array_2d(),
+ view: cam.view().to_cols_array_2d(),
+ projection: cam.projection().to_cols_array_2d(),
+ u_light: [light_dir.x, light_dir.y, light_dir.z],
+ tex: sampler,
+ color: [c[0], c[1], c[2]],
+ uv_offset: [mat.uv_offset.x, mat.uv_offset.y],
+ uv_scale: [mat.uv_scale.x, mat.uv_scale.y],
+ };
- ctx.target.draw(
- &mesh.vbuf,
- &mesh.ibuf,
- &self.program,
- &uniforms,
- &self.params,
- ).unwrap();
- }
+ ctx.target.draw(
+ &mesh.vbuf,
+ &mesh.ibuf,
+ &self.program,
+ &uniforms,
+ &self.params,
+ ).unwrap();
}
}
}
diff --git a/glium_platform/src/render/debug_ui.rs b/glium_platform/src/render/debug_ui.rs
new file mode 100644
index 0000000..ac4a06e
--- /dev/null
+++ b/glium_platform/src/render/debug_ui.rs
@@ -0,0 +1,75 @@
+use std::sync::{Arc, Mutex};
+use std::time::Instant;
+use glium::Display;
+use glium::glutin::surface::WindowSurface;
+use imgui::{Context as ImguiContext};
+use imgui_winit_support::{HiDpiMode, WinitPlatform};
+use imgui_glium_renderer::Renderer as ImguiGliumRenderer;
+use winit::window::Window;
+use winit::event::Event;
+use glium::Frame;
+use crate::RenderingSystem;
+use crate::system::RenderingContext;
+
+pub struct ImguiBridge {
+ imgui: ImguiContext,
+ platform: WinitPlatform,
+ renderer: ImguiGliumRenderer,
+ last_frame: Instant,
+ rendered_this_frame: bool,
+}
+
+impl RenderingSystem for ImguiBridge {
+ fn handle_event(&mut self, window: Arc>, event: Event<()>) {
+ let window = window.lock().unwrap();
+ self.platform.handle_event(self.imgui.io_mut(), &*window, &event);
+ }
+
+ fn prepare_frame(&mut self, window: Arc>) {
+ self.rendered_this_frame = false;
+ let now = Instant::now();
+ self.imgui.io_mut().update_delta_time(now - self.last_frame);
+ self.last_frame = now;
+ let window = window.lock().unwrap();
+ self.platform
+ .prepare_frame(self.imgui.io_mut(), &*window)
+ .expect("Failed to prepare frame");
+ }
+
+ fn render(&mut self, ctx: &mut RenderingContext) {
+ if self.rendered_this_frame { return; }
+ self.rendered_this_frame = true;
+
+ let ui = self.imgui.frame();
+ ctx.debug_ui_buffer.write_buffer(&ui);
+
+ {
+ let window = ctx.window.lock().unwrap();
+ self.platform.prepare_render(&ui, &*window);
+ }
+ let draw_data = self.imgui.render();
+ if draw_data.total_vtx_count == 0 && draw_data.total_idx_count == 0 {
+ return;
+ }
+
+ self.renderer.render(ctx.target, draw_data).expect("imgui rendering failed");
+ }
+
+ fn initialize(display: &Display, window: Arc>) -> Self {
+ let mut imgui = ImguiContext::create();
+ imgui.set_ini_filename(None);
+ let mut platform = WinitPlatform::new(&mut imgui);
+ let window = window.lock().unwrap();
+ platform.attach_window(imgui.io_mut(), &*window, HiDpiMode::Default);
+ imgui.fonts().add_font(&[imgui::FontSource::DefaultFontData { config: None }]);
+ let renderer = ImguiGliumRenderer::new(&mut imgui, display).unwrap();
+
+ Self {
+ imgui,
+ platform,
+ renderer,
+ last_frame: Instant::now(),
+ rendered_this_frame: false,
+ }
+ }
+}
diff --git a/glium_platform/src/render/debug_wireframe.rs b/glium_platform/src/render/debug_wireframe.rs
deleted file mode 100644
index 2f259e9..0000000
--- a/glium_platform/src/render/debug_wireframe.rs
+++ /dev/null
@@ -1,80 +0,0 @@
-use std::sync::{Arc, Mutex};
-use glium::{Display, Program, Surface, VertexBuffer, implement_vertex};
-use glium::glutin::surface::WindowSurface;
-use glium::index::PrimitiveType;
-use glium::uniform;
-use winit::event_loop::EventLoop;
-use raidillon_assets::include_shader;
-use crate::system::RenderingContext;
-use crate::RenderingSystem;
-pub use raidillon_platform::Camera;
-
-#[derive(Copy, Clone)]
-struct DebugVertex {
- position: [f32; 3],
- color: [f32; 4],
-}
-
-implement_vertex!(DebugVertex, position, color);
-
-/// renders debug wireframes from the shared buffer
-pub struct DebugWireframeRenderingSystem {
- program: Program,
- params: glium::DrawParameters<'static>,
-}
-
-impl RenderingSystem for DebugWireframeRenderingSystem {
- fn initialize(display: &Display, _window: Arc>, _event_loop: &EventLoop<()>) -> Self {
- const VERT_SRC: &str = include_shader!("debug_wireframe.vert");
- const FRAG_SRC: &str = include_shader!("debug_wireframe.frag");
-
- let program = Program::from_source(display, VERT_SRC, FRAG_SRC, None).unwrap();
-
- let params = glium::DrawParameters {
- depth: glium::Depth {
- test: glium::draw_parameters::DepthTest::IfLess,
- write: false,
- ..Default::default()
- },
- line_width: Some(1.0),
- ..Default::default()
- };
-
- Self { program, params }
- }
-
- fn render(&mut self, ctx: &mut RenderingContext) {
- let debug_wireframes = ctx.debug_wireframes.borrow();
-
- if !debug_wireframes.enabled || debug_wireframes.vertices.is_empty() {
- return;
- }
-
- let cam = match ctx.scene.world.query::<&Camera>().iter().next() {
- Some((_, cam)) => *cam,
- None => return,
- };
-
- let vertices: Vec |