engine/render: start implement multi-texture materials
parent
c5e6c75f23
commit
0aefcdd347
|
@ -11,6 +11,8 @@ rust_binary(
|
|||
],
|
||||
srcs = [
|
||||
"src/main.rs",
|
||||
"src/physics/mod.rs",
|
||||
"src/physics/color.rs",
|
||||
"src/render/mod.rs",
|
||||
"src/render/renderable.rs",
|
||||
"src/render/vulkan/data.rs",
|
||||
|
|
|
@ -3,13 +3,9 @@
|
|||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(binding = 0) uniform sampler2D texSampler;
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
layout(location = 1) in vec2 fragTexCoord;
|
||||
|
||||
layout(location = 0) in vec2 fragTexCoord;
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
//outColor = vec4(fragColor, 1.0);
|
||||
outColor = texture(texSampler, fragTexCoord);
|
||||
}
|
||||
|
|
|
@ -6,12 +6,10 @@ layout(push_constant) uniform UniformBufferObject {
|
|||
} ubo;
|
||||
|
||||
layout(location = 0) in vec3 pos;
|
||||
layout(location = 1) in vec3 color;
|
||||
layout(location = 2) in mat4 model;
|
||||
layout(location = 6) in vec2 tex;
|
||||
layout(location = 1) in mat4 model;
|
||||
layout(location = 5) in vec2 tex;
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
layout(location = 1) out vec2 fragTexCoord;
|
||||
layout(location = 0) out vec2 fragTexCoord;
|
||||
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
|
@ -19,7 +17,6 @@ out gl_PerVertex {
|
|||
|
||||
void main() {
|
||||
gl_Position = ubo.view * model * vec4(pos, 1.0);
|
||||
fragColor = color;
|
||||
|
||||
fragTexCoord = tex;
|
||||
|
||||
|
|
|
@ -7,9 +7,11 @@ use cgmath as cgm;
|
|||
|
||||
mod render;
|
||||
mod util;
|
||||
mod physics;
|
||||
|
||||
use render::vulkan::data;
|
||||
use render::renderable::{Object, Renderable, Resource, ResourceManager, Texture, Mesh};
|
||||
use render::renderable::{Object, Renderable, Resource, ResourceManager, Material, Mesh, ImageRefOrColor};
|
||||
use physics::color;
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
@ -64,9 +66,10 @@ fn main() {
|
|||
rm.add(Resource::Mesh(Mesh::new(vertices, indices)))
|
||||
};
|
||||
|
||||
let path = &crate::util::file::resource_path(String::from("assets/test-128px.png"));
|
||||
let image = Arc::new(image::open(path).unwrap());
|
||||
let texture_cube = rm.add(Resource::Texture(Texture::new(image)));
|
||||
let material_cube = rm.add(Resource::Material(Material::new(
|
||||
ImageRefOrColor::image(String::from("assets/test-128px.png")),
|
||||
ImageRefOrColor::color(color::LinearF32::new(1.0)),
|
||||
)));
|
||||
|
||||
let mut renderer = render::Renderer::initialize();
|
||||
|
||||
|
@ -78,7 +81,7 @@ fn main() {
|
|||
let cube = render::renderable::Object {
|
||||
mesh: mesh_cube,
|
||||
transform: transform,
|
||||
texture: texture_cube,
|
||||
material: material_cube,
|
||||
};
|
||||
cubes.push(Box::new(cube));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct XYZ {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32,
|
||||
}
|
||||
|
||||
impl XYZ {
|
||||
pub fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
Self {
|
||||
x, y, z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct sRGB {
|
||||
pub r: u8,
|
||||
pub g: u8,
|
||||
pub b: u8,
|
||||
}
|
||||
|
||||
impl sRGB {
|
||||
pub fn new(r: u8, g: u8, b: u8) -> Self {
|
||||
Self {
|
||||
r, g, b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LinearF32 {
|
||||
pub d: f32,
|
||||
}
|
||||
|
||||
impl LinearF32 {
|
||||
pub fn new(d: f32) -> Self {
|
||||
Self {
|
||||
d,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub mod color;
|
|
@ -14,22 +14,24 @@ use vulkano::format as vf;
|
|||
use vulkano::image as vm;
|
||||
|
||||
use crate::render::vulkan::data;
|
||||
use crate::physics::color;
|
||||
use crate::util::file;
|
||||
|
||||
pub struct ResourceManager {
|
||||
meshes: HashMap<u64, Mesh>,
|
||||
textures: HashMap<u64, Texture>,
|
||||
materials: HashMap<u64, Material>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ResourceID {
|
||||
Texture(u64),
|
||||
Material(u64),
|
||||
Mesh(u64),
|
||||
}
|
||||
|
||||
impl hash::Hash for ResourceID {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
ResourceID::Texture(i) => i.hash(state),
|
||||
ResourceID::Material(i) => i.hash(state),
|
||||
ResourceID::Mesh(i) => i.hash(state),
|
||||
}
|
||||
}
|
||||
|
@ -38,11 +40,11 @@ impl hash::Hash for ResourceID {
|
|||
impl PartialEq for ResourceID {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let this = match self {
|
||||
ResourceID::Texture(i) => i,
|
||||
ResourceID::Material(i) => i,
|
||||
ResourceID::Mesh(i) => i,
|
||||
};
|
||||
let that = match other {
|
||||
ResourceID::Texture(i) => i,
|
||||
ResourceID::Material(i) => i,
|
||||
ResourceID::Mesh(i) => i,
|
||||
};
|
||||
this == that
|
||||
|
@ -52,7 +54,7 @@ impl PartialEq for ResourceID {
|
|||
impl Eq for ResourceID {}
|
||||
|
||||
pub enum Resource {
|
||||
Texture(Texture),
|
||||
Material(Material),
|
||||
Mesh(Mesh),
|
||||
}
|
||||
|
||||
|
@ -60,16 +62,16 @@ impl<'a> ResourceManager {
|
|||
pub fn new() -> Self {
|
||||
Self {
|
||||
meshes: HashMap::new(),
|
||||
textures: HashMap::new(),
|
||||
materials: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, r: Resource) -> ResourceID {
|
||||
match r {
|
||||
Resource::Texture(t) => {
|
||||
Resource::Material(t) => {
|
||||
let id = t.id;
|
||||
self.textures.insert(id, t);
|
||||
ResourceID::Texture(id)
|
||||
self.materials.insert(id, t);
|
||||
ResourceID::Material(id)
|
||||
}
|
||||
Resource::Mesh(t) => {
|
||||
let id = t.id;
|
||||
|
@ -79,9 +81,9 @@ impl<'a> ResourceManager {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn texture(&'a self, id: &ResourceID) -> Option<&'a Texture> {
|
||||
if let ResourceID::Texture(i) = id {
|
||||
return Some(self.textures.get(&i).unwrap());
|
||||
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
|
||||
}
|
||||
|
@ -94,47 +96,169 @@ impl<'a> ResourceManager {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Texture {
|
||||
image: Arc<image::DynamicImage>,
|
||||
pub trait ChannelLayout {
|
||||
fn vulkan_from_image(
|
||||
image: Arc<image::DynamicImage>,
|
||||
graphics_queue: Arc<vd::Queue>,
|
||||
) -> Arc<vm::ImmutableImage<vf::Format>>;
|
||||
|
||||
fn vulkan_from_value(
|
||||
&self,
|
||||
graphics_queue: Arc<vd::Queue>,
|
||||
) -> Arc<vm::ImmutableImage<vf::Format>>;
|
||||
}
|
||||
|
||||
impl ChannelLayout for color::XYZ {
|
||||
fn vulkan_from_image(
|
||||
image: Arc<image::DynamicImage>,
|
||||
graphics_queue: Arc<vd::Queue>,
|
||||
) -> Arc<vm::ImmutableImage<vf::Format>> {
|
||||
let (width, height) = (image.width(), image.height());
|
||||
let rgba = image.to_rgba();
|
||||
// TODO(q3k): RGB -> CIE XYZ
|
||||
let (image_view, future) = vm::ImmutableImage::from_iter(
|
||||
rgba.into_raw().iter().cloned(),
|
||||
vm::Dimensions::Dim2d{ width, height },
|
||||
vf::Format::R8G8B8A8Unorm,
|
||||
graphics_queue.clone(),
|
||||
).unwrap();
|
||||
|
||||
future.flush().unwrap();
|
||||
image_view
|
||||
}
|
||||
|
||||
fn vulkan_from_value(
|
||||
&self,
|
||||
graphics_queue: Arc<vd::Queue>,
|
||||
) -> Arc<vm::ImmutableImage<vf::Format>> {
|
||||
let mut image = image::ImageBuffer::<image::Rgba<f32>, Vec<f32>>::new(1, 1);
|
||||
image.put_pixel(0, 0, image::Rgba([self.x, self.y, self.z, 0.0]));
|
||||
|
||||
let (image_view, future) = vm::ImmutableImage::from_iter(
|
||||
image.into_raw().iter().cloned(),
|
||||
vm::Dimensions::Dim2d{ width: 1, height: 1 },
|
||||
vf::Format::R32G32B32A32Sfloat,
|
||||
graphics_queue.clone(),
|
||||
).unwrap();
|
||||
future.flush().unwrap();
|
||||
image_view
|
||||
}
|
||||
}
|
||||
|
||||
impl ChannelLayout for color::LinearF32 {
|
||||
fn vulkan_from_image(
|
||||
image: Arc<image::DynamicImage>,
|
||||
graphics_queue: Arc<vd::Queue>,
|
||||
) -> Arc<vm::ImmutableImage<vf::Format>> {
|
||||
let (width, height) = (image.width(), image.height());
|
||||
assert!(match image.color() {
|
||||
image::ColorType::L8 => true,
|
||||
image::ColorType::L16 => true,
|
||||
_ => false,
|
||||
}, "linearf32 texture must be 8-bit grayscale");
|
||||
let gray = image.to_luma();
|
||||
let (image_view, future) = vm::ImmutableImage::from_iter(
|
||||
gray.into_raw().iter().cloned(),
|
||||
vm::Dimensions::Dim2d{ width, height },
|
||||
vf::Format::R8G8B8A8Unorm,
|
||||
graphics_queue.clone(),
|
||||
).unwrap();
|
||||
|
||||
future.flush().unwrap();
|
||||
image_view
|
||||
}
|
||||
|
||||
fn vulkan_from_value(
|
||||
&self,
|
||||
graphics_queue: Arc<vd::Queue>,
|
||||
) -> Arc<vm::ImmutableImage<vf::Format>> {
|
||||
let mut image = image::ImageBuffer::<image::Luma<f32>, Vec<f32>>::new(1, 1);
|
||||
image.put_pixel(0, 0, image::Luma([self.d]));
|
||||
|
||||
let (image_view, future) = vm::ImmutableImage::from_iter(
|
||||
image.into_raw().iter().cloned(),
|
||||
vm::Dimensions::Dim2d{ width: 1, height: 1 },
|
||||
vf::Format::R32Sfloat,
|
||||
graphics_queue.clone(),
|
||||
).unwrap();
|
||||
|
||||
future.flush().unwrap();
|
||||
image_view
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ImageRefOrColor<T: ChannelLayout> {
|
||||
Color(T),
|
||||
ImageRef(ImageRef),
|
||||
}
|
||||
|
||||
impl<T: ChannelLayout> ImageRefOrColor<T> {
|
||||
fn vulkan_image(&self, graphics_queue: Arc<vd::Queue>) -> Arc<vm::ImmutableImage<vf::Format>> {
|
||||
match self {
|
||||
ImageRefOrColor::<T>::Color(c) => c.vulkan_from_value(graphics_queue),
|
||||
ImageRefOrColor::<T>::ImageRef(r) => T::vulkan_from_image(r.load(), graphics_queue),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color(color: T) -> Self {
|
||||
ImageRefOrColor::<T>::Color(color)
|
||||
}
|
||||
|
||||
pub fn image(name: String) -> Self {
|
||||
ImageRefOrColor::<T>::ImageRef(ImageRef{ name })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ImageRef {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl ImageRef {
|
||||
fn load (&self) -> Arc<image::DynamicImage> {
|
||||
let path = &file::resource_path(self.name.clone());
|
||||
Arc::new(image::open(path).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Material {
|
||||
diffuse: ImageRefOrColor<color::XYZ>,
|
||||
roughness: ImageRefOrColor<color::LinearF32>,
|
||||
|
||||
id: u64,
|
||||
// vulkan cache
|
||||
vulkan: Mutex<Option<Arc<vm::ImmutableImage<vf::Format>>>>,
|
||||
vulkan: Mutex<Option<data::Textures>>,
|
||||
}
|
||||
impl Texture {
|
||||
|
||||
impl Material {
|
||||
pub fn new(
|
||||
image: Arc<image::DynamicImage>,
|
||||
diffuse: ImageRefOrColor<color::XYZ>,
|
||||
roughness: ImageRefOrColor<color::LinearF32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
image,
|
||||
diffuse,
|
||||
roughness,
|
||||
|
||||
// TODO: use a better method
|
||||
id: time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos() as u64,
|
||||
vulkan: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
pub fn vulkan_texture(
|
||||
|
||||
pub fn vulkan_textures(
|
||||
&self,
|
||||
graphics_queue: Arc<vd::Queue>,
|
||||
) -> Arc<vm::ImmutableImage<vf::Format>> {
|
||||
) -> data::Textures {
|
||||
let mut cache = self.vulkan.lock().unwrap();
|
||||
match &mut *cache {
|
||||
Some(data) => data.clone(),
|
||||
None => {
|
||||
let width = self.image.width();
|
||||
let height = self.image.height();
|
||||
let image_rgba = self.image.to_rgba();
|
||||
let (image_view, future) = vm::ImmutableImage::from_iter(
|
||||
image_rgba.into_raw().iter().cloned(),
|
||||
vm::Dimensions::Dim2d{ width, height },
|
||||
vf::Format::R8G8B8A8Unorm,
|
||||
graphics_queue.clone(),
|
||||
).unwrap();
|
||||
|
||||
future.flush().unwrap();
|
||||
|
||||
*cache = Some(image_view.clone());
|
||||
|
||||
image_view
|
||||
let diffuse = self.diffuse.vulkan_image(graphics_queue.clone());
|
||||
let roughness = self.roughness.vulkan_image(graphics_queue.clone());
|
||||
let textures = data::Textures {
|
||||
diffuse, roughness,
|
||||
};
|
||||
*cache = Some(textures.clone());
|
||||
textures
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -226,12 +350,12 @@ pub trait Renderable {
|
|||
|
||||
pub struct Object {
|
||||
pub mesh: ResourceID,
|
||||
pub texture: 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.texture, &self.transform))
|
||||
Some((self.mesh, self.material, &self.transform))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use vulkano::image as vm;
|
||||
use vulkano::format::Format;
|
||||
|
||||
use cgmath as cgm;
|
||||
|
||||
#[derive(Default, Copy, Clone)]
|
||||
|
@ -35,3 +40,11 @@ vulkano::impl_vertex!(Instance, model);
|
|||
pub struct UniformBufferObject {
|
||||
pub view: cgm::Matrix4<f32>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Textures {
|
||||
// diffuse: RGB
|
||||
pub diffuse: Arc<vm::ImmutableImage<Format>>,
|
||||
// roughness: R
|
||||
pub roughness: Arc<vm::ImmutableImage<Format>>,
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
|||
let mut buffers: Vec<Box<vc::AutoCommandBuffer>> = vec![];
|
||||
|
||||
|
||||
// Sort renderables by mesh and textureid.
|
||||
// Sort renderables by mesh and materialid.
|
||||
let mut meshes: ResourceMap<(renderable::ResourceID, renderable::ResourceID), &cgm::Matrix4<f32>> = ResourceMap::new();
|
||||
|
||||
let ubo = data::UniformBufferObject {
|
||||
|
@ -173,8 +173,8 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
|||
|
||||
profiler.end("mgc.prep");
|
||||
for r in renderables {
|
||||
if let Some((mesh_id, texture_id, transform)) = r.render_data() {
|
||||
meshes.add((mesh_id, texture_id), transform);
|
||||
if let Some((mesh_id, material_id, transform)) = r.render_data() {
|
||||
meshes.add((mesh_id, material_id), transform);
|
||||
}
|
||||
}
|
||||
profiler.end("mgc.sort");
|
||||
|
@ -185,9 +185,9 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
|||
|
||||
let pipeline = self.pipeline.as_ref().unwrap().get_pipeline().clone();
|
||||
|
||||
for ((mesh_id, texture_id), transforms) in meshes.resources {
|
||||
for ((mesh_id, material_id), transforms) in meshes.resources {
|
||||
let mesh = rm.mesh(&mesh_id).unwrap();
|
||||
let texture = rm.texture(&texture_id).unwrap();
|
||||
let material = rm.material(&material_id).unwrap();
|
||||
|
||||
let mut builder = vc::AutoCommandBufferBuilder::secondary_graphics_one_time_submit(
|
||||
device.clone(), queue.family(), vf::Subpass::from(rp.clone(), 0).unwrap()).unwrap();
|
||||
|
@ -199,7 +199,7 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
|
|||
).unwrap();
|
||||
future.flush().unwrap();
|
||||
|
||||
let image = texture.vulkan_texture(queue.clone());
|
||||
let image = material.vulkan_textures(queue.clone()).diffuse;
|
||||
let ds = self.pipeline.as_mut().unwrap().make_descriptor_set(image);
|
||||
|
||||
let (vbuffer, ibuffer) = mesh.vulkan_buffers(queue.clone());
|
||||
|
|
|
@ -38,25 +38,17 @@ impl Forward {
|
|||
name: Some(Cow::Borrowed("pos")),
|
||||
},
|
||||
vps::ShaderInterfaceDefEntry {
|
||||
location: 1..2, format: Format::R32G32B32Sfloat,
|
||||
name: Some(Cow::Borrowed("color")),
|
||||
},
|
||||
vps::ShaderInterfaceDefEntry {
|
||||
location: 2..6, format: Format::R32G32B32A32Sfloat,
|
||||
location: 1..5, format: Format::R32G32B32A32Sfloat,
|
||||
name: Some(Cow::Borrowed("model")),
|
||||
},
|
||||
vps::ShaderInterfaceDefEntry {
|
||||
location: 6..7, format: Format::R32G32Sfloat,
|
||||
location: 5..6, format: Format::R32G32Sfloat,
|
||||
name: Some(Cow::Borrowed("tex")),
|
||||
},
|
||||
],
|
||||
outputs: vec![
|
||||
vps::ShaderInterfaceDefEntry {
|
||||
location: 0..1, format: Format::R32G32B32Sfloat,
|
||||
name: Some(Cow::Borrowed("fragColor")),
|
||||
},
|
||||
vps::ShaderInterfaceDefEntry {
|
||||
location: 1..2, format: Format::R32G32Sfloat,
|
||||
location: 0..1, format: Format::R32G32Sfloat,
|
||||
name: Some(Cow::Borrowed("fragTexCoord")),
|
||||
},
|
||||
],
|
||||
|
@ -78,11 +70,7 @@ impl Forward {
|
|||
ty: vps::GraphicsShaderType::Fragment,
|
||||
inputs: vec![
|
||||
vps::ShaderInterfaceDefEntry {
|
||||
location: 0..1, format: Format::R32G32B32Sfloat,
|
||||
name: Some(Cow::Borrowed("fragColor")),
|
||||
},
|
||||
vps::ShaderInterfaceDefEntry {
|
||||
location: 1..2, format: Format::R32G32Sfloat,
|
||||
location: 0..1, format: Format::R32G32Sfloat,
|
||||
name: Some(Cow::Borrowed("fragTexCoord")),
|
||||
},
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue