Add physics crate with rapier3d
This commit is contained in:
parent
a3d3f641cd
commit
8c9c310198
13 changed files with 756 additions and 102 deletions
|
|
@ -4,12 +4,12 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.98"
|
||||
glam = "0.30.4"
|
||||
glium = { version = "0.35.0", features = ["glutin_backend", "simple_window_builder"] }
|
||||
gltf = { version = "1.4.1", features = ["import", "utils", "KHR_texture_transform"] }
|
||||
glutin = { version = "0.32.3", default-features = false }
|
||||
hecs = "0.10.5"
|
||||
image = "0.25.6"
|
||||
raidillon_ecs = { path = "../raidillon_ecs" }
|
||||
image = "0.25"
|
||||
anyhow = "1"
|
||||
hecs = "0.10.5"
|
||||
glium = { version = "0.35", features = ["glutin"] }
|
||||
winit = "0.30"
|
||||
raidillon_ecs = { path = "../raidillon_ecs" }
|
||||
rapier3d = "0.26.1"
|
||||
gltf = { version = "1.4.1", features = ["import", "utils", "KHR_texture_transform"] }
|
||||
|
|
|
|||
79
raidillon_render/src/debug.rs
Normal file
79
raidillon_render/src/debug.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
use glium::{implement_vertex, VertexBuffer, Surface, Program, DrawParameters, index::NoIndices, index::PrimitiveType, uniform};
|
||||
use glium::glutin::surface::WindowSurface;
|
||||
use rapier3d::prelude::{ColliderSet, Aabb};
|
||||
use glam::{Mat4};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct DebugVertex {
|
||||
position: [f32; 3],
|
||||
}
|
||||
|
||||
implement_vertex!(DebugVertex, position);
|
||||
|
||||
pub struct ColliderDebugRenderer {
|
||||
program: Program,
|
||||
display: glium::Display<WindowSurface>,
|
||||
}
|
||||
|
||||
impl ColliderDebugRenderer {
|
||||
pub fn new(display: &glium::Display<WindowSurface>) -> anyhow::Result<Self> {
|
||||
const VERT: &str = r#"#version 330 core
|
||||
layout(location = 0) in vec3 position;
|
||||
uniform mat4 vp;
|
||||
void main() {
|
||||
gl_Position = vp * vec4(position, 1.0);
|
||||
}
|
||||
"#;
|
||||
const FRAG: &str = r#"#version 330 core
|
||||
out vec4 color;
|
||||
void main() {
|
||||
color = vec4(1.0,1.0,0.0,1.0);
|
||||
}
|
||||
"#;
|
||||
let program = Program::from_source(display, VERT, FRAG, None)?;
|
||||
Ok(Self { program, display: display.clone() })
|
||||
}
|
||||
|
||||
pub fn draw<S: Surface>(&self, colliders: &ColliderSet, vp: Mat4, target: &mut S) {
|
||||
let mut vertices: Vec<DebugVertex> = Vec::new();
|
||||
for (_, c) in colliders.iter() {
|
||||
let aabb: Aabb = c.compute_aabb();
|
||||
let min = aabb.mins;
|
||||
let max = aabb.maxs;
|
||||
// 8 corners
|
||||
let p0 = [min.x, min.y, min.z];
|
||||
let p1 = [max.x, min.y, min.z];
|
||||
let p2 = [max.x, max.y, min.z];
|
||||
let p3 = [min.x, max.y, min.z];
|
||||
let p4 = [min.x, min.y, max.z];
|
||||
let p5 = [max.x, min.y, max.z];
|
||||
let p6 = [max.x, max.y, max.z];
|
||||
let p7 = [min.x, max.y, max.z];
|
||||
// 12 edges (pairs)
|
||||
let edges = [
|
||||
(p0, p1), (p1, p2), (p2, p3), (p3, p0),
|
||||
(p4, p5), (p5, p6), (p6, p7), (p7, p4),
|
||||
(p0, p4), (p1, p5), (p2, p6), (p3, p7),
|
||||
];
|
||||
for (a, b) in edges.iter() {
|
||||
vertices.push(DebugVertex { position: *a });
|
||||
vertices.push(DebugVertex { position: *b });
|
||||
}
|
||||
}
|
||||
if vertices.is_empty() { return; }
|
||||
let vb = VertexBuffer::new(&self.display, &vertices).unwrap();
|
||||
let no_indices = NoIndices(PrimitiveType::LinesList);
|
||||
let uniforms = uniform! { vp: vp.to_cols_array_2d() };
|
||||
let params = DrawParameters {
|
||||
depth: glium::Depth {
|
||||
test: glium::draw_parameters::DepthTest::IfLessOrEqual,
|
||||
write: false,
|
||||
.. Default::default()
|
||||
},
|
||||
polygon_mode: glium::draw_parameters::PolygonMode::Line,
|
||||
line_width: Some(1.0),
|
||||
.. Default::default()
|
||||
};
|
||||
target.draw(&vb, &no_indices, &self.program, &uniforms, ¶ms).ok();
|
||||
}
|
||||
}
|
||||
|
|
@ -29,87 +29,88 @@ where
|
|||
// ---------- 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();
|
||||
if let Some(mat_idx) = primitive.material().index() {
|
||||
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();
|
||||
// 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,
|
||||
// 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,
|
||||
};
|
||||
}
|
||||
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,
|
||||
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])?);
|
||||
}
|
||||
// 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])?);
|
||||
}
|
||||
// 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])?);
|
||||
}
|
||||
// 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) = 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])?);
|
||||
}
|
||||
// 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) = 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])?);
|
||||
}
|
||||
// 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
|
||||
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]);
|
||||
// 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ pub mod gltf_loader;
|
|||
pub mod render;
|
||||
pub mod ecs_renderer;
|
||||
pub mod window;
|
||||
pub mod debug;
|
||||
|
||||
pub use camera::Camera;
|
||||
pub use render::GliumRenderer;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
use crate::camera::Camera;
|
||||
use raidillon_ecs::{ModelHandle, Transform};
|
||||
|
||||
use crate::model::{Model, Mesh};
|
||||
use crate::debug::ColliderDebugRenderer;
|
||||
use rapier3d::prelude::ColliderSet;
|
||||
use glium::texture::{RawImage2d, SrgbTexture2d};
|
||||
use glium::{uniform, Program, Surface};
|
||||
use glium::uniforms::{MinifySamplerFilter, MagnifySamplerFilter, SamplerWrapFunction};
|
||||
use glam::{Vec3, Vec4};
|
||||
use glam::{Vec3, Vec4, Mat4};
|
||||
use hecs::World;
|
||||
use glium::glutin::surface::WindowSurface;
|
||||
use image::io::Reader as ImageReader;
|
||||
|
|
@ -22,6 +25,11 @@ pub struct GliumRenderer {
|
|||
skybox_program: Program,
|
||||
skybox_texture: SrgbTexture2d,
|
||||
skybox_mesh: Mesh,
|
||||
|
||||
debug_renderer: ColliderDebugRenderer,
|
||||
show_colliders: bool,
|
||||
|
||||
collider_set: Option<*const ColliderSet>,
|
||||
}
|
||||
|
||||
impl GliumRenderer {
|
||||
|
|
@ -58,6 +66,8 @@ impl GliumRenderer {
|
|||
let cube_model = crate::gltf_loader::load_gltf("resources/models/cube.gltf", &display)?;
|
||||
let skybox_mesh = cube_model.mesh;
|
||||
|
||||
let debug_renderer = crate::debug::ColliderDebugRenderer::new(&display)?;
|
||||
|
||||
Ok(Self {
|
||||
display,
|
||||
program,
|
||||
|
|
@ -67,9 +77,23 @@ impl GliumRenderer {
|
|||
skybox_program,
|
||||
skybox_texture,
|
||||
skybox_mesh,
|
||||
debug_renderer,
|
||||
show_colliders: false,
|
||||
collider_set: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Provide the collider set for the upcoming frame. Pass `None` when no
|
||||
/// collider debug rendering is desired or right after rendering to release
|
||||
/// the reference.
|
||||
pub fn set_colliders(&mut self, colliders: Option<&ColliderSet>) {
|
||||
self.collider_set = colliders.map(|c| c as *const ColliderSet);
|
||||
}
|
||||
|
||||
pub fn set_show_colliders(&mut self, v: bool) {
|
||||
self.show_colliders = v;
|
||||
}
|
||||
|
||||
fn draw_scene<S: Surface>(&self, world: &World, target: &mut S) {
|
||||
let cam = match world.query::<&Camera>().iter().next() {
|
||||
Some((_, cam)) => *cam,
|
||||
|
|
@ -147,6 +171,18 @@ impl GliumRenderer {
|
|||
&uniforms,
|
||||
&sky_params,
|
||||
).unwrap();
|
||||
|
||||
if self.show_colliders {
|
||||
if let Some(ptr) = self.collider_set {
|
||||
// SAFETY: `set_colliders` guarantees the pointer is valid for
|
||||
// the duration of this render call and we only read from it.
|
||||
unsafe {
|
||||
let colset: &ColliderSet = &*ptr;
|
||||
let vp_mat = cam.projection() * cam.view();
|
||||
self.debug_renderer.draw(colset, vp_mat, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_into<S: Surface>(&mut self, world: &World, target: &mut S) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue