engine: port renderer to ecs
parent
bf378bd288
commit
120712d0a8
|
@ -20,6 +20,7 @@ rust_binary(
|
|||
"src/render/material.rs",
|
||||
"src/render/mesh.rs",
|
||||
"src/render/renderable.rs",
|
||||
"src/render/resource.rs",
|
||||
|
||||
"src/render/vulkan/mod.rs",
|
||||
"src/render/vulkan/data.rs",
|
||||
|
|
|
@ -25,165 +25,175 @@ mod render;
|
|||
mod util;
|
||||
mod physics;
|
||||
|
||||
use ecs::{component, world};
|
||||
use ecs::{Component, World, Processor};
|
||||
use render::vulkan::data;
|
||||
use render::light::Omni;
|
||||
use render::material::{Texture, PBRMaterialBuilder};
|
||||
use render::mesh::Mesh;
|
||||
use render::renderable::{Light, Object, Renderable, ResourceManager};
|
||||
use render::{Light, Material, Mesh, Transform, Renderable};
|
||||
use physics::color;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Dupa {
|
||||
zupa: u32,
|
||||
kupa: Vec<u32>,
|
||||
struct App {
|
||||
world: World,
|
||||
renderer: render::Renderer,
|
||||
}
|
||||
|
||||
impl component::Component for Dupa {}
|
||||
impl App {
|
||||
pub fn new() -> Self {
|
||||
let mut world = World::new();
|
||||
let renderer = render::Renderer::initialize(&mut world);
|
||||
App {
|
||||
world, renderer,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Position {
|
||||
x: u32,
|
||||
y: u32,
|
||||
z: u32,
|
||||
pub fn initialize_scene(&mut self) {
|
||||
let mesh = {
|
||||
let vertices = Arc::new(vec![
|
||||
data::Vertex::new([-0.5, -0.5, 0.5], [ 0.0, 0.0, 1.0], [1.0, 0.0]),
|
||||
data::Vertex::new([ 0.5, -0.5, 0.5], [ 0.0, 0.0, 1.0], [0.0, 0.0]),
|
||||
data::Vertex::new([ 0.5, 0.5, 0.5], [ 0.0, 0.0, 1.0], [0.0, 1.0]),
|
||||
data::Vertex::new([-0.5, 0.5, 0.5], [ 0.0, 0.0, 1.0], [1.0, 1.0]),
|
||||
|
||||
data::Vertex::new([ 0.5, -0.5, -0.5], [ 1.0, 0.0, 0.0], [0.0, 1.0]),
|
||||
data::Vertex::new([ 0.5, 0.5, -0.5], [ 1.0, 0.0, 0.0], [1.0, 1.0]),
|
||||
data::Vertex::new([ 0.5, 0.5, 0.5], [ 1.0, 0.0, 0.0], [1.0, 0.0]),
|
||||
data::Vertex::new([ 0.5, -0.5, 0.5], [ 1.0, 0.0, 0.0], [0.0, 0.0]),
|
||||
|
||||
data::Vertex::new([-0.5, -0.5, -0.5], [-1.0, 0.0, 0.0], [1.0, 1.0]),
|
||||
data::Vertex::new([-0.5, 0.5, -0.5], [-1.0, 0.0, 0.0], [0.0, 1.0]),
|
||||
data::Vertex::new([-0.5, 0.5, 0.5], [-1.0, 0.0, 0.0], [0.0, 0.0]),
|
||||
data::Vertex::new([-0.5, -0.5, 0.5], [-1.0, 0.0, 0.0], [1.0, 0.0]),
|
||||
|
||||
data::Vertex::new([-0.5, -0.5, -0.5], [ 0.0, -1.0, 0.0], [0.0, 1.0]),
|
||||
data::Vertex::new([ 0.5, -0.5, -0.5], [ 0.0, -1.0, 0.0], [1.0, 1.0]),
|
||||
data::Vertex::new([ 0.5, -0.5, 0.5], [ 0.0, -1.0, 0.0], [1.0, 0.0]),
|
||||
data::Vertex::new([-0.5, -0.5, 0.5], [ 0.0, -1.0, 0.0], [0.0, 0.0]),
|
||||
|
||||
data::Vertex::new([-0.5, 0.5, -0.5], [ 0.0, 1.0, 0.0], [1.0, 1.0]),
|
||||
data::Vertex::new([ 0.5, 0.5, -0.5], [ 0.0, 1.0, 0.0], [0.0, 1.0]),
|
||||
data::Vertex::new([ 0.5, 0.5, 0.5], [ 0.0, 1.0, 0.0], [0.0, 0.0]),
|
||||
data::Vertex::new([-0.5, 0.5, 0.5], [ 0.0, 1.0, 0.0], [1.0, 0.0]),
|
||||
|
||||
data::Vertex::new([-0.5, -0.5, -0.5], [ 0.0, 0.0, -1.0], [0.0, 0.0]),
|
||||
data::Vertex::new([ 0.5, -0.5, -0.5], [ 0.0, 0.0, -1.0], [1.0, 0.0]),
|
||||
data::Vertex::new([ 0.5, 0.5, -0.5], [ 0.0, 0.0, -1.0], [1.0, 1.0]),
|
||||
data::Vertex::new([-0.5, 0.5, -0.5], [ 0.0, 0.0, -1.0], [0.0, 1.0]),
|
||||
]);
|
||||
let indices = Arc::new(vec![
|
||||
0, 1, 2, 2, 3, 0,
|
||||
|
||||
4, 5, 6, 6, 7, 4,
|
||||
8, 10, 9, 10, 8, 11,
|
||||
|
||||
12, 13, 14, 14, 15, 12,
|
||||
16, 18, 17, 18, 16, 19,
|
||||
|
||||
20, 22, 21, 22, 20, 23,
|
||||
|
||||
]);
|
||||
self.renderer.add_resource(Mesh::new(vertices, indices))
|
||||
};
|
||||
|
||||
let material = self.renderer.add_resource(PBRMaterialBuilder {
|
||||
diffuse: Texture::from_image(String::from("assets/test-128px.png")),
|
||||
roughness: Texture::from_color(color::LinearF32::new(1.0)),
|
||||
}.build());
|
||||
|
||||
for x in -20..20 {
|
||||
for y in -20..20 {
|
||||
for z in -20..20 {
|
||||
self.world.new_entity()
|
||||
.with(Transform::at((x as f32)*4.0, (y as f32)*4.0, (z as f32)*4.0))
|
||||
.with(Renderable::Mesh(mesh, material))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let light = self.renderer.add_resource(Light::omni_test());
|
||||
|
||||
// The Sun (Sol) is 1AU from the Earth. We ignore the diameter of the Sun and the Earth, as
|
||||
// these are negligible at this scale.
|
||||
let sun_distance: f32 = 149_597_870_700.0;
|
||||
// Solar constant: solar radiant power per square meter of earth's area [w/m^2].
|
||||
let solar_constant: f32 = 1366.0;
|
||||
// Solar luminous emittance (assuming 93 luminous efficacy) [lm/m^2].
|
||||
let sun_luminous_emittance: f32 = solar_constant * 93.0;
|
||||
// Solar luminour power (integrating over a sphere of radius == sun_distance) [lm].
|
||||
let sun_lumen: f32 = sun_luminous_emittance * (4.0 * 3.14159 * sun_distance * sun_distance);
|
||||
|
||||
let sun_color = color::XYZ::new(sun_lumen/3.0, sun_lumen/3.0, sun_lumen/3.0);
|
||||
let sun = self.renderer.add_resource(Light::omni_with_color(sun_color));
|
||||
|
||||
// In our scene, the sun at a 30 degree zenith.
|
||||
let sun_angle: f32 = (3.14159 * 2.0) / (360.0 / 30.0);
|
||||
|
||||
self.world.new_entity()
|
||||
.with(Transform::at(-10.0, -10.0, -5.0))
|
||||
.with(Renderable::Light(light))
|
||||
.build();
|
||||
self.world.new_entity()
|
||||
.with(Transform::at(-10.0, -10.0, -5.0))
|
||||
.with(Renderable::Light(light))
|
||||
.build();
|
||||
self.world.new_entity()
|
||||
.with(Transform::at(0.0, sun_angle.sin() * sun_distance, sun_angle.cos() * sun_distance))
|
||||
.with(Renderable::Light(sun))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
fn run(self) {
|
||||
let mut p = Processor::new(&self.world);
|
||||
p.add_system(self.renderer);
|
||||
|
||||
let start = time::Instant::now();
|
||||
loop {
|
||||
let instant_ns = time::Instant::now().duration_since(start).as_nanos() as u64;
|
||||
let instant = ((instant_ns/1000) as f32) / 1_000_000.0;
|
||||
|
||||
let position = (instant / 10.0) * 3.14 * 2.0;
|
||||
|
||||
let camera = cgm::Point3::new(
|
||||
7.0 + (position / 4.0).sin(),
|
||||
12.0 + (position / 4.0).cos(),
|
||||
3.0
|
||||
);
|
||||
|
||||
let view = cgm::Matrix4::look_at(
|
||||
camera.clone(),
|
||||
cgm::Point3::new(0.0, 0.0, 0.0),
|
||||
cgm::Vector3::new(0.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
//rm.light_mut(&light1).as_mut().unwrap().position = cgm::Vector3::new(
|
||||
// -0.0 + (position*3.0).sin() * 4.0,
|
||||
// -0.0 + (position*4.0).cos() * 4.0,
|
||||
// -0.0 + (position*2.0).sin() * 3.0,
|
||||
//);
|
||||
//rm.light_mut(&light2).as_mut().unwrap().position = cgm::Vector3::new(
|
||||
// -0.0 + (position*3.0).cos() * 4.0,
|
||||
// -0.0 + (position*4.0).sin() * 4.0,
|
||||
// -0.0 + (position*2.0).cos() * 3.0,
|
||||
//);
|
||||
|
||||
self.world.set_global(render::SceneInfo {
|
||||
camera,
|
||||
view,
|
||||
});
|
||||
p.run();
|
||||
let status = self.world.global::<render::Status>().get();
|
||||
if status.closed {
|
||||
log::info!("Renderer closed, exiting.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl component::Component for Position {}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
log::info!("Starting...");
|
||||
|
||||
let mut rm = ResourceManager::new();
|
||||
|
||||
let mesh = {
|
||||
let vertices = Arc::new(vec![
|
||||
data::Vertex::new([-0.5, -0.5, 0.5], [ 0.0, 0.0, 1.0], [1.0, 0.0]),
|
||||
data::Vertex::new([ 0.5, -0.5, 0.5], [ 0.0, 0.0, 1.0], [0.0, 0.0]),
|
||||
data::Vertex::new([ 0.5, 0.5, 0.5], [ 0.0, 0.0, 1.0], [0.0, 1.0]),
|
||||
data::Vertex::new([-0.5, 0.5, 0.5], [ 0.0, 0.0, 1.0], [1.0, 1.0]),
|
||||
|
||||
data::Vertex::new([ 0.5, -0.5, -0.5], [ 1.0, 0.0, 0.0], [0.0, 1.0]),
|
||||
data::Vertex::new([ 0.5, 0.5, -0.5], [ 1.0, 0.0, 0.0], [1.0, 1.0]),
|
||||
data::Vertex::new([ 0.5, 0.5, 0.5], [ 1.0, 0.0, 0.0], [1.0, 0.0]),
|
||||
data::Vertex::new([ 0.5, -0.5, 0.5], [ 1.0, 0.0, 0.0], [0.0, 0.0]),
|
||||
|
||||
data::Vertex::new([-0.5, -0.5, -0.5], [-1.0, 0.0, 0.0], [1.0, 1.0]),
|
||||
data::Vertex::new([-0.5, 0.5, -0.5], [-1.0, 0.0, 0.0], [0.0, 1.0]),
|
||||
data::Vertex::new([-0.5, 0.5, 0.5], [-1.0, 0.0, 0.0], [0.0, 0.0]),
|
||||
data::Vertex::new([-0.5, -0.5, 0.5], [-1.0, 0.0, 0.0], [1.0, 0.0]),
|
||||
|
||||
data::Vertex::new([-0.5, -0.5, -0.5], [ 0.0, -1.0, 0.0], [0.0, 1.0]),
|
||||
data::Vertex::new([ 0.5, -0.5, -0.5], [ 0.0, -1.0, 0.0], [1.0, 1.0]),
|
||||
data::Vertex::new([ 0.5, -0.5, 0.5], [ 0.0, -1.0, 0.0], [1.0, 0.0]),
|
||||
data::Vertex::new([-0.5, -0.5, 0.5], [ 0.0, -1.0, 0.0], [0.0, 0.0]),
|
||||
|
||||
data::Vertex::new([-0.5, 0.5, -0.5], [ 0.0, 1.0, 0.0], [1.0, 1.0]),
|
||||
data::Vertex::new([ 0.5, 0.5, -0.5], [ 0.0, 1.0, 0.0], [0.0, 1.0]),
|
||||
data::Vertex::new([ 0.5, 0.5, 0.5], [ 0.0, 1.0, 0.0], [0.0, 0.0]),
|
||||
data::Vertex::new([-0.5, 0.5, 0.5], [ 0.0, 1.0, 0.0], [1.0, 0.0]),
|
||||
|
||||
data::Vertex::new([-0.5, -0.5, -0.5], [ 0.0, 0.0, -1.0], [0.0, 0.0]),
|
||||
data::Vertex::new([ 0.5, -0.5, -0.5], [ 0.0, 0.0, -1.0], [1.0, 0.0]),
|
||||
data::Vertex::new([ 0.5, 0.5, -0.5], [ 0.0, 0.0, -1.0], [1.0, 1.0]),
|
||||
data::Vertex::new([-0.5, 0.5, -0.5], [ 0.0, 0.0, -1.0], [0.0, 1.0]),
|
||||
]);
|
||||
let indices = Arc::new(vec![
|
||||
0, 1, 2, 2, 3, 0,
|
||||
|
||||
4, 5, 6, 6, 7, 4,
|
||||
8, 10, 9, 10, 8, 11,
|
||||
|
||||
12, 13, 14, 14, 15, 12,
|
||||
16, 18, 17, 18, 16, 19,
|
||||
|
||||
20, 22, 21, 22, 20, 23,
|
||||
|
||||
]);
|
||||
rm.add_mesh(Mesh::new(vertices, indices))
|
||||
};
|
||||
|
||||
let material = rm.add_material(PBRMaterialBuilder {
|
||||
diffuse: Texture::from_image(String::from("assets/test-128px.png")),
|
||||
roughness: Texture::from_color(color::LinearF32::new(1.0)),
|
||||
}.build());
|
||||
|
||||
|
||||
let mut cubes: Vec<Box<Object>> = vec![];
|
||||
for x in -20..20 {
|
||||
for y in -20..20 {
|
||||
for z in -20..20 {
|
||||
let transform = cgm::Matrix4::from_translation(cgm::Vector3::new((x as f32)*4.0, (y as f32)*4.0, (z as f32)*4.0));
|
||||
let cube = render::renderable::Object {
|
||||
mesh, material, transform,
|
||||
};
|
||||
cubes.push(Box::new(cube));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let light1 = rm.add_light(Omni::test(cgm::Vector3::new(-10.0, -10.0, -5.0)));
|
||||
let light2 = rm.add_light(Omni::test(cgm::Vector3::new(-10.0, -10.0, -5.0)));
|
||||
|
||||
// The Sun (Sol) is 1AU from the Earth. We ignore the diameter of the Sun and the Earth, as
|
||||
// these are negligible at this scale.
|
||||
let sun_distance: f32 = 149_597_870_700.0;
|
||||
// Solar constant: solar radiant power per square meter of earth's area [w/m^2].
|
||||
let solar_constant: f32 = 1366.0;
|
||||
// Solar luminous emittance (assuming 93 luminous efficacy) [lm/m^2].
|
||||
let sun_luminous_emittance: f32 = solar_constant * 93.0;
|
||||
// Solar luminour power (integrating over a sphere of radius == sun_distance) [lm].
|
||||
let sun_lumen: f32 = sun_luminous_emittance * (4.0 * 3.14159 * sun_distance * sun_distance);
|
||||
|
||||
// In our scene, the sun at a 30 degree zenith.
|
||||
let sun_angle: f32 = (3.14159 * 2.0) / (360.0 / 30.0);
|
||||
let sun = rm.add_light(
|
||||
Omni::with_color(
|
||||
cgm::Vector3::new(0.0, sun_angle.sin() * sun_distance, sun_angle.cos() * sun_distance),
|
||||
color::XYZ::new(sun_lumen/3.0, sun_lumen/3.0, sun_lumen/3.0)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
let mut renderables: Vec<Box<dyn Renderable>> = cubes.into_iter().map(|e| e as Box<dyn Renderable>).collect();
|
||||
renderables.push(Box::new(Light{ light: light1 }));
|
||||
renderables.push(Box::new(Light{ light: light2 }));
|
||||
renderables.push(Box::new(Light{ light: sun }));
|
||||
|
||||
let start = time::Instant::now();
|
||||
let mut renderer = render::Renderer::initialize();
|
||||
loop {
|
||||
let instant_ns = time::Instant::now().duration_since(start).as_nanos() as u64;
|
||||
let instant = ((instant_ns/1000) as f32) / 1_000_000.0;
|
||||
|
||||
let position = (instant / 10.0) * 3.14 * 2.0;
|
||||
|
||||
let camera = cgm::Point3::new(
|
||||
7.0 + (position / 4.0).sin(),
|
||||
12.0 + (position / 4.0).cos(),
|
||||
3.0
|
||||
);
|
||||
|
||||
rm.light_mut(&light1).as_mut().unwrap().position = cgm::Vector3::new(
|
||||
-0.0 + (position*3.0).sin() * 4.0,
|
||||
-0.0 + (position*4.0).cos() * 4.0,
|
||||
-0.0 + (position*2.0).sin() * 3.0,
|
||||
);
|
||||
rm.light_mut(&light2).as_mut().unwrap().position = cgm::Vector3::new(
|
||||
-0.0 + (position*3.0).cos() * 4.0,
|
||||
-0.0 + (position*4.0).sin() * 4.0,
|
||||
-0.0 + (position*2.0).cos() * 3.0,
|
||||
);
|
||||
|
||||
let view = cgm::Matrix4::look_at(
|
||||
camera.clone(),
|
||||
cgm::Point3::new(0.0, 0.0, 0.0),
|
||||
cgm::Vector3::new(0.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
renderer.draw_frame(&camera, &view, &rm, &renderables);
|
||||
if renderer.poll_close() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let mut app = App::new();
|
||||
app.initialize_scene();
|
||||
app.run();
|
||||
}
|
||||
|
|
|
@ -24,13 +24,11 @@ use crate::render::vulkan::data;
|
|||
/// An Omni point light, with position in 3d space, and 'color' defined in lumens per CIE XYZ
|
||||
/// color channel.
|
||||
pub struct Omni {
|
||||
pub position: cgm::Vector3<f32>,
|
||||
/// Luminour power/flux defined as lumens per XYZ color channel.
|
||||
pub color: color::XYZ,
|
||||
|
||||
pub id: u64,
|
||||
}
|
||||
|
||||
|
||||
impl Omni {
|
||||
/// Make a test light. This has... a color. It's kinda yellow. And something close to 650
|
||||
/// lumens of luminous power.
|
||||
|
@ -40,24 +38,34 @@ impl Omni {
|
|||
// [Kry85]
|
||||
// M. Krystek. 1985. "An algorithm to calculate correlated color temperature"
|
||||
// Color Research & Application, 10 (1), 38–40.
|
||||
pub fn test(position: cgm::Vector3<f32>) -> Self {
|
||||
Self::with_color(position, color::XYZ::new(234.7*10.0, 214.1*10.0, 207.9*10.0))
|
||||
pub fn test() -> Self {
|
||||
Self::with_color(color::XYZ::new(234.7*10.0, 214.1*10.0, 207.9*10.0))
|
||||
}
|
||||
|
||||
pub fn with_color(position: cgm::Vector3<f32>, color: color::XYZ) -> Self{
|
||||
pub fn with_color(color: color::XYZ) -> Self{
|
||||
Self {
|
||||
position,
|
||||
color,
|
||||
// TODO: use a better method
|
||||
id: time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos() as u64,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vulkan_uniform(&self) -> data::OmniLight {
|
||||
pub fn vulkan_uniform(&self, pos: &cgm::Vector4<f32>) -> data::OmniLight {
|
||||
// TODO: cache this?
|
||||
data::OmniLight {
|
||||
pos: [self.position.x, self.position.y, self.position.z, 1.0],
|
||||
pos: [pos.x, pos.y, pos.z, pos.w],
|
||||
color: [self.color.x, self.color.y, self.color.z, 0.0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Light {
|
||||
Omni(Omni),
|
||||
}
|
||||
|
||||
impl Light {
|
||||
pub fn omni_test() -> Light {
|
||||
Light::Omni(Omni::test())
|
||||
}
|
||||
pub fn omni_with_color(color: color::XYZ) -> Light {
|
||||
Light::Omni(Omni::with_color(color))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,8 +27,6 @@ use crate::render::vulkan::data;
|
|||
pub struct Mesh {
|
||||
vertices: Arc<Vec<data::Vertex>>,
|
||||
indices: Arc<Vec<u16>>,
|
||||
|
||||
pub id: u64,
|
||||
// vulkan buffers cache
|
||||
vulkan: Mutex<Option<data::VertexData>>,
|
||||
}
|
||||
|
@ -40,8 +38,6 @@ impl Mesh {
|
|||
) -> Self {
|
||||
Self {
|
||||
vertices, indices,
|
||||
// TODO: use a better method
|
||||
id: time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos() as u64,
|
||||
vulkan: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
@ -80,4 +76,3 @@ impl Mesh {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
// Abrasion. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use cgmath as cgm;
|
||||
|
||||
|
@ -31,22 +32,91 @@ use vulkano_win::VkSurfaceBuild;
|
|||
use vulkano::instance as vi;
|
||||
use vulkano::swapchain as vs;
|
||||
|
||||
use ecs::Join;
|
||||
|
||||
pub mod light;
|
||||
pub mod material;
|
||||
pub mod mesh;
|
||||
pub mod renderable;
|
||||
pub mod resource;
|
||||
pub mod vulkan;
|
||||
|
||||
pub use light::Light;
|
||||
pub use material::Material;
|
||||
pub use mesh::Mesh;
|
||||
pub use renderable::{Transform, Renderable};
|
||||
pub use resource::{Resource, ResourceID};
|
||||
|
||||
const WIDTH: u32 = 800;
|
||||
const HEIGHT: u32 = 600;
|
||||
|
||||
pub struct Renderer {
|
||||
instance: vulkan::Instance<Window>,
|
||||
events_loop: EventLoop<()>,
|
||||
rm: resource::Manager,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Status {
|
||||
pub closed: bool,
|
||||
}
|
||||
impl ecs::Global for Status {}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SceneInfo {
|
||||
pub camera: cgm::Point3<f32>,
|
||||
pub view: cgm::Matrix4<f32>,
|
||||
}
|
||||
impl ecs::Global for SceneInfo {}
|
||||
|
||||
impl<'a> ecs::System<'a> for Renderer {
|
||||
type SystemData = (
|
||||
ecs::ReadComponent<'a, Transform>,
|
||||
ecs::ReadComponent<'a, Renderable>,
|
||||
ecs::ReadWriteGlobal<'a, Status>,
|
||||
ecs::ReadGlobal<'a, SceneInfo>,
|
||||
);
|
||||
|
||||
fn run(&mut self,
|
||||
( transforms
|
||||
, renderables
|
||||
, status
|
||||
, scene): Self::SystemData,
|
||||
) {
|
||||
let transformedRenderables = (transforms, renderables);
|
||||
|
||||
let mut rd = vulkan::RenderData {
|
||||
meshes: BTreeMap::new(),
|
||||
lights: Vec::new(),
|
||||
};
|
||||
for (transform, renderable) in transformedRenderables.join_all() {
|
||||
match renderable {
|
||||
Renderable::Light(lrid) => {
|
||||
rd.lights.push((*lrid, transform.xyzw()));
|
||||
},
|
||||
Renderable::Mesh(mesh_id, material_id) => {
|
||||
rd.meshes.entry((*mesh_id, *material_id)).or_insert(Vec::new()).push(transform.m4());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let camera = &scene.get().camera;
|
||||
let view = &scene.get().view;
|
||||
self.instance.flip(camera, view, &rd, &self.rm);
|
||||
|
||||
if self.poll_close() {
|
||||
status.get().closed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn initialize() -> Self {
|
||||
pub fn initialize(world: &mut ecs::World) -> Self {
|
||||
world.set_global(Status{
|
||||
closed: false,
|
||||
});
|
||||
|
||||
let mut instance = vulkan::Instance::new("abrasion".to_string());
|
||||
let (events_loop, surface) = Self::init_window(instance.get_vulkan());
|
||||
instance.use_surface(&surface);
|
||||
|
@ -54,6 +124,7 @@ impl Renderer {
|
|||
Self {
|
||||
instance,
|
||||
events_loop,
|
||||
rm: resource::Manager::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,17 +138,7 @@ impl Renderer {
|
|||
(events_loop, surface)
|
||||
}
|
||||
|
||||
pub fn draw_frame(
|
||||
&mut self,
|
||||
camera: &cgm::Point3<f32>,
|
||||
view: &cgm::Matrix4<f32>,
|
||||
rm: &renderable::ResourceManager,
|
||||
renderables: &Vec<Box<dyn renderable::Renderable>>
|
||||
) {
|
||||
self.instance.flip(camera, view, rm, renderables);
|
||||
}
|
||||
|
||||
pub fn poll_close(&mut self) -> bool {
|
||||
fn poll_close(&mut self) -> bool {
|
||||
let mut close = false;
|
||||
// TODO(q3k): migrate to EventLoop::run
|
||||
self.events_loop.run_return(|ev, _, control_flow| {
|
||||
|
@ -88,4 +149,8 @@ impl Renderer {
|
|||
});
|
||||
return close;
|
||||
}
|
||||
|
||||
pub fn add_resource<T: Resource>(&mut self, r: T) -> ResourceID<T> {
|
||||
self.rm.add(r)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,134 +19,33 @@ use std::hash;
|
|||
|
||||
use cgmath as cgm;
|
||||
|
||||
use crate::render::light::Omni;
|
||||
use crate::render::material::Material;
|
||||
use crate::render::mesh::Mesh;
|
||||
use ecs::Component;
|
||||
use crate::render::{Light, Mesh, Material};
|
||||
use crate::render::resource::{ResourceID};
|
||||
|
||||
pub struct ResourceManager {
|
||||
meshes: HashMap<u64, Mesh>,
|
||||
materials: HashMap<u64, Material>,
|
||||
lights: HashMap<u64, Omni>,
|
||||
}
|
||||
pub struct Transform(cgm::Matrix4<f32>);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ResourceID {
|
||||
Material(u64),
|
||||
Mesh(u64),
|
||||
Light(u64),
|
||||
}
|
||||
impl Component for Transform {}
|
||||
|
||||
impl ResourceID {
|
||||
pub fn id(&self) -> u64 {
|
||||
match self {
|
||||
ResourceID::Material(i) => *i,
|
||||
ResourceID::Mesh(i) => *i,
|
||||
ResourceID::Light(i) => *i,
|
||||
}
|
||||
impl Transform {
|
||||
pub fn at(x: f32, y: f32, z: f32) -> Self {
|
||||
Transform(cgm::Matrix4::from_translation(cgm::Vector3::new(x, y, z)))
|
||||
}
|
||||
pub fn xyzw(&self) -> cgm::Vector4<f32> {
|
||||
self.0 * cgm::Vector4::new(0.0, 0.0, 0.0, 1.0)
|
||||
}
|
||||
pub fn xyz(&self) -> cgm::Vector3<f32> {
|
||||
let res4 = self.xyzw();
|
||||
cgm::Vector3::new(res4.x/res4.w, res4.y/res4.w, res4.z/res4.w)
|
||||
}
|
||||
pub fn m4(&self) -> &cgm::Matrix4<f32> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl hash::Hash for ResourceID {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
ResourceID::Material(i) => i.hash(state),
|
||||
ResourceID::Mesh(i) => i.hash(state),
|
||||
ResourceID::Light(i) => i.hash(state),
|
||||
}
|
||||
}
|
||||
pub enum Renderable {
|
||||
Light(ResourceID<Light>),
|
||||
Mesh(ResourceID<Mesh>, ResourceID<Material>),
|
||||
}
|
||||
|
||||
impl PartialEq for ResourceID {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id() == other.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ResourceID {}
|
||||
|
||||
impl<'a> ResourceManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
meshes: HashMap::new(),
|
||||
materials: HashMap::new(),
|
||||
lights: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_material(&mut self, t: Material) -> ResourceID {
|
||||
let id = t.id;
|
||||
self.materials.insert(id, t);
|
||||
ResourceID::Material(id)
|
||||
}
|
||||
|
||||
pub fn add_mesh(&mut self, t: Mesh) -> ResourceID {
|
||||
let id = t.id;
|
||||
self.meshes.insert(id, t);
|
||||
ResourceID::Mesh(id)
|
||||
}
|
||||
|
||||
pub fn add_light(&mut self, t: Omni) -> ResourceID {
|
||||
let id = t.id;
|
||||
self.lights.insert(id, t);
|
||||
ResourceID::Light(id)
|
||||
}
|
||||
|
||||
pub fn material(&'a self, id: &ResourceID) -> Option<&'a Material> {
|
||||
if let ResourceID::Material(i) = id {
|
||||
return Some(self.materials.get(&i).unwrap());
|
||||
}
|
||||
return None
|
||||
}
|
||||
|
||||
pub fn mesh(&'a self, id: &ResourceID) -> Option<&'a Mesh> {
|
||||
if let ResourceID::Mesh(i) = id {
|
||||
return Some(self.meshes.get(&i).unwrap());
|
||||
}
|
||||
return None
|
||||
}
|
||||
|
||||
pub fn light(&'a self, id: &ResourceID) -> Option<&'a Omni> {
|
||||
if let ResourceID::Light(i) = id {
|
||||
return Some(self.lights.get(&i).unwrap());
|
||||
}
|
||||
return None
|
||||
}
|
||||
|
||||
pub fn light_mut(&'a mut self, id: &ResourceID) -> Option<&'a mut Omni> {
|
||||
if let ResourceID::Light(i) = id {
|
||||
return Some(self.lights.get_mut(&i).unwrap());
|
||||
}
|
||||
return None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Renderable {
|
||||
fn render_data(&self) -> Option<(ResourceID, ResourceID, &cgm::Matrix4<f32>)> {
|
||||
None
|
||||
}
|
||||
fn light_data(&self) -> Option<ResourceID> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Object {
|
||||
pub mesh: ResourceID,
|
||||
pub material: ResourceID,
|
||||
pub transform: cgm::Matrix4<f32>,
|
||||
}
|
||||
|
||||
impl Renderable for Object {
|
||||
fn render_data(&self) -> Option<(ResourceID, ResourceID, &cgm::Matrix4<f32>)> {
|
||||
Some((self.mesh, self.material, &self.transform))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Light {
|
||||
pub light: ResourceID,
|
||||
}
|
||||
|
||||
impl Renderable for Light {
|
||||
fn light_data(&self) -> Option<ResourceID> {
|
||||
Some(self.light)
|
||||
}
|
||||
}
|
||||
impl Component for Renderable {}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::render::{Mesh, Material, Light};
|
||||
|
||||
type Map<T> = BTreeMap<ResourceID<T>, T>;
|
||||
|
||||
pub struct Manager {
|
||||
meshes: Map<Mesh>,
|
||||
materials: Map<Material>,
|
||||
lights: Map<Light>,
|
||||
counter: u64,
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
pub fn new() -> Self {
|
||||
Manager {
|
||||
meshes: BTreeMap::new(),
|
||||
materials: BTreeMap::new(),
|
||||
lights: BTreeMap::new(),
|
||||
|
||||
counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn map<T: Resource>(&self) -> &Map<T> {
|
||||
T::map(&self)
|
||||
}
|
||||
|
||||
pub fn add<T: Resource>(&mut self, r: T) -> ResourceID<T> {
|
||||
let id = ResourceID {
|
||||
numerical: self.counter,
|
||||
phantom: std::marker::PhantomData,
|
||||
};
|
||||
self.counter += 1;
|
||||
T::map_mut(self).insert(id, r);
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Resource: Sized {
|
||||
fn map(rm: &Manager) -> &Map<Self>;
|
||||
fn map_mut(rm: &mut Manager) -> &mut Map<Self>;
|
||||
}
|
||||
|
||||
impl Resource for Light {
|
||||
fn map(rm: &Manager) -> &Map<Self> { &rm.lights }
|
||||
fn map_mut(rm: &mut Manager) -> &mut Map<Self> { &mut rm.lights }
|
||||
}
|
||||
impl Resource for Mesh {
|
||||
fn map(rm: &Manager) -> &Map<Self> { &rm.meshes }
|
||||
fn map_mut(rm: &mut Manager) -> &mut Map<Self> { &mut rm.meshes }
|
||||
}
|
||||
impl Resource for Material {
|
||||
fn map(rm: &Manager) -> &Map<Self> { &rm.materials }
|
||||
fn map_mut(rm: &mut Manager) -> &mut Map<Self> { &mut rm.materials }
|
||||
}
|
||||
|
||||
pub struct ResourceID<T: Resource> {
|
||||
numerical: u64,
|
||||
phantom: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl <T: Resource> Clone for ResourceID<T> {
|
||||
fn clone(&self) -> ResourceID<T> {
|
||||
ResourceID {
|
||||
numerical: self.numerical.clone(),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Resource> Copy for ResourceID<T> {}
|
||||
|
||||
impl <T: Resource> ResourceID<T> {
|
||||
pub fn get(self, rm: &Manager) -> &T {
|
||||
rm.map::<T>().get(&self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Resource> Ord for ResourceID<T> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.numerical.cmp(&other.numerical)
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Resource> PartialOrd for ResourceID<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Resource> PartialEq for ResourceID<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.numerical == other.numerical
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Resource> Eq for ResourceID<T> {}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
// You should have received a copy of the GNU General Public License along with
|
||||
// Abrasion. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
use std::time;
|
||||
use log;
|
||||
|
@ -36,10 +37,10 @@ mod shaders;
|
|||
mod swapchain_binding;
|
||||
mod worker;
|
||||
|
||||
use crate::render::renderable;
|
||||
use crate::util::counter::Counter;
|
||||
use crate::util::profiler::Profiler;
|
||||
use crate::util::resourcemap::ResourceMap;
|
||||
use crate::render::{Light, Material, Mesh, ResourceID};
|
||||
use crate::render::resource;
|
||||
|
||||
const VERSION: vi::Version = vi::Version { major: 1, minor: 0, patch: 0};
|
||||
|
||||
|
@ -49,6 +50,11 @@ fn required_instance_extensions() -> vi::InstanceExtensions {
|
|||
exts
|
||||
}
|
||||
|
||||
pub struct RenderData<'a> {
|
||||
pub meshes: BTreeMap<(ResourceID<Mesh>, ResourceID<Material>), Vec<&'a cgm::Matrix4<f32>>>,
|
||||
pub lights: Vec<(ResourceID<Light>, cgm::Vector4<f32>)>,
|
||||
}
|
||||
|
||||
pub struct Instance<WT> {
|
||||
debug_callback: vi::debug::DebugCallback,
|
||||
vulkan: Arc<vi::Instance>,
|
||||
|
@ -167,8 +173,8 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
|||
profiler: &mut Profiler,
|
||||
camera: &cgm::Point3<f32>,
|
||||
view: &cgm::Matrix4<f32>,
|
||||
rm: &renderable::ResourceManager,
|
||||
renderables: &Vec<Box<dyn renderable::Renderable>>,
|
||||
data: &RenderData,
|
||||
rm: &resource::Manager,
|
||||
) -> Vec<Box<vc::AutoCommandBuffer>> {
|
||||
|
||||
let dimensions = self.dimensions();
|
||||
|
@ -182,23 +188,23 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
|||
profiler.end("mgc.prep");
|
||||
|
||||
|
||||
// Sort renderables by mesh and materialid, and find lights.
|
||||
let mut meshes: ResourceMap<(renderable::ResourceID, renderable::ResourceID), &cgm::Matrix4<f32>> = ResourceMap::new();
|
||||
//// Sort renderables by mesh and materialid, and find lights.
|
||||
//let mut meshes: ResourceMap<(renderable::ResourceID, renderable::ResourceID), &cgm::Matrix4<f32>> = ResourceMap::new();
|
||||
let mut omni_lights = [data::OmniLight{ pos: [0.0, 0.0, 0.0, 0.0], color: [0.0, 0.0, 0.0, 0.0]}; 4];
|
||||
let mut omni_light_count = 0;
|
||||
|
||||
for r in renderables {
|
||||
if let Some((mesh_id, material_id, transform)) = r.render_data() {
|
||||
meshes.add((mesh_id, material_id), transform);
|
||||
}
|
||||
if let Some(light_id) = r.light_data() {
|
||||
if omni_light_count < 4 {
|
||||
let light = rm.light(&light_id).unwrap();
|
||||
omni_lights[omni_light_count] = light.vulkan_uniform();
|
||||
omni_light_count += 1;
|
||||
for (light_id, transform) in &data.lights {
|
||||
if omni_light_count < 4 {
|
||||
let light = light_id.get(rm);
|
||||
match light {
|
||||
Light::Omni(omni) => {
|
||||
omni_lights[omni_light_count] = omni.vulkan_uniform(&transform);
|
||||
omni_light_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
profiler.end("mgc.sort");
|
||||
|
||||
|
||||
|
@ -219,9 +225,9 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
|||
|
||||
let ubo_buffer = Arc::new(self.uniform_pool.as_ref().unwrap().next(ubo).unwrap());
|
||||
|
||||
for ((mesh_id, material_id), transforms) in meshes.resources {
|
||||
let mesh = rm.mesh(&mesh_id).unwrap();
|
||||
let material = rm.material(&material_id).unwrap();
|
||||
for ((mesh_id, material_id), transforms) in &data.meshes {
|
||||
let mesh = mesh_id.get(rm);
|
||||
let material = material_id.get(rm);
|
||||
|
||||
let mut builder = vc::AutoCommandBufferBuilder::secondary_graphics_one_time_submit(
|
||||
device.clone(), queue.family(), vf::Subpass::from(rp.clone(), 0).unwrap()).unwrap();
|
||||
|
@ -283,13 +289,13 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
|||
&mut self,
|
||||
camera: &cgm::Point3<f32>,
|
||||
view: &cgm::Matrix4<f32>,
|
||||
rm: &renderable::ResourceManager,
|
||||
renderables: &Vec<Box<dyn renderable::Renderable>>,
|
||||
data: &RenderData,
|
||||
rm: &resource::Manager,
|
||||
) {
|
||||
let mut profiler = Profiler::new();
|
||||
|
||||
// Build batch command buffer as early as possible.
|
||||
let mut batches = self.make_graphics_commands(&mut profiler, camera, view, rm, renderables);
|
||||
let mut batches = self.make_graphics_commands(&mut profiler, camera, view, data, rm);
|
||||
profiler.end("mgc");
|
||||
|
||||
match &self.previous_frame_end {
|
||||
|
@ -300,7 +306,7 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
|||
if !self.armed {
|
||||
self.arm();
|
||||
// Rearming means the batch is invalid - rebuild it.
|
||||
batches = self.make_graphics_commands(&mut profiler, camera, view, rm, renderables);
|
||||
batches = self.make_graphics_commands(&mut profiler, camera, view, data, rm);
|
||||
}
|
||||
profiler.end("arm");
|
||||
|
||||
|
|
Loading…
Reference in New Issue