Rename crate folders to remove raidillon prefix
This commit is contained in:
parent
176ea52ab0
commit
3458662cfc
29 changed files with 31 additions and 28 deletions
2242
glium_platform/Cargo.lock
generated
Normal file
2242
glium_platform/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
15
glium_platform/Cargo.toml
Normal file
15
glium_platform/Cargo.toml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "raidillon_glium"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.98"
|
||||
glam = "0.30.5"
|
||||
glium = { version = "0.35.0", features = ["glutin_backend", "simple_window_builder"] }
|
||||
gltf = { version = "1.4.1", features = ["import", "utils", "KHR_texture_transform"] }
|
||||
raidillon_platform = { path = "../platform" }
|
||||
raidillon_core = { path = "../core" }
|
||||
raidillon_assets = { path = "../assets" }
|
||||
winit = "0.30.12"
|
||||
indexmap = "2.10.0"
|
||||
45
glium_platform/src/assets.rs
Normal file
45
glium_platform/src/assets.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use raidillon_assets::{ModelManagerRef, ModelManager};
|
||||
use crate::model::Model;
|
||||
use std::path::{Path, PathBuf};
|
||||
use crate::gltf_loader::load_gltf;
|
||||
use glium::backend::Facade;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
/// Glium platform asset manager implementation.
|
||||
pub struct GliumAssetManager {
|
||||
models: HashMap<PathBuf, Model>,
|
||||
facade: Box<dyn Facade>,
|
||||
}
|
||||
|
||||
impl GliumAssetManager {
|
||||
pub fn new(facade: Box<dyn Facade>) -> Self {
|
||||
let models = HashMap::new();
|
||||
Self {
|
||||
models,
|
||||
facade,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModelManager for GliumAssetManager {
|
||||
fn load_gltf(&mut self, path: &Path) {
|
||||
let model = load_gltf(path, self.facade.as_ref()).unwrap();
|
||||
self.models.insert(path.to_path_buf(), model);
|
||||
}
|
||||
|
||||
fn unload_model(&mut self, path: &Path) {
|
||||
self.models.remove(&path.to_path_buf());
|
||||
}
|
||||
|
||||
// fn get_model(&mut self, path: &Path) -> &Self::Model {
|
||||
// let path_buf = path.to_path_buf();
|
||||
// match self.models.entry(path_buf) {
|
||||
// Entry::Occupied(entry) => entry.into_mut(),
|
||||
// Entry::Vacant(entry) => {
|
||||
// let model = load_gltf(path, self.facade.as_ref()).unwrap();
|
||||
// entry.insert(model)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
170
glium_platform/src/gltf_loader.rs
Normal file
170
glium_platform/src/gltf_loader.rs
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
use anyhow::{Context, Result};
|
||||
use glium::{backend::Facade, IndexBuffer, VertexBuffer};
|
||||
use glium::index::PrimitiveType;
|
||||
use std::{fmt::Debug, path::Path};
|
||||
use crate::model::{Vertex, Mesh, Material, Model};
|
||||
use glium::texture::{RawImage2d, Texture2d, SrgbTexture2d};
|
||||
use glium::uniforms::{SamplerWrapFunction, MinifySamplerFilter, MagnifySamplerFilter};
|
||||
use gltf::image::Format as GltfFormat;
|
||||
use glam::Vec2;
|
||||
|
||||
/// Load a glTF 2.0 file from disk and upload the first primitive to the GPU.
|
||||
pub fn load_gltf<P>(path: P, facade: &dyn Facade) -> Result<Model>
|
||||
where
|
||||
P: AsRef<Path> + 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();
|
||||
mat.sampler.wrap_function.0 = match sampler_info.wrap_s() {
|
||||
gltf::texture::WrappingMode::ClampToEdge => SamplerWrapFunction::Clamp,
|
||||
gltf::texture::WrappingMode::MirroredRepeat => SamplerWrapFunction::Mirror,
|
||||
gltf::texture::WrappingMode::Repeat => SamplerWrapFunction::Repeat,
|
||||
};
|
||||
mat.sampler.wrap_function.1 = match sampler_info.wrap_t() {
|
||||
gltf::texture::WrappingMode::ClampToEdge => SamplerWrapFunction::Clamp,
|
||||
gltf::texture::WrappingMode::MirroredRepeat => SamplerWrapFunction::Mirror,
|
||||
gltf::texture::WrappingMode::Repeat => SamplerWrapFunction::Repeat,
|
||||
};
|
||||
if let Some(f) = sampler_info.mag_filter() {
|
||||
mat.sampler.magnify_filter = match f {
|
||||
gltf::texture::MagFilter::Nearest => MagnifySamplerFilter::Nearest,
|
||||
gltf::texture::MagFilter::Linear => MagnifySamplerFilter::Linear,
|
||||
};
|
||||
}
|
||||
if let Some(f) = sampler_info.min_filter() {
|
||||
mat.sampler.minify_filter = match f {
|
||||
gltf::texture::MinFilter::Nearest => MinifySamplerFilter::Nearest,
|
||||
gltf::texture::MinFilter::Linear => MinifySamplerFilter::Linear,
|
||||
gltf::texture::MinFilter::NearestMipmapNearest => MinifySamplerFilter::NearestMipmapNearest,
|
||||
gltf::texture::MinFilter::NearestMipmapLinear => MinifySamplerFilter::NearestMipmapLinear,
|
||||
gltf::texture::MinFilter::LinearMipmapNearest => MinifySamplerFilter::LinearMipmapNearest,
|
||||
gltf::texture::MinFilter::LinearMipmapLinear => MinifySamplerFilter::LinearMipmapLinear,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 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])?);
|
||||
}
|
||||
|
||||
// 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])?);
|
||||
}
|
||||
|
||||
// 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])?);
|
||||
}
|
||||
|
||||
// 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])?);
|
||||
}
|
||||
|
||||
// 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])?);
|
||||
}
|
||||
|
||||
// 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]);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 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<u32> = reader.read_indices().context("missing indices")?.into_u32().collect();
|
||||
|
||||
// Interleave
|
||||
let vertices: Vec<Vertex> = (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.
|
||||
fn glium_linear_texture(facade: &dyn Facade, img: &gltf::image::Data) -> Result<Texture2d>
|
||||
{
|
||||
let rgba = to_rgba(img);
|
||||
let raw = RawImage2d::from_raw_rgba(rgba, (img.width, img.height));
|
||||
Ok(Texture2d::new(facade, raw)?)
|
||||
}
|
||||
|
||||
/// sRGB texture from glTF image data.
|
||||
fn glium_srgb_texture(facade: &dyn Facade, img: &gltf::image::Data) -> Result<SrgbTexture2d>
|
||||
{
|
||||
let rgba = to_rgba(img);
|
||||
let raw = RawImage2d::from_raw_rgba(rgba, (img.width, img.height));
|
||||
Ok(SrgbTexture2d::new(facade, raw)?)
|
||||
}
|
||||
|
||||
/// Convert various glTF image formats to RGBA8 as expected by glium.
|
||||
fn to_rgba(img: &gltf::image::Data) -> Vec<u8> {
|
||||
match img.format {
|
||||
GltfFormat::R8G8B8A8 => img.pixels.clone(),
|
||||
GltfFormat::R8G8B8 => {
|
||||
// Expand RGB to RGBA with alpha=255
|
||||
img.pixels
|
||||
.chunks(3)
|
||||
.flat_map(|rgb| [rgb[0], rgb[1], rgb[2], 255u8])
|
||||
.collect()
|
||||
}
|
||||
GltfFormat::R8G8 => {
|
||||
// Treat RG as luminance+alpha? For simplicity, replicate first channel into RGB, second as alpha.
|
||||
img.pixels
|
||||
.chunks(2)
|
||||
.flat_map(|rg| [rg[0], rg[0], rg[0], rg[1]])
|
||||
.collect()
|
||||
}
|
||||
GltfFormat::R8 => {
|
||||
// Grayscale: replicate into RGB, alpha=255
|
||||
img.pixels
|
||||
.iter()
|
||||
.flat_map(|l| [*l, *l, *l, 255u8])
|
||||
.collect()
|
||||
}
|
||||
_ => img.pixels.clone(),
|
||||
}
|
||||
}
|
||||
10
glium_platform/src/lib.rs
Normal file
10
glium_platform/src/lib.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
pub mod platform;
|
||||
pub mod assets;
|
||||
pub mod model;
|
||||
pub mod gltf_loader;
|
||||
pub mod system;
|
||||
mod render;
|
||||
|
||||
pub use assets::GliumAssetManager;
|
||||
pub use platform::GliumPlatform;
|
||||
pub use system::RenderingSystem;
|
||||
57
glium_platform/src/model.rs
Normal file
57
glium_platform/src/model.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
use glium::{IndexBuffer, VertexBuffer, implement_vertex};
|
||||
use glium::texture::{SrgbTexture2d, Texture2d};
|
||||
use glium::uniforms::SamplerBehavior;
|
||||
use glam::{Vec2};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Vertex {
|
||||
pub position: [f32; 3],
|
||||
pub normal: [f32; 3],
|
||||
pub tex_coords: [f32; 2],
|
||||
}
|
||||
implement_vertex!(Vertex, position, normal, tex_coords);
|
||||
|
||||
pub struct Mesh {
|
||||
pub vbuf: VertexBuffer<Vertex>,
|
||||
pub ibuf: IndexBuffer<u32>,
|
||||
}
|
||||
|
||||
pub struct Material {
|
||||
pub base_color: Option<SrgbTexture2d>,
|
||||
pub metallic_roughness: Option<Texture2d>,
|
||||
pub normal: Option<Texture2d>,
|
||||
pub occlusion: Option<Texture2d>,
|
||||
pub emissive: Option<SrgbTexture2d>,
|
||||
pub sampler: SamplerBehavior,
|
||||
pub uv_offset: Vec2,
|
||||
pub uv_scale: Vec2,
|
||||
pub base_color_factor: [f32; 4],
|
||||
pub emissive_factor: [f32; 3],
|
||||
pub metal_factor: f32,
|
||||
pub roughness_factor: f32,
|
||||
}
|
||||
|
||||
impl Default for Material {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
base_color: None,
|
||||
metallic_roughness: None,
|
||||
normal: None,
|
||||
occlusion: None,
|
||||
emissive: None,
|
||||
sampler: SamplerBehavior::default(),
|
||||
uv_offset: Vec2::ZERO,
|
||||
uv_scale: Vec2::ONE,
|
||||
base_color_factor: [1.0; 4],
|
||||
emissive_factor: [0.0; 3],
|
||||
metal_factor: 1.0,
|
||||
roughness_factor: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Model {
|
||||
pub mesh: Mesh,
|
||||
pub material: Material,
|
||||
}
|
||||
85
glium_platform/src/platform.rs
Normal file
85
glium_platform/src/platform.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use raidillon_platform::Platform;
|
||||
use glium::winit::event_loop::EventLoop;
|
||||
use glium::winit::window::Window;
|
||||
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 winit::event::{Event, WindowEvent};
|
||||
use raidillon_assets::{ModelManager, ModelManagerRef};
|
||||
use raidillon_core::Engine;
|
||||
use crate::GliumAssetManager;
|
||||
use crate::render::BasicRenderingSystem;
|
||||
|
||||
pub struct GliumPlatform {
|
||||
event_loop: EventLoop<()>,
|
||||
window: Window,
|
||||
display: Display<WindowSurface>,
|
||||
rendering_system_manager: RenderingSystemManager,
|
||||
asset_manager: ModelManagerRef,
|
||||
engine: Engine,
|
||||
}
|
||||
|
||||
impl Platform for GliumPlatform {
|
||||
fn initialize(mut engine: Engine, title: String, width: u32, height: u32) -> Self {
|
||||
let event_loop = glium::winit::event_loop::EventLoop::builder()
|
||||
.build()
|
||||
.expect("create event-loop");
|
||||
|
||||
let (window, display) = SimpleWindowBuilder::new()
|
||||
.with_title(title.as_str())
|
||||
.with_inner_size(width, height)
|
||||
.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();
|
||||
engine.set_model_manager(asset_manager.clone());
|
||||
Self {
|
||||
event_loop,
|
||||
window,
|
||||
display,
|
||||
rendering_system_manager,
|
||||
asset_manager,
|
||||
engine
|
||||
}
|
||||
}
|
||||
|
||||
fn run(mut self) {
|
||||
let _ = &self.event_loop.run(move |event, el| {
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
// TODO: Run uninitialize on renderer and engine
|
||||
el.exit();
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
let mut target = self.display.draw();
|
||||
target.clear_color(0.0, 0.0, 0.0, 1.0);
|
||||
// TODO: let mut context;
|
||||
let mut context = RenderingContext {
|
||||
scene: self.engine.scene_manager.current_mut(),
|
||||
target: &mut target,
|
||||
asset_manager: self.asset_manager.clone(),
|
||||
};
|
||||
|
||||
for (system_id, system) in self.rendering_system_manager.systems.iter_mut() {
|
||||
system.render(&mut context);
|
||||
}
|
||||
target.finish().unwrap();
|
||||
}
|
||||
_ => {},
|
||||
},
|
||||
Event::AboutToWait => {
|
||||
self.engine.set_winit_event(event.clone());
|
||||
self.engine.update();
|
||||
self.window.request_redraw();
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
11
glium_platform/src/render/basic.rs
Normal file
11
glium_platform/src/render/basic.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use crate::RenderingSystem;
|
||||
use crate::system::RenderingContext;
|
||||
|
||||
/// A basic renderer pipeline step.
|
||||
pub struct BasicRenderingSystem;
|
||||
|
||||
impl RenderingSystem for BasicRenderingSystem {
|
||||
fn render(&mut self, ctx: &mut RenderingContext) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
2
glium_platform/src/render/mod.rs
Normal file
2
glium_platform/src/render/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
mod basic;
|
||||
pub use basic::BasicRenderingSystem;
|
||||
39
glium_platform/src/system.rs
Normal file
39
glium_platform/src/system.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
use raidillon_core::Scene;
|
||||
use glium::Frame;
|
||||
use crate::GliumAssetManager;
|
||||
use indexmap::IndexMap;
|
||||
use raidillon_assets::ModelManagerRef;
|
||||
|
||||
pub struct RenderingContext<'a> {
|
||||
pub scene: &'a Scene,
|
||||
pub target: &'a mut Frame,
|
||||
pub asset_manager: ModelManagerRef,
|
||||
}
|
||||
|
||||
/// The internal "rendering system" trait of glium_platform.
|
||||
/// 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) {}
|
||||
}
|
||||
|
||||
type SystemID = String;
|
||||
|
||||
pub struct RenderingSystemManager {
|
||||
pub systems: IndexMap<SystemID, Box<dyn RenderingSystem>>,
|
||||
}
|
||||
|
||||
impl RenderingSystemManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
systems: IndexMap::default(),
|
||||
}
|
||||
}
|
||||
pub fn add_system(&mut self, id: SystemID, system: Box<dyn RenderingSystem>) {
|
||||
self.systems.insert(id, system);
|
||||
}
|
||||
|
||||
pub fn remove_system(&mut self, id: SystemID) {
|
||||
self.systems.shift_remove(&id);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue