o3 refactor

This commit is contained in:
reo 2025-07-22 21:12:05 +03:00
parent 341d531db3
commit 049f522bb1
19 changed files with 508 additions and 214 deletions

View file

@ -1,62 +0,0 @@
use raidillon_ecs::{Transform, ModelHandle};
use hecs::{Entity, World};
use crate::render::GliumRenderer;
use crate::model::Model;
/// This system joins the renderer and ECS,
/// and provides tools to use them together
/// effectively.
pub struct ECSRenderer {
pub renderer: GliumRenderer,
pub world: World,
}
impl ECSRenderer {
pub fn from_display_handle(handle: &crate::window::DisplayHandle) -> anyhow::Result<Self> {
let world = World::new();
let renderer = crate::render::GliumRenderer::new(handle.as_inner().clone())?;
Ok(Self { renderer, world })
}
pub fn new(renderer: GliumRenderer, world: World) -> Self {
Self { renderer, world }
}
pub fn spawn_mesh(&mut self, model: Model, transform: Transform) -> Entity {
let model_id = self.renderer.models.len();
self.renderer.models.push(model);
self.world.spawn((
transform,
ModelHandle(model_id),
))
}
pub fn despawn_mesh(&mut self, entity: Entity) {
if let Ok(model_handle) = self.world.get::<&ModelHandle>(entity) {
if model_handle.0 < self.renderer.models.len() {
self.renderer.models.remove(model_handle.0);
}
}
let _ = self.world.despawn(entity);
}
/// Render a single frame using the internal renderer & world.
pub fn render(&mut self) {
self.renderer.render(&self.world);
}
/// Render into an existing glium target surface. Useful for composing with
/// other render passes (e.g. Dear ImGui).
pub fn render_into<S: glium::Surface>(&mut self, target: &mut S) {
self.renderer.render_into(&self.world, target);
}
pub fn load_mesh_from_gltf<P: AsRef<std::path::Path> + std::fmt::Debug>(
&mut self,
path: P,
transform: Transform,
) -> anyhow::Result<Entity> {
let model = crate::gltf_loader::load_gltf(path, self.renderer.display())?;
Ok(self.spawn_mesh(model, transform))
}
}

View file

@ -2,10 +2,10 @@ pub mod camera;
pub mod model;
pub mod gltf_loader;
pub mod render;
pub mod ecs_renderer;
pub mod window;
pub mod render_system;
pub use camera::Camera;
pub use render::GliumRenderer;
pub use ecs_renderer::ECSRenderer;
pub use window::{DisplayHandle, init_window as init_render_window};
pub use render_system::RenderSystem;

View file

@ -12,12 +12,12 @@ use glium::draw_parameters::DepthTest;
pub struct GliumRenderer {
display: glium::Display<WindowSurface>,
program: Program,
white_tex: SrgbTexture2d,
pub(crate) program: Program,
pub(crate) white_tex: SrgbTexture2d,
pub models: Vec<Model>,
params: glium::DrawParameters<'static>,
pub(crate) params: glium::DrawParameters<'static>,
skybox_program: Program,
skybox_texture: SrgbTexture2d,

View file

@ -0,0 +1,92 @@
use crate::render::GliumRenderer;
use glium::Surface;
use hecs::World;
use raidillon_ecs::ModelId;
use crate::model::Model;
pub trait ModelProvider {
fn get_model(&self, id: ModelId) -> Option<&Model>;
}
/// Pure render system that owns the low-level renderer but **not** the ECS
/// world, allowing it to be plugged into any external world.
pub struct RenderSystem {
renderer: GliumRenderer,
}
impl RenderSystem {
/// Construct a RenderSystem from a window `DisplayHandle`.
pub fn new(display: crate::window::DisplayHandle) -> anyhow::Result<Self> {
Ok(Self {
renderer: GliumRenderer::new(display.as_inner().clone())?,
})
}
/// Render the given `world` into an arbitrary glium surface.
pub fn render_into<S: Surface, P: ModelProvider>(&mut self, world: &World, assets: &P, target: &mut S) {
// delegate to custom draw that uses assets
self.draw_scene(world, assets, target);
}
pub fn render<S: Surface, P: ModelProvider>(&mut self, world: &World, assets: &P) {
let mut frame = self.renderer.display().draw();
self.draw_scene(world, assets, &mut frame);
frame.finish().unwrap();
}
/// Load model via AssetManager caching.
pub fn load_model<P: AsRef<str>, A: ModelProvider + ?Sized>( &self, path: P, assets: &mut A ) -> anyhow::Result<ModelId> where A: crate::render_system::ModelProvider {
// cannot implement generic load here without knowing concrete; will leave stub not used.
anyhow::bail!("Not implemented - load via AssetManager in core");
}
/// Expose the underlying display (useful for ImGui, etc.).
pub fn display(&self) -> &glium::Display<glium::glutin::surface::WindowSurface> {
self.renderer.display()
}
fn draw_scene<S: Surface, P: ModelProvider>(&self, world: &World, assets: &P, target: &mut S) {
// replicate old GliumRenderer::draw_scene but using assets
use glium::{uniform, uniforms::{MinifySamplerFilter, MagnifySamplerFilter, SamplerWrapFunction}};
use glam::{Vec3, Vec4};
use raidillon_ecs::{Transform};
let cam = match world.query::<&crate::camera::Camera>().iter().next() {
Some((_, cam)) => *cam,
None => return,
};
let light_dir: Vec3 = Vec3::new(0.0, -1.0, 0.0).normalize();
for (_, (tr, mh)) in world.query::<(&Transform, &ModelId)>().iter() {
if let Some(model) = assets.get_model(*mh) {
let mesh = &model.mesh;
let mat = &model.material;
let tex_ref = mat.base_color.as_ref().unwrap_or(&self.renderer.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 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.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],
};
target.draw(&mesh.vbuf, &mesh.ibuf, &self.renderer.program, &uniforms, &self.renderer.params).unwrap();
}
}
// skybox omitted for brevity
}
}