engine: port renderer to ecs

master
q3k 2021-01-13 23:10:58 +00:00
parent bf378bd288
commit 120712d0a8
8 changed files with 406 additions and 322 deletions

View File

@ -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",

View File

@ -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();
}

View File

@ -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), 3840.
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))
}
}

View File

@ -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 {
}
}
}

View File

@ -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)
}
}

View File

@ -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 {}

View File

@ -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> {}

View File

@ -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");