wip: engine enhancements and fixes

This commit is contained in:
reo 2025-08-24 18:49:41 +03:00
parent 9816f14f53
commit cc7921a5fe
27 changed files with 347 additions and 32 deletions

15
Cargo.lock generated
View file

@ -75,9 +75,9 @@ checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
[[package]]
name = "anyhow"
version = "1.0.98"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
[[package]]
name = "arrayref"
@ -1258,10 +1258,19 @@ dependencies = [
"winit",
]
[[package]]
name = "raidillon_ecs"
version = "0.1.0"
dependencies = [
"glam",
"raidillon_assets",
]
[[package]]
name = "raidillon_game"
version = "0.1.0"
dependencies = [
"glam",
"raidillon_core",
"raidillon_glium",
"raidillon_platform",
@ -1278,6 +1287,7 @@ dependencies = [
"indexmap",
"raidillon_assets",
"raidillon_core",
"raidillon_ecs",
"raidillon_platform",
"winit",
]
@ -1286,6 +1296,7 @@ dependencies = [
name = "raidillon_platform"
version = "0.1.0"
dependencies = [
"glam",
"raidillon_assets",
"raidillon_core",
"winit",

View file

@ -4,5 +4,6 @@ members = [
"glium_platform",
"platform",
"asset",
"game"
"game",
"ecs"
]

View file

@ -2,5 +2,3 @@
name = "raidillon_assets"
version = "0.1.0"
edition = "2024"
[dependencies]

View file

@ -1,3 +1,10 @@
pub mod model_manager;
pub use crate::model_manager::{ModelManager, ModelManagerRef};
pub use crate::model_manager::ModelID;
#[macro_export]
macro_rules! include_shader {
($path:expr) => {
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/shaders/", $path))
};
}

View file

@ -1,14 +1,17 @@
use std::any::Any;
use std::cell::RefCell;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::rc::Rc;
pub type ModelManagerRef = Rc<RefCell<Box<dyn ModelManager>>>;
pub type ModelID = PathBuf;
/// The asset manager trait of Raidillon.
pub trait ModelManager {
pub trait ModelManager: Any {
/// Loads a gltf model to VRAM.
fn load_gltf(&mut self, path: &Path);
/// Unloads the loaded model from VRAM.
fn unload_model(&mut self, path: &Path);
fn get_model(&self, id: &ModelID) -> Option<&dyn Any>;
}

View file

@ -0,0 +1,34 @@
#version 330 core
in vec3 v_normal;
in vec2 v_tex;
in vec3 v_position;
out vec4 frag_color;
uniform vec3 u_light;
uniform sampler2D tex;
uniform vec3 color; // base colour factor (acts as solid colour when no texture)
void main() {
// Combine base texture (or constant white) with colour factor supplied by CPU.
vec3 base_col = texture(tex, v_tex).rgb * color;
vec3 ambient_color = base_col * 0.2;
vec3 diffuse_color = base_col * 0.6;
vec3 specular_color = vec3(1.0);
// u_light is the direction **from the light towards the fragment**.
float diffuse = max(dot(normalize(v_normal), normalize(u_light)), 0.0);
vec3 camera_dir = normalize(-v_position);
vec3 half_dir = normalize(normalize(u_light) + camera_dir);
float specular = pow(max(dot(half_dir, normalize(v_normal)), 0.0), 16.0);
vec3 result = ambient_color + diffuse * diffuse_color + specular * specular_color;
// Convert from linear to sRGB for display (approximate γ-correction)
result = pow(result, vec3(1.0 / 2.2));
frag_color = vec4(result, 1.0);
}

View file

@ -0,0 +1,23 @@
#version 330 core
in vec3 position;
in vec3 normal;
in vec2 tex_coords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform vec2 uv_offset;
uniform vec2 uv_scale;
out vec3 v_normal;
out vec2 v_tex;
out vec3 v_position;
void main() {
mat4 modelview = view * model;
v_normal = transpose(inverse(mat3(modelview))) * normal;
v_tex = tex_coords * uv_scale + uv_offset;
v_position = (modelview * vec4(position, 1.0)).xyz;
gl_Position = projection * modelview * vec4(position, 1.0);
}

View file

@ -0,0 +1,22 @@
#version 330 core
in vec3 direction;
out vec4 frag_color;
uniform sampler2D equirect;
const vec2 inv_atan = vec2(0.15915494309, 0.31830988618);
vec2 sample_spherical_map(vec3 v) {
vec2 uv = vec2(atan(v.z, v.x), asin(v.y));
uv *= inv_atan;
uv += 0.5;
return uv;
}
void main() {
vec2 uv = sample_spherical_map(normalize(direction));
vec3 color = texture(equirect, uv).rgb;
frag_color = vec4(color, 1.0);
}

View file

@ -0,0 +1,15 @@
#version 330 core
in vec3 position;
uniform mat4 view;
uniform mat4 projection;
out vec3 direction;
void main() {
direction = position;
vec4 pos = projection * view * vec4(position, 1.0);
gl_Position = pos.xyww;
}

View file

@ -2,9 +2,9 @@ use std::collections::HashMap;
use std::path::{Path, PathBuf};
pub struct Scene {
title: String,
world: hecs::World,
skybox_texture_path: Option<PathBuf>,
pub title: String,
pub world: hecs::World,
pub skybox_texture_path: Option<PathBuf>,
}
impl Scene {

View file

@ -16,7 +16,7 @@ pub trait System {
fn update(&mut self, ctx: &mut SystemContext);
}
pub type SystemID = String;
pub type SystemID = &'static str;
pub struct SystemManager {
pub systems: IndexMap<SystemID, Box<dyn System>>,
}

8
ecs/Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "raidillon_ecs"
version = "0.1.0"
edition = "2024"
[dependencies]
glam = "0.30.5"
raidillon_assets = { path = "../asset" }

15
ecs/src/components.rs Normal file
View file

@ -0,0 +1,15 @@
use glam::{Vec3, Quat, Mat4};
pub use raidillon_assets::ModelID;
#[derive(Copy, Clone)]
pub struct Transform {
pub translation: Vec3,
pub rotation: Quat,
pub scale: Vec3,
}
impl Transform {
pub fn matrix(&self) -> Mat4 {
Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
}
}

3
ecs/src/lib.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod components;
pub use components::{Transform, ModelID};

View file

@ -11,3 +11,4 @@ glium = ["raidillon_glium"]
raidillon_core = { path = "../core" }
raidillon_platform = { path = "../platform" }
raidillon_glium = { path = "../glium_platform", optional = true }
glam = "0.30.5"

View file

@ -1,14 +1,39 @@
use raidillon_core::{Engine, Scene};
use raidillon_platform::Platform;
use glam::Vec3;
use raidillon_core::{Engine, Scene, System};
use raidillon_core::system::SystemContext;
use raidillon_platform::{Platform, Camera};
#[cfg(feature = "glium")]
use raidillon_glium::GliumPlatform;
const RENDERING_TEST_SYSTEM: &str = "rendering_test_system";
struct RenderingTestSystem;
impl System for RenderingTestSystem {
fn initialize(&mut self) {}
fn load_world(&mut self, ctx: &mut SystemContext) {
ctx.scene.world.spawn((Camera {
eye: Vec3::new(0.0, 0.0, 2.0),
center: Vec3::ZERO,
up: Vec3::Y,
fovy: 60_f32.to_radians(),
aspect: 1920 as f32 / 1080 as f32, // FIXME
znear: 0.1,
zfar: 100.0,
},));
// TODO: Load a sample glTF file
}
fn update(&mut self, ctx: &mut SystemContext) {}
}
fn main() {
let mut engine = Engine::new();
// Define systems
// engine.system_manager.add_system("spawn_chunks".to_string(), ChunkSystem);
// engine.system_manager.add_system("movement".to_string(), MovementSystem);
engine.system_manager.add_system(RENDERING_TEST_SYSTEM, Box::new(RenderingTestSystem));
// Set up the scene
let main_scene_id = "Main".to_owned();

View file

@ -11,5 +11,6 @@ gltf = { version = "1.4.1", features = ["import", "utils", "KHR_texture_transfor
raidillon_platform = { path = "../platform" }
raidillon_core = { path = "../core" }
raidillon_assets = { path = "../asset" }
raidillon_ecs = { path = "../ecs" }
winit = "0.30.12"
indexmap = "2.10.0"

View file

@ -1,3 +1,5 @@
use std::any::Any;
use std::cell::RefCell;
use raidillon_assets::{ModelManagerRef, ModelManager};
use crate::model::Model;
use std::path::{Path, PathBuf};
@ -5,10 +7,12 @@ use crate::gltf_loader::load_gltf;
use glium::backend::Facade;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::rc::Rc;
use raidillon_assets::model_manager::ModelID;
/// Glium platform asset manager implementation.
pub struct GliumAssetManager {
models: HashMap<PathBuf, Model>,
pub models: HashMap<PathBuf, Model>,
facade: Box<dyn Facade>,
}
@ -42,4 +46,8 @@ impl ModelManager for GliumAssetManager {
// }
// }
// }
fn get_model(&self, id: &ModelID) -> Option<&dyn Any> {
self.models.get(id).map(|model| model as &dyn Any)
}
}

View file

@ -5,6 +5,6 @@ pub mod gltf_loader;
pub mod system;
mod render;
pub use assets::GliumAssetManager;
pub use assets::{GliumAssetManager};
pub use platform::GliumPlatform;
pub use system::RenderingSystem;

View file

@ -1,3 +1,4 @@
use std::any::{Any, TypeId};
use glium::{IndexBuffer, VertexBuffer, implement_vertex};
use glium::texture::{SrgbTexture2d, Texture2d};
use glium::uniforms::SamplerBehavior;

View file

@ -1,5 +1,6 @@
use std::cell::RefCell;
use std::rc::Rc;
use std::str::FromStr;
use std::sync::{Arc, RwLock};
use raidillon_platform::Platform;
use glium::winit::event_loop::EventLoop;
@ -8,12 +9,14 @@ use glium::backend::glutin::Display;
use glium::glutin::surface::WindowSurface;
use glium::backend::glutin::SimpleWindowBuilder;
use glium::Surface;
use crate::system::{RenderingSystemManager, RenderingSystem, RenderingContext};
use crate::system::{RenderingSystemManager, RenderingSystem, RenderingContext, SystemID};
use winit::event::{Event, WindowEvent};
use raidillon_assets::{ModelManager, ModelManagerRef};
use raidillon_core::Engine;
use crate::GliumAssetManager;
use crate::render::BasicRenderingSystem;
use crate::{GliumAssetManager};
use crate::render::BasicMeshRenderingSystem;
pub const MESH_RENDERER: &str = "mesh_renderer";
pub struct GliumPlatform {
event_loop: EventLoop<()>,
@ -36,8 +39,15 @@ impl Platform for GliumPlatform {
.build(&event_loop);
let asset_manager: ModelManagerRef = Rc::new(RefCell::new(Box::new(GliumAssetManager::new(Box::new(display.clone())))));
let rendering_system_manager = RenderingSystemManager::new();
let mut rendering_system_manager = RenderingSystemManager::new();
engine.set_model_manager(asset_manager.clone());
// Install rendering systems
rendering_system_manager.add_system(
MESH_RENDERER,
Box::new(BasicMeshRenderingSystem::initialize(&display))
);
Self {
event_loop,
window,
@ -49,6 +59,7 @@ impl Platform for GliumPlatform {
}
fn run(mut self) {
self.engine.initialize();
let _ = &self.event_loop.run(move |event, el| {
match event {
Event::WindowEvent { event, .. } => match event {

View file

@ -1,11 +1,111 @@
use crate::RenderingSystem;
use std::any::Any;
use glium::{uniform, Display, Program, Surface};
use glium::glutin::surface::WindowSurface;
use glium::texture::{RawImage2d, SrgbTexture2d};
use crate::{GliumAssetManager, RenderingSystem};
use crate::system::RenderingContext;
use raidillon_assets::include_shader;
pub use raidillon_platform::Camera;
use glam::Vec3;
use glium::uniforms::{MagnifySamplerFilter, MinifySamplerFilter, SamplerWrapFunction};
use raidillon_ecs::{Transform, ModelID};
use crate::model::Model;
/// A basic renderer pipeline step.
pub struct BasicRenderingSystem;
pub struct BasicMeshRenderingSystem {
program: Program,
white_tex: SrgbTexture2d,
params: glium::DrawParameters<'static>,
}
impl RenderingSystem for BasicMeshRenderingSystem {
fn initialize(display: &Display<WindowSurface>) -> Self {
const VERT_SRC: &str = include_shader!("gl_textured.vert");
const FRAG_SRC: &str = include_shader!("gl_textured.frag");
let program = Program::from_source(display, VERT_SRC, FRAG_SRC, None).unwrap();
let white_tex = {
let data = vec![255u8, 255u8, 255u8, 255u8];
let raw = RawImage2d::from_raw_rgba(data, (1, 1));
SrgbTexture2d::new(display, raw).unwrap()
};
let params = glium::DrawParameters {
depth: glium::Depth {
test: glium::draw_parameters::DepthTest::IfLess,
write: true,
.. Default::default()
},
.. Default::default()
};
Self {
program, white_tex, params
}
}
impl RenderingSystem for BasicRenderingSystem {
fn render(&mut self, ctx: &mut RenderingContext) {
todo!()
let cam = match ctx.scene.world.query::<&Camera>().iter().next() {
Some((_, cam)) => *cam,
None => {
eprintln!("[renderer] No camera component found. Skipping frame");
return;
}
};
// Direction from the light source (0,+Y) towards the scene.
let light_dir: Vec3 = Vec3::new(0.0, -1.0, 0.0).normalize();
// let asset_manager = ctx.asset_manager.borrow();
// let any_ref: &dyn Any = &**asset_manager;
// if let Some(glium_manager) = any_ref.downcast_ref::<GliumAssetManager>() {
// &glium_manager.models;
// }
let asset_manager = ctx.asset_manager.borrow();
for (_, (tr, mh)) in ctx.scene.world.query::<(&Transform, &ModelID)>().iter() {
let model = match asset_manager.get_model(mh) {
Some(model) => model,
_ => continue,
};
let model = match model.downcast_ref::<Model>() {
Some(model) => model,
None => continue,
};
let mesh = &model.mesh;
let mat = &model.material;
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 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],
};
ctx.target.draw(
&mesh.vbuf,
&mesh.ibuf,
&self.program,
&uniforms,
&self.params,
).unwrap();
}
}
}

View file

@ -1,2 +1,2 @@
mod basic;
pub use basic::BasicRenderingSystem;
pub use basic::BasicMeshRenderingSystem;

View file

@ -1,8 +1,11 @@
use std::cell::RefCell;
use std::rc::Rc;
use raidillon_core::Scene;
use glium::Frame;
use crate::GliumAssetManager;
use glium::{Display, Frame};
use glium::glutin::surface::WindowSurface;
use indexmap::IndexMap;
use raidillon_assets::ModelManagerRef;
use crate::GliumAssetManager;
pub struct RenderingContext<'a> {
pub scene: &'a Scene,
@ -14,10 +17,10 @@ pub struct RenderingContext<'a> {
/// This is unrelated to the main System trait in core.
pub trait RenderingSystem {
fn render(&mut self, ctx: &mut RenderingContext);
fn compile_shaders(&mut self) {}
fn initialize(display: &Display<WindowSurface>) -> Self where Self: Sized;
}
type SystemID = String;
pub type SystemID = &'static str;
pub struct RenderingSystemManager {
pub systems: IndexMap<SystemID, Box<dyn RenderingSystem>>,

View file

@ -7,3 +7,4 @@ edition = "2024"
winit = "0.30.12"
raidillon_core = { path = "../core" }
raidillon_assets = { path = "../asset" }
glam = "0.30.5"

24
platform/src/camera.rs Normal file
View file

@ -0,0 +1,24 @@
use glam::{Mat4, Vec3};
#[derive(Copy, Clone)]
pub struct Camera {
pub eye: Vec3,
pub center: Vec3,
pub up: Vec3,
pub fovy: f32,
pub aspect: f32,
pub znear: f32,
pub zfar: f32,
}
impl Camera {
pub fn view(&self) -> Mat4 {
Mat4::look_at_rh(self.eye, self.center, self.up)
}
pub fn projection(&self) -> Mat4 {
Mat4::perspective_rh(self.fovy, self.aspect, self.znear, self.zfar)
}
pub fn view_proj(&self) -> Mat4 {
self.projection() * self.view()
}
}

View file

@ -1,5 +1,5 @@
pub mod platform;
pub mod context;
mod camera;
pub use context::PlatformContext;
pub use platform::Platform;
pub use camera::Camera;