various overhauls to data formats
This commit is contained in:
+14
-3
@@ -1,10 +1,21 @@
|
||||
#version 450
|
||||
|
||||
layout(location=0)in vec3 fragColor;
|
||||
layout(location=0)in vec3 normal;
|
||||
layout(location=0)out vec4 f_color;
|
||||
|
||||
const vec3 light=normalize(vec3(4,6,8));
|
||||
layout(set=0,binding=0)uniform Data{
|
||||
vec4[32]pos;
|
||||
vec4[32]col;
|
||||
uint light_count;
|
||||
}uniforms;
|
||||
|
||||
void main(){
|
||||
f_color=vec4(vec3(dot(fragColor,light))*.5+.5,1.);
|
||||
vec3 accum=vec3(0.,0.,0.);
|
||||
|
||||
for(int i=0;i<uniforms.light_count;i++)
|
||||
{
|
||||
accum+=uniforms.col[i].xyz*((dot(normalize(normal),uniforms.pos[i].xyz)*.5)+.5);
|
||||
}
|
||||
|
||||
f_color=vec4(accum,1.);
|
||||
}
|
||||
+43
-2
@@ -1,6 +1,11 @@
|
||||
use egui::{Color32, Frame, Id, ScrollArea, TextEdit, TextStyle};
|
||||
use egui::{
|
||||
plot::{Line, Plot, PlotPoints},
|
||||
Color32, Frame, Id, ScrollArea, TextEdit, TextStyle,
|
||||
};
|
||||
use egui_winit_vulkano::Gui;
|
||||
|
||||
use crate::objects::{Light, Mesh};
|
||||
|
||||
fn sized_text(ui: &mut egui::Ui, text: impl Into<String>, size: f32) {
|
||||
ui.label(egui::RichText::new(text).size(size));
|
||||
}
|
||||
@@ -17,6 +22,11 @@ Vulkan(o) is hard, that I know...
|
||||
pub struct GState {
|
||||
pub cursor_sensitivity: f32,
|
||||
pub move_speed: f32,
|
||||
|
||||
pub meshes: Vec<Mesh>,
|
||||
pub lights: Vec<Light>,
|
||||
|
||||
pub fps: [f64; 128],
|
||||
}
|
||||
|
||||
impl Default for GState {
|
||||
@@ -24,6 +34,11 @@ impl Default for GState {
|
||||
Self {
|
||||
cursor_sensitivity: 1.0,
|
||||
move_speed: 1.0,
|
||||
|
||||
meshes: vec![],
|
||||
lights: vec![],
|
||||
|
||||
fps: [0.0; 128],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,10 +55,36 @@ pub fn gui_up(gui: &mut Gui, state: &mut GState) {
|
||||
sized_text(ui, "Settings", 32.0);
|
||||
});
|
||||
ui.separator();
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
//ui.heading("Camera Control");
|
||||
ui.heading("Camera Control");
|
||||
ui.add(egui::Slider::new(&mut state.cursor_sensitivity, 0.0..=2.0).text("Mouse Sensitivity"));
|
||||
ui.add(egui::Slider::new(&mut state.move_speed, 0.0..=2.0).text("Movement Speed"));
|
||||
ui.heading("Meshes");
|
||||
for mesh in &mut state.meshes {
|
||||
ui.label(mesh.name.clone());
|
||||
ui.add(egui::Slider::new(&mut mesh.pos.x, -100.0..=100.0).text("Position.x"));
|
||||
ui.add(egui::Slider::new(&mut mesh.pos.y, -100.0..=100.0).text("Position.y"));
|
||||
ui.add(egui::Slider::new(&mut mesh.pos.z, -100.0..=100.0).text("Position.z"));
|
||||
ui.add(egui::Slider::new(&mut mesh.rot.x.0, 0.0..=360.0).text("Rotation.x"));
|
||||
ui.add(egui::Slider::new(&mut mesh.rot.y.0, 0.0..=360.0).text("Rotation.y"));
|
||||
ui.add(egui::Slider::new(&mut mesh.rot.z.0, 0.0..=360.0).text("Rotation.z"));
|
||||
}
|
||||
ui.heading("Lights");
|
||||
for light in &mut state.lights {
|
||||
ui.label("Light");
|
||||
ui.add(egui::Slider::new(&mut light.pos.x, -100.0..=100.0).text("Position.x"));
|
||||
ui.add(egui::Slider::new(&mut light.pos.y, -100.0..=100.0).text("Position.y"));
|
||||
ui.add(egui::Slider::new(&mut light.pos.z, -100.0..=100.0).text("Position.z"));
|
||||
ui.add(egui::Slider::new(&mut light.colour.x, 0.0..=1.0).text("Colour.r"));
|
||||
ui.add(egui::Slider::new(&mut light.colour.y, 0.0..=1.0).text("Colour.g"));
|
||||
ui.add(egui::Slider::new(&mut light.colour.z, 0.0..=1.0).text("Colour.b"));
|
||||
}
|
||||
let fps: PlotPoints = state.fps.iter().enumerate().map(|(x,y)| [x as f64,*y]).collect::<Vec<_>>().into();
|
||||
let line = Line::new(fps);
|
||||
ui.heading("FPS");
|
||||
Plot::new("fps").view_aspect(2.0).show(ui, |plot_ui| plot_ui.line(line));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+156
-103
@@ -24,13 +24,19 @@ use obj::{LoadConfig, ObjData};
|
||||
use rodio::{source::Source, Decoder, OutputStream};
|
||||
use std::io::Cursor;
|
||||
use std::{sync::Arc, time::Instant};
|
||||
use vulkano::buffer::CpuBufferPool;
|
||||
use vulkano::command_buffer::allocator::StandardCommandBufferAllocator;
|
||||
use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
|
||||
use vulkano::descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet};
|
||||
use vulkano::device::DeviceOwned;
|
||||
use vulkano::format::Format;
|
||||
use vulkano::image::AttachmentImage;
|
||||
use vulkano::memory::allocator::StandardMemoryAllocator;
|
||||
use vulkano::memory::allocator::{MemoryUsage, StandardMemoryAllocator};
|
||||
use vulkano::pipeline::graphics::depth_stencil::DepthStencilState;
|
||||
use vulkano::pipeline::graphics::rasterization::CullMode;
|
||||
use vulkano::pipeline::graphics::rasterization::FrontFace::Clockwise;
|
||||
use vulkano::pipeline::PipelineBindPoint;
|
||||
use vulkano::shader::ShaderModule;
|
||||
use vulkano::swapchain::{PresentMode, SwapchainPresentInfo};
|
||||
use vulkano::VulkanLibrary;
|
||||
use winit::event::{DeviceEvent, DeviceId, ElementState, MouseButton, VirtualKeyCode};
|
||||
@@ -70,8 +76,12 @@ use winit::{
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
use crate::gui::*;
|
||||
mod gui;
|
||||
use crate::gui::*;
|
||||
mod objects;
|
||||
use crate::objects::*;
|
||||
|
||||
pub type MemoryAllocator = StandardMemoryAllocator;
|
||||
|
||||
fn main() {
|
||||
// The first step of any Vulkan program is to create an instance.
|
||||
@@ -81,7 +91,7 @@ fn main() {
|
||||
// All the window-drawing functionalities are part of non-core extensions that we need
|
||||
// to enable manually. To do so, we ask the `vulkano_win` crate for the list of extensions
|
||||
// required to draw to a window.
|
||||
let library = VulkanLibrary::new().unwrap();
|
||||
let library = VulkanLibrary::new().expect("Vulkan is not installed???");
|
||||
let required_extensions = vulkano_win::required_extensions(&library);
|
||||
|
||||
// Now creating the instance.
|
||||
@@ -284,50 +294,6 @@ fn main() {
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
const OBJ: &[u8] = include_bytes!("bunny.obj");
|
||||
|
||||
let buny = ObjData::load_buf_with_config(OBJ, LoadConfig::default()).unwrap();
|
||||
|
||||
let polys = &buny.objects[0].groups[0].polys;
|
||||
|
||||
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||
|
||||
// We now create a buffer that will store the shape of our triangle.
|
||||
// We use #[repr(C)] here to force rustc to not do anything funky with our data, although for this
|
||||
// particular example, it doesn't actually change the in-memory representation.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
|
||||
struct Vertex {
|
||||
position: [f32; 3],
|
||||
normal: [f32; 3],
|
||||
}
|
||||
impl_vertex!(Vertex, position, normal);
|
||||
|
||||
let vertices = polys
|
||||
.iter()
|
||||
.flat_map(|p| {
|
||||
p.0.iter()
|
||||
.map(|v| Vertex {
|
||||
position: buny.position[v.0],
|
||||
normal: v
|
||||
.2
|
||||
.and_then(|vt| Some(buny.normal[vt]))
|
||||
.unwrap_or([0.0, 0.0, 0.0]),
|
||||
})
|
||||
.collect::<Vec<Vertex>>()
|
||||
})
|
||||
.collect::<Vec<Vertex>>();
|
||||
let vertex_buffer = CpuAccessibleBuffer::from_iter(
|
||||
&memory_allocator,
|
||||
BufferUsage {
|
||||
vertex_buffer: true,
|
||||
..BufferUsage::empty()
|
||||
},
|
||||
false,
|
||||
vertices,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// The next step is to create the shaders.
|
||||
//
|
||||
// The raw shader creation API provided by the vulkano library is unsafe for various
|
||||
@@ -344,7 +310,7 @@ fn main() {
|
||||
//
|
||||
// A more detailed overview of what the `shader!` macro generates can be found in the
|
||||
// `vulkano-shaders` crate docs. You can view them at https://docs.rs/vulkano-shaders/
|
||||
mod vs {
|
||||
mod mesh_vs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "vertex",
|
||||
src: "
|
||||
@@ -363,7 +329,7 @@ fn main() {
|
||||
|
||||
void main() {
|
||||
mat4 worldview = pc.view * pc.world;
|
||||
v_normal = normalize(transpose(inverse(mat3(worldview))) * normal);
|
||||
v_normal = normal; //normalize(transpose(inverse(mat3(worldview))) * normal);
|
||||
gl_Position = pc.proj * worldview * vec4(position*1000.0, 1.0);
|
||||
}
|
||||
",
|
||||
@@ -375,19 +341,26 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
mod fs {
|
||||
mod mesh_fs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
path: "src/frag.glsl"
|
||||
path: "src/frag.glsl",
|
||||
types_meta: {
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
#[derive(Clone, Copy, Zeroable, Pod, Debug)]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let vs = vs::load(device.clone()).unwrap();
|
||||
let fs = fs::load(device.clone()).unwrap();
|
||||
let mesh_vs = mesh_vs::load(device.clone()).unwrap();
|
||||
let mesh_fs = mesh_fs::load(device.clone()).unwrap();
|
||||
|
||||
/*let uniform_buffer =
|
||||
CpuBufferPool::<vs::ty::PushConstantData>::uniform_buffer(memory_allocator);*/
|
||||
|
||||
let memory_allocator = Arc::new(MemoryAllocator::new_default(device.clone()));
|
||||
|
||||
// At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL
|
||||
// implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this
|
||||
// manually.
|
||||
@@ -439,33 +412,6 @@ fn main() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Before we draw we have to create what is called a pipeline. This is similar to an OpenGL
|
||||
// program, but much more specific.
|
||||
let pipeline = GraphicsPipeline::start()
|
||||
// We have to indicate which subpass of which render pass this pipeline is going to be used
|
||||
// in. The pipeline will only be usable from this particular subpass.
|
||||
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
|
||||
// We need to indicate the layout of the vertices.
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
// The content of the vertex buffer describes a list of triangles.
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
// A Vulkan shader can in theory contain multiple entry points, so we have to specify
|
||||
// which one.
|
||||
.vertex_shader(vs.entry_point("main").unwrap(), ())
|
||||
// Use a resizable viewport set to draw over the entire window
|
||||
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
|
||||
// See `vertex_shader`.
|
||||
.fragment_shader(fs.entry_point("main").unwrap(), ())
|
||||
.depth_stencil_state(DepthStencilState::simple_depth_test())
|
||||
.rasterization_state(RasterizationState {
|
||||
front_face: Fixed(Clockwise),
|
||||
cull_mode: Fixed(CullMode::Back),
|
||||
..RasterizationState::default()
|
||||
})
|
||||
// Now that our builder is filled, we call `build()` to obtain an actual pipeline.
|
||||
.build(device.clone())
|
||||
.unwrap();
|
||||
|
||||
// Dynamic viewports allow us to recreate just the viewport when the window is resized
|
||||
// Otherwise we would have to recreate the whole pipeline.
|
||||
let mut viewport = Viewport {
|
||||
@@ -479,8 +425,10 @@ fn main() {
|
||||
//
|
||||
// Since we need to draw to multiple images, we are going to create a different framebuffer for
|
||||
// each image.
|
||||
let mut framebuffers = window_size_dependent_setup(
|
||||
let ([mut mesh_pipeline], mut framebuffers) = window_size_dependent_setup(
|
||||
&memory_allocator,
|
||||
&mesh_vs,
|
||||
&mesh_fs,
|
||||
&images,
|
||||
render_pass.clone(),
|
||||
&mut viewport,
|
||||
@@ -524,9 +472,18 @@ fn main() {
|
||||
stream_handle.play_raw(source.convert_samples()).unwrap();
|
||||
*/
|
||||
|
||||
let rotation_start = Instant::now();
|
||||
let mut render_start = Instant::now();
|
||||
|
||||
//let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
|
||||
let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone());
|
||||
|
||||
let uniform_buffer = CpuBufferPool::<mesh_fs::ty::Data>::new(
|
||||
memory_allocator.clone(),
|
||||
BufferUsage {
|
||||
uniform_buffer: true,
|
||||
..BufferUsage::empty()
|
||||
},
|
||||
MemoryUsage::Upload,
|
||||
);
|
||||
|
||||
// Create an egui GUI
|
||||
let mut gui = Gui::new_with_subpass(
|
||||
@@ -561,6 +518,19 @@ fn main() {
|
||||
d: false,
|
||||
};
|
||||
|
||||
gstate.meshes.push(load_obj(
|
||||
&memory_allocator,
|
||||
&mut Cursor::new(PLATONIC_SOLIDS[0].1),
|
||||
PLATONIC_SOLIDS[0].0.to_string(),
|
||||
));
|
||||
|
||||
gstate
|
||||
.lights
|
||||
.push(Light::new([4., 6., 8.], [1., 1., 8.], 0.01));
|
||||
gstate
|
||||
.lights
|
||||
.push(Light::new([-4., 6., -8.], [8., 4., 1.], 0.01));
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
if let Event::WindowEvent { event: we, .. } = &event {
|
||||
if !gui.update(we) {
|
||||
@@ -613,12 +583,23 @@ fn main() {
|
||||
..
|
||||
} => {
|
||||
if looking {
|
||||
camforward.x -= Deg(delta.1 as f32) * gstate.cursor_sensitivity;
|
||||
camforward.y += Deg(delta.0 as f32) * gstate.cursor_sensitivity;
|
||||
camforward.x -= Deg(delta.1 as f32) * gstate.cursor_sensitivity * 0.3;
|
||||
camforward.y += Deg(delta.0 as f32) * gstate.cursor_sensitivity * 0.3;
|
||||
camforward.x = camforward.x + Deg(360f32) % Deg(360f32);
|
||||
camforward.y = camforward.y + Deg(360f32) % Deg(360f32);
|
||||
}
|
||||
//println!("AXISM {:?}", delta);
|
||||
}
|
||||
Event::RedrawEventsCleared => {
|
||||
for i in 1..gstate.fps.len() {
|
||||
gstate.fps[i - 1] = gstate.fps[i];
|
||||
}
|
||||
|
||||
gstate.fps[gstate.fps.len() - 1] =
|
||||
1.0 / (Instant::now() - render_start).as_secs_f64();
|
||||
|
||||
render_start = Instant::now();
|
||||
|
||||
// Do not draw frame when screen dimensions are zero.
|
||||
// On Windows, this can occur from minimizing the application.
|
||||
let window = surface.object().unwrap().downcast_ref::<Window>().unwrap();
|
||||
@@ -653,8 +634,10 @@ fn main() {
|
||||
swapchain = new_swapchain;
|
||||
// Because framebuffers contains an Arc on the old swapchain, we need to
|
||||
// recreate framebuffers as well.
|
||||
framebuffers = window_size_dependent_setup(
|
||||
([mesh_pipeline], framebuffers) = window_size_dependent_setup(
|
||||
&memory_allocator,
|
||||
&mesh_vs,
|
||||
&mesh_fs,
|
||||
&new_images,
|
||||
render_pass.clone(),
|
||||
&mut viewport,
|
||||
@@ -664,7 +647,7 @@ fn main() {
|
||||
|
||||
//println!("{:?}", right);
|
||||
|
||||
let uniform_data = {
|
||||
let mut push_constants = {
|
||||
if looking {
|
||||
if keys.w {
|
||||
campos -= Matrix3::from_angle_y(camforward.y)
|
||||
@@ -718,7 +701,7 @@ fn main() {
|
||||
* Matrix4::from_scale(scale);
|
||||
//*Matrix4::from_angle_z(Deg(180f32));
|
||||
|
||||
let pc = vs::ty::PushConstantData {
|
||||
let pc = mesh_vs::ty::PushConstantData {
|
||||
world: Matrix4::identity().into(),
|
||||
view: view.into(),
|
||||
proj: proj.into(),
|
||||
@@ -735,13 +718,35 @@ fn main() {
|
||||
pc
|
||||
};
|
||||
|
||||
//let layout = pipeline.layout().set_layouts().get(0).unwrap();
|
||||
/*let set = PersistentDescriptorSet::new(
|
||||
&memory_allocator,
|
||||
let uniform_buffer_subbuffer = {
|
||||
let mut pos = [[0f32; 4]; 32];
|
||||
let mut col = [[0f32; 4]; 32];
|
||||
|
||||
for (i, light) in gstate.lights.iter().enumerate() {
|
||||
pos[i][0] = light.pos.x;
|
||||
pos[i][1] = light.pos.y;
|
||||
pos[i][2] = light.pos.z;
|
||||
col[i][0] = light.colour.x;
|
||||
col[i][1] = light.colour.y;
|
||||
col[i][2] = light.colour.z;
|
||||
}
|
||||
|
||||
let uniform_data = mesh_fs::ty::Data {
|
||||
pos,
|
||||
col,
|
||||
light_count: gstate.lights.len() as u32,
|
||||
};
|
||||
|
||||
uniform_buffer.from_data(uniform_data).unwrap()
|
||||
};
|
||||
|
||||
let layout = mesh_pipeline.layout().set_layouts().get(0).unwrap();
|
||||
let set = PersistentDescriptorSet::new(
|
||||
&descriptor_set_allocator,
|
||||
layout.clone(),
|
||||
[WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)],
|
||||
)
|
||||
.unwrap();*/
|
||||
.unwrap();
|
||||
|
||||
// Before we can draw on the output, we have to *acquire* an image from the swapchain. If
|
||||
// no image is available (which happens if you submit draw commands too quickly), then the
|
||||
@@ -816,19 +821,31 @@ fn main() {
|
||||
// The last two parameters contain the list of resources to pass to the shaders.
|
||||
// Since we used an `EmptyPipeline` object, the objects have to be `()`.
|
||||
.set_viewport(0, [viewport.clone()])
|
||||
.bind_pipeline_graphics(pipeline.clone())
|
||||
/*.bind_descriptor_sets(
|
||||
.bind_pipeline_graphics(mesh_pipeline.clone())
|
||||
.bind_descriptor_sets(
|
||||
PipelineBindPoint::Graphics,
|
||||
pipeline.layout().clone(),
|
||||
mesh_pipeline.layout().clone(),
|
||||
0,
|
||||
set,
|
||||
)*/
|
||||
.bind_vertex_buffers(0, vertex_buffer.clone())
|
||||
.push_constants(pipeline.layout().clone(), 0, uniform_data)
|
||||
.draw(vertex_buffer.len() as u32, 1, 0, 0)
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
for object in &gstate.meshes {
|
||||
push_constants.world =
|
||||
(Matrix4::from_translation(object.pos - Point3::origin())
|
||||
* Matrix4::from(object.rot)
|
||||
* object.scale)
|
||||
.into();
|
||||
builder
|
||||
.bind_vertex_buffers(0, object.vertices.clone())
|
||||
.bind_index_buffer(object.indices.clone())
|
||||
.push_constants(mesh_pipeline.layout().clone(), 0, push_constants)
|
||||
.draw_indexed(object.indices.len() as u32, 1, 0, 0, 0)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// We leave the render pass. Note that if we had multiple
|
||||
// subpasses we could have called `next_subpass` to jump to the next subpass.
|
||||
builder
|
||||
.next_subpass(SubpassContents::SecondaryCommandBuffers)
|
||||
.unwrap()
|
||||
.execute_commands(cb)
|
||||
@@ -879,10 +896,12 @@ fn main() {
|
||||
/// This method is called once during initialization, then again whenever the window is resized
|
||||
fn window_size_dependent_setup(
|
||||
allocator: &StandardMemoryAllocator,
|
||||
mesh_vs: &ShaderModule,
|
||||
mesh_fs: &ShaderModule,
|
||||
images: &[Arc<SwapchainImage>],
|
||||
render_pass: Arc<RenderPass>,
|
||||
viewport: &mut Viewport,
|
||||
) -> Vec<Arc<Framebuffer>> {
|
||||
) -> ([Arc<GraphicsPipeline>; 1], Vec<Arc<Framebuffer>>) {
|
||||
let dimensions = images[0].dimensions().width_height();
|
||||
viewport.dimensions = [dimensions[0] as f32, dimensions[1] as f32];
|
||||
|
||||
@@ -891,7 +910,7 @@ fn window_size_dependent_setup(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
images
|
||||
let framebuffers = images
|
||||
.iter()
|
||||
.map(|image| {
|
||||
let view = ImageView::new_default(image.clone()).unwrap();
|
||||
@@ -904,5 +923,39 @@ fn window_size_dependent_setup(
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Before we draw we have to create what is called a pipeline. This is similar to an OpenGL
|
||||
// program, but much more specific.
|
||||
let mesh_pipeline = GraphicsPipeline::start()
|
||||
// We have to indicate which subpass of which render pass this pipeline is going to be used
|
||||
// in. The pipeline will only be usable from this particular subpass.
|
||||
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
|
||||
// We need to indicate the layout of the vertices.
|
||||
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
|
||||
// The content of the vertex buffer describes a list of triangles.
|
||||
.input_assembly_state(InputAssemblyState::new())
|
||||
// A Vulkan shader can in theory contain multiple entry points, so we have to specify
|
||||
// which one.
|
||||
.vertex_shader(mesh_vs.entry_point("main").unwrap(), ())
|
||||
.viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([
|
||||
Viewport {
|
||||
origin: [0.0, 0.0],
|
||||
dimensions: [dimensions[0] as f32, dimensions[1] as f32],
|
||||
depth_range: 0.0..1.0,
|
||||
},
|
||||
]))
|
||||
// See `vertex_shader`.
|
||||
.fragment_shader(mesh_fs.entry_point("main").unwrap(), ())
|
||||
.depth_stencil_state(DepthStencilState::simple_depth_test())
|
||||
.rasterization_state(RasterizationState {
|
||||
front_face: Fixed(Clockwise),
|
||||
cull_mode: Fixed(CullMode::Back),
|
||||
..RasterizationState::default()
|
||||
})
|
||||
// Now that our builder is filled, we call `build()` to obtain an actual pipeline.
|
||||
.build(allocator.device().clone())
|
||||
.unwrap();
|
||||
|
||||
([mesh_pipeline], framebuffers)
|
||||
}
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
use std::{collections::HashMap, io::Read, sync::Arc};
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use cgmath::{Deg, Euler, Matrix3, Point3, SquareMatrix, Vector3};
|
||||
use obj::{LoadConfig, ObjData};
|
||||
use vulkano::{
|
||||
buffer::{BufferUsage, CpuAccessibleBuffer},
|
||||
impl_vertex,
|
||||
};
|
||||
|
||||
use crate::MemoryAllocator;
|
||||
|
||||
pub const PLATONIC_SOLIDS: [(&str, &[u8]); 1] = [("Buny", include_bytes!("bunny.obj"))];
|
||||
|
||||
// We now create a buffer that will store the shape of our triangle.
|
||||
// We use #[repr(C)] here to force rustc to not do anything funky with our data, although for this
|
||||
// particular example, it doesn't actually change the in-memory representation.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
|
||||
pub struct Vertex {
|
||||
position: [f32; 3],
|
||||
normal: [f32; 3],
|
||||
}
|
||||
impl_vertex!(Vertex, position, normal);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mesh {
|
||||
pub name: String,
|
||||
pub vertices: Arc<CpuAccessibleBuffer<[Vertex]>>,
|
||||
pub indices: Arc<CpuAccessibleBuffer<[u32]>>,
|
||||
pub pos: Point3<f32>,
|
||||
pub rot: Euler<Deg<f32>>,
|
||||
pub scale: f32,
|
||||
}
|
||||
|
||||
pub fn load_obj(memory_allocator: &MemoryAllocator, input: &mut dyn Read, name: String) -> Mesh {
|
||||
let object = ObjData::load_buf_with_config(input, LoadConfig::default()).unwrap();
|
||||
|
||||
let mut vertices = vec![];
|
||||
|
||||
let mut indices = vec![];
|
||||
|
||||
let mut temp_hash_map = HashMap::<(u32, u32), u32>::new();
|
||||
|
||||
// We're gonna have to remap all the indices that OBJ uses. Annoying.
|
||||
// Get each pair of vertex position to vertex normal and assign it a new index. This might duplicate
|
||||
// vertices or normals but each *pair* needs a unique index
|
||||
// Uses the hash map to check that we're not duplicating unnecessarily
|
||||
for g in &object.objects[0].groups {
|
||||
for p in &g.polys {
|
||||
for v in &p.0 {
|
||||
//println!("{:?}", v);
|
||||
let mapping = ((v.0) as u32, (v.2.unwrap_or(0)) as u32);
|
||||
if let Some(&exist) = &temp_hash_map.get(&mapping) {
|
||||
//println!("{:?}", exist);
|
||||
indices.push(exist);
|
||||
} else {
|
||||
vertices.push(Vertex {
|
||||
position: object.position[mapping.0 as usize],
|
||||
normal: object.normal[mapping.1 as usize],
|
||||
});
|
||||
temp_hash_map.insert(mapping, (vertices.len() - 1) as u32);
|
||||
indices.push((vertices.len() - 1) as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let vertex_buffer = CpuAccessibleBuffer::from_iter(
|
||||
memory_allocator,
|
||||
BufferUsage {
|
||||
vertex_buffer: true,
|
||||
..BufferUsage::empty()
|
||||
},
|
||||
false,
|
||||
vertices,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let index_buffer = CpuAccessibleBuffer::from_iter(
|
||||
memory_allocator,
|
||||
BufferUsage {
|
||||
index_buffer: true,
|
||||
..BufferUsage::empty()
|
||||
},
|
||||
false,
|
||||
indices,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Mesh {
|
||||
vertices: vertex_buffer,
|
||||
indices: index_buffer,
|
||||
pos: Point3 {
|
||||
x: 0.,
|
||||
y: 0.,
|
||||
z: 0.,
|
||||
},
|
||||
rot: Euler::new(Deg(0.), Deg(0.), Deg(0.)),
|
||||
scale: 1.,
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Light {
|
||||
pub pos: Point3<f32>,
|
||||
pub colour: Vector3<f32>,
|
||||
}
|
||||
|
||||
impl Light {
|
||||
pub fn new(pos: [f32; 3], colour: [f32; 3], intensity: f32) -> Light {
|
||||
let c: Vector3<f32> = colour.into();
|
||||
Light {
|
||||
pos: pos.into(),
|
||||
colour: c * intensity,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user