Initial commit, quad render with proj camera

master
Rasmus Rosengren 3 years ago
commit d9bc0e36f5
Signed by: rsrp
GPG Key ID: A13BC7BC4F81CF5F
  1. 1
      .gitignore
  2. 1432
      Cargo.lock
  3. 12
      Cargo.toml
  4. 8
      assets/shaders/chunk.frag
  5. 10
      assets/shaders/chunk.vert
  6. 99
      src/camera.rs
  7. 138
      src/input.rs
  8. 159
      src/main.rs
  9. 134
      src/renderer/debug.rs
  10. 51
      src/renderer/mod.rs
  11. 141
      src/renderer/shader.rs
  12. 59
      src/renderer/texture.rs
  13. 48
      src/renderer/vertex_array.rs
  14. 34
      src/renderer/vertex_buffer.rs

1
.gitignore vendored

@ -0,0 +1 @@
/target

1432
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,12 @@
[package]
name = "blocks"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cgmath = "0.18"
gl = "0.14"
glutin = "0.27"
image = "0.23"

@ -0,0 +1,8 @@
#version 430
layout(location = 0) out vec4 out_color;
// uniform vec3 color;
void main() {
out_color = vec4(1.0, 0.0, 0.5, 1.0);
}

@ -0,0 +1,10 @@
#version 430
layout(location = 0) in vec2 position;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main() {
gl_Position = projection * view * model * vec4(position, 0.0, 1.0);
}

@ -0,0 +1,99 @@
use std::ops::Rem;
use cgmath::{perspective, point3, vec3, Angle, Deg, InnerSpace, Matrix4, Point3, Vector3};
#[derive(Clone, Copy, Debug)]
pub struct Camera {
pub position: Point3<f32>,
pub yaw: Deg<f32>,
pub pitch: Deg<f32>,
}
static WORLD_UP: Vector3<f32> = vec3(0.0, 1.0, 0.0);
impl Camera {
pub fn new() -> Self {
Self {
position: point3(0.0, 0.0, 0.0),
yaw: Deg(0.0),
pitch: Deg(0.0),
}
}
pub fn move_right(&mut self, amount: f32) {
let right_dir = self.right_dir();
self.position += amount * right_dir;
}
pub fn move_left(&mut self, amount: f32) {
self.move_right(-amount);
}
pub fn move_forward(&mut self, amount: f32) {
let forward_dir = self.horizontal_front_dir();
self.position += amount * forward_dir;
}
pub fn move_backward(&mut self, amount: f32) {
self.move_forward(-amount);
}
pub fn move_up(&mut self, amount: f32) {
self.position += amount * WORLD_UP;
}
pub fn move_down(&mut self, amount: f32) {
self.move_up(-amount);
}
pub fn rotate_yaw<D: Into<Deg<f32>>>(&mut self, amount: D) {
self.yaw -= amount.into();
self.yaw = Deg(self.yaw.0.rem(360.0));
}
pub fn rotate_pitch<D: Into<Deg<f32>>>(&mut self, amount: D) {
self.pitch += amount.into();
self.pitch = Deg(self.pitch.0.clamp(-89.9, 89.9));
}
pub fn view_matrix(&self) -> Matrix4<f32> {
let front_dir = self.front_dir();
let right_dir = self.right_dir();
let up_dir = front_dir.cross(right_dir);
Matrix4::look_to_rh(self.position, front_dir, WORLD_UP)
}
pub fn projection_matrix(&self) -> Matrix4<f32> {
perspective(Deg(45.0), 16.0 / 9.0, 0.0001, 1000.0)
}
fn horizontal_front_dir(&self) -> Vector3<f32> {
vec3(self.yaw.cos(), 0.0, -self.yaw.sin()).normalize()
}
fn front_dir(&self) -> Vector3<f32> {
println!(
"{:?}",
vec3(
self.yaw.cos() * self.pitch.cos(),
self.pitch.sin(),
-self.yaw.sin() * self.pitch.cos(),
)
);
vec3(
self.yaw.cos() * self.pitch.cos(),
self.pitch.sin(),
-self.yaw.sin() * self.pitch.cos(),
)
.normalize()
}
fn right_dir(&self) -> Vector3<f32> {
vec3(
(self.yaw - Deg(90.0)).cos(),
0.0,
-(self.yaw - Deg(90.0)).sin(),
)
.normalize()
}
}

@ -0,0 +1,138 @@
use std::collections::{HashMap, HashSet};
use glutin::event::{
ElementState, KeyboardInput, MouseButton, MouseScrollDelta, VirtualKeyCode, WindowEvent,
};
pub struct KeyboardState {
state: HashSet<VirtualKeyCode>,
momentary_state: HashMap<VirtualKeyCode, ElementState>,
}
impl KeyboardState {
pub fn new() -> Self {
Self {
state: HashSet::new(),
momentary_state: HashMap::new(),
}
}
pub fn is_pressed(&self, key: VirtualKeyCode) -> bool {
self.state.get(&key).is_some()
}
pub fn was_pressed(&self, key: VirtualKeyCode) -> bool {
self.momentary_state.get(&key) == Some(&ElementState::Pressed)
}
pub fn was_released(&self, key: VirtualKeyCode) -> bool {
self.momentary_state.get(&key) == Some(&ElementState::Released)
}
pub fn process_event(&mut self, event: &WindowEvent) {
match *event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state,
virtual_keycode: Some(virtual_keycode),
..
},
..
} => {
self.momentary_state.insert(virtual_keycode, state);
match state {
ElementState::Pressed => {
self.state.insert(virtual_keycode);
}
ElementState::Released => {
self.state.remove(&virtual_keycode);
}
}
}
_ => (),
}
}
pub fn clear_momentary_state(&mut self) {
self.momentary_state.clear();
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Point {
pub x: f64,
pub y: f64,
}
impl Point {
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
}
pub struct MouseState {
button_state: HashSet<MouseButton>,
momentary_button_state: HashMap<MouseButton, ElementState>,
pub position: Point,
pub mouse_delta: Point,
pub scroll_delta: f32,
}
impl MouseState {
pub fn new() -> Self {
Self {
button_state: HashSet::new(),
momentary_button_state: HashMap::new(),
position: Default::default(),
mouse_delta: Default::default(),
scroll_delta: 0.0,
}
}
pub fn is_pressed(&self, button: MouseButton) -> bool {
self.button_state.get(&button).is_some()
}
pub fn was_pressed(&self, button: MouseButton) -> bool {
self.momentary_button_state.get(&button) == Some(&ElementState::Pressed)
}
pub fn was_released(&self, button: MouseButton) -> bool {
self.momentary_button_state.get(&button) == Some(&ElementState::Released)
}
pub fn process_event(&mut self, event: &WindowEvent) {
match *event {
WindowEvent::CursorMoved { position, .. } => {
self.mouse_delta.x += position.x - self.position.x;
self.mouse_delta.y += position.y - self.position.y;
self.position = Point::new(position.x, position.y);
}
WindowEvent::MouseInput { button, state, .. } => {
self.momentary_button_state.insert(button, state);
match state {
ElementState::Pressed => {
self.button_state.insert(button);
}
ElementState::Released => {
self.button_state.remove(&button);
}
}
}
WindowEvent::MouseWheel { delta, .. } => match delta {
MouseScrollDelta::LineDelta(_, y) => {
self.scroll_delta += y;
}
_ => (),
},
_ => (),
}
}
pub fn clear_momentary_state(&mut self) {
self.momentary_button_state.clear();
self.mouse_delta = Default::default();
self.scroll_delta = 0.0;
}
}

@ -0,0 +1,159 @@
mod camera;
mod input;
mod renderer;
use std::{ffi::CString, time::Instant};
use cgmath::{Deg, Matrix4};
use gl::types::*;
use glutin::dpi::PhysicalSize;
use input::{KeyboardState, MouseState};
use renderer::{shader::Shader, vertex_buffer::VertexBuffer};
use crate::{
camera::Camera,
renderer::{debug::DebugCallback, vertex_array::VertexArray, VertexBufferElement},
};
// Vertex data
static QUAD_DATA: [GLfloat; 24] = [
0.5, 0.5, 1.0, 1.0, // top right
-0.5, 0.5, 0.0, 1.0, // top left
-0.5, -0.5, 0.0, 0.0, // bottom left
-0.5, -0.5, 0.0, 0.0, // bottom left
0.5, -0.5, 1.0, 0.0, // bottom right
0.5, 0.5, 1.0, 1.0, // top right
];
fn main() {
let event_loop = glutin::event_loop::EventLoop::new();
let window =
glutin::window::WindowBuilder::new().with_inner_size(PhysicalSize::new(1920.0, 1080.0));
let gl_window = glutin::ContextBuilder::new()
.build_windowed(window, &event_loop)
.unwrap();
// It is essential to make the context current before calling `gl::load_with`.
let gl_window = unsafe { gl_window.make_current() }.unwrap();
// Load the OpenGL function pointers
gl::load_with(|symbol| gl_window.get_proc_address(symbol));
let chunk_shader = Shader::from_file("chunk.vert", "chunk.frag");
unsafe {
gl::Enable(gl::BLEND);
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
}
let _debug_callback = unsafe {
DebugCallback::new(|message| {
println!("{:?}", message);
})
};
let quad_va = unsafe {
let vb = VertexBuffer::new(
&QUAD_DATA,
vec![
VertexBufferElement::floats(2),
VertexBufferElement::floats(2),
],
);
VertexArray::new(&[vb])
};
let mut camera = Camera::new();
camera.position.x = 10.0;
camera.position.y = 10.0;
let mut keyboard_state = KeyboardState::new();
let mut mouse_state = MouseState::new();
let mut show_fps = false;
let mut last_update_time = Instant::now();
event_loop.run(move |event, _, control_flow| {
use glutin::event::{Event, VirtualKeyCode, WindowEvent};
use glutin::event_loop::ControlFlow;
*control_flow = ControlFlow::Poll;
match event {
Event::LoopDestroyed => return,
Event::WindowEvent { event, .. } => {
keyboard_state.process_event(&event);
mouse_state.process_event(&event);
match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => unsafe {
gl::Viewport(0, 0, size.width as i32, size.height as i32);
},
_ => (),
}
}
_ => (),
}
let now = Instant::now();
let ms_since_last_update = (now - last_update_time).as_nanos() as f64 / 1_000_000.0;
if ms_since_last_update > 16.666 {
last_update_time = now;
println!("{:?}", camera);
if keyboard_state.was_pressed(VirtualKeyCode::F) {
show_fps = !show_fps;
}
let move_speed = 0.1;
if keyboard_state.is_pressed(VirtualKeyCode::W) {
camera.move_forward(move_speed);
} else if keyboard_state.is_pressed(VirtualKeyCode::S) {
camera.move_backward(move_speed);
}
if keyboard_state.is_pressed(VirtualKeyCode::D) {
camera.move_right(move_speed);
} else if keyboard_state.is_pressed(VirtualKeyCode::A) {
camera.move_left(move_speed);
}
if keyboard_state.is_pressed(VirtualKeyCode::Space) {
camera.move_up(move_speed);
} else if keyboard_state.is_pressed(VirtualKeyCode::LShift) {
camera.move_down(move_speed);
}
let mouse_sens = 0.4;
camera.rotate_yaw(Deg(mouse_sens * mouse_state.mouse_delta.x as f32));
camera.rotate_pitch(-Deg(mouse_sens * mouse_state.mouse_delta.y as f32));
let start = Instant::now();
unsafe {
gl::ClearColor(0.3, 0.3, 0.6, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
let model_matrix = Matrix4::from_scale(10.0);
chunk_shader.enable();
chunk_shader.set_mat4(&CString::new("model").unwrap(), model_matrix);
chunk_shader.set_mat4(&CString::new("view").unwrap(), camera.view_matrix());
chunk_shader.set_mat4(
&CString::new("projection").unwrap(),
camera.projection_matrix(),
);
quad_va.bind();
gl::DrawArrays(gl::TRIANGLES, 0, 6);
}
let dur = Instant::now() - start;
let ms = dur.as_nanos() as f64 / 1_000_000.0;
if show_fps {
println!("Render time: {}ms", ms);
}
gl_window.swap_buffers().unwrap();
keyboard_state.clear_momentary_state();
mouse_state.clear_momentary_state();
}
});
}

@ -0,0 +1,134 @@
use std::{
ffi::{c_void, CStr},
panic, ptr,
};
use gl::types::{GLchar, GLenum, GLsizei};
#[derive(Clone, Debug)]
pub struct DebugMessage {
id: GLenum,
source: DebugSource,
type_: DebugType,
severity: DebugSeverity,
message: String,
}
#[derive(Clone, Copy, Debug)]
pub enum DebugSource {
Api,
WindowSystem,
ShaderCompiler,
ThirdParty,
Application,
Other,
}
#[derive(Clone, Copy, Debug)]
pub enum DebugType {
Error,
DeprecatedBehavior,
UndefinedBehavior,
Portability,
Performance,
Marker,
Other,
PopGroup,
PushGroup,
}
#[derive(Clone, Copy, Debug)]
pub enum DebugSeverity {
Notification,
Low,
Medium,
High,
}
pub struct DebugCallback {
_user_callback: Box<Box<dyn Fn(DebugMessage)>>,
}
impl DebugCallback {
pub unsafe fn new<F>(user_callback: F) -> Self
where
F: Fn(DebugMessage) + 'static + Send + panic::RefUnwindSafe,
{
let user_callback = Box::new(Box::new(user_callback) as Box<_>);
gl::Enable(gl::DEBUG_OUTPUT);
gl::DebugMessageCallback(
Some(debug_callback),
&*user_callback as &Box<_> as *const Box<_> as *const c_void,
);
Self {
_user_callback: user_callback,
}
}
}
impl Drop for DebugCallback {
fn drop(&mut self) {
unsafe {
gl::DebugMessageCallback(None, ptr::null());
gl::Disable(gl::DEBUG_OUTPUT);
}
}
}
extern "system" fn debug_callback(
source: GLenum,
type_: GLenum,
id: GLenum,
severity: GLenum,
_length: GLsizei,
message: *const GLchar,
user_param: *mut c_void,
) {
let user_callback = user_param as *mut Box<dyn Fn()> as *const _;
let user_callback: &Box<dyn Fn(DebugMessage)> = unsafe { &*user_callback };
let msg_src = match source {
gl::DEBUG_SOURCE_API => DebugSource::Api,
gl::DEBUG_SOURCE_WINDOW_SYSTEM => DebugSource::WindowSystem,
gl::DEBUG_SOURCE_SHADER_COMPILER => DebugSource::ShaderCompiler,
gl::DEBUG_SOURCE_THIRD_PARTY => DebugSource::ThirdParty,
gl::DEBUG_SOURCE_APPLICATION => DebugSource::Application,
gl::DEBUG_SOURCE_OTHER => DebugSource::Other,
_ => unreachable!(),
};
let msg_type = match type_ {
gl::DEBUG_TYPE_ERROR => DebugType::Error,
gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR => DebugType::DeprecatedBehavior,
gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR => DebugType::UndefinedBehavior,
gl::DEBUG_TYPE_PORTABILITY => DebugType::Portability,
gl::DEBUG_TYPE_PERFORMANCE => DebugType::Performance,
gl::DEBUG_TYPE_MARKER => DebugType::Marker,
gl::DEBUG_TYPE_OTHER => DebugType::Other,
gl::DEBUG_TYPE_POP_GROUP => DebugType::PopGroup,
gl::DEBUG_TYPE_PUSH_GROUP => DebugType::PushGroup,
_ => unreachable!(),
};
let msg_severity = match severity {
gl::DEBUG_SEVERITY_NOTIFICATION => DebugSeverity::Notification,
gl::DEBUG_SEVERITY_LOW => DebugSeverity::Low,
gl::DEBUG_SEVERITY_MEDIUM => DebugSeverity::Medium,
gl::DEBUG_SEVERITY_HIGH => DebugSeverity::High,
_ => unreachable!(),
};
let msg = unsafe { String::from_utf8(CStr::from_ptr(message).to_bytes().to_vec()).unwrap() };
let message = DebugMessage {
id,
source: msg_src,
type_: msg_type,
severity: msg_severity,
message: msg,
};
user_callback(message);
}

@ -0,0 +1,51 @@
pub mod debug;
pub mod shader;
pub mod texture;
pub mod vertex_array;
pub mod vertex_buffer;
use std::mem;
use gl::types::GLfloat;
#[derive(Debug)]
pub enum GlType {
Float,
}
impl GlType {
pub fn to_raw_enum(&self) -> u32 {
match *self {
GlType::Float => gl::FLOAT,
}
}
pub fn mem_size(&self) -> u32 {
match *self {
GlType::Float => mem::size_of::<GLfloat>() as u32,
}
}
}
#[derive(Debug)]
pub struct VertexBufferElement {
size: u32,
type_: GlType,
}
impl VertexBufferElement {
pub fn floats(size: u32) -> Self {
Self {
size,
type_: GlType::Float,
}
}
pub fn to_raw_enum(&self) -> u32 {
self.type_.to_raw_enum()
}
pub fn mem_size(&self) -> u32 {
self.size * self.type_.mem_size()
}
}

@ -0,0 +1,141 @@
use cgmath::{Matrix, Matrix2, Matrix4, Vector3, Vector4};
use gl::types::*;
use std::{
ffi::{CStr, CString},
fs, ptr, str,
};
pub struct Shader {
id: GLuint,
}
impl Shader {
pub fn from_file(vs_path: &str, fs_path: &str) -> Self {
let vs_source = fs::read_to_string(format!("assets/shaders/{}", vs_path))
.expect(&format!("Could not read vertex shader {}", vs_path));
let fs_source = fs::read_to_string(format!("assets/shaders/{}", fs_path))
.expect(&format!("Could not read fragment shader {}", fs_path));
Self::from_source(&vs_source, &fs_source)
}
pub fn from_source(vs_source: &str, fs_source: &str) -> Self {
let vs_id = Self::compile_shader(vs_source, gl::VERTEX_SHADER);
let fs_id = Self::compile_shader(fs_source, gl::FRAGMENT_SHADER);
let program_id = Self::link_program(vs_id, fs_id);
unsafe {
gl::DeleteShader(vs_id);
gl::DeleteShader(fs_id);
}
Self { id: program_id }
}
pub fn enable(&self) {
unsafe {
gl::UseProgram(self.id);
}
}
pub fn set_mat2(&self, name: &CStr, matrix: Matrix2<f32>) {
unsafe {
let location = gl::GetUniformLocation(self.id, name.as_ptr());
gl::UniformMatrix2fv(location, 1, gl::FALSE, matrix.as_ptr());
}
}
pub fn set_mat4(&self, name: &CStr, matrix: Matrix4<f32>) {
unsafe {
let location = gl::GetUniformLocation(self.id, name.as_ptr());
gl::UniformMatrix4fv(location, 1, gl::FALSE, matrix.as_ptr());
}
}
pub fn set_vec3(&self, name: &CStr, vec: Vector3<f32>) {
unsafe {
let location = gl::GetUniformLocation(self.id, name.as_ptr());
gl::Uniform3f(location, vec.x, vec.y, vec.z);
}
}
pub fn set_vec4(&self, name: &CStr, vec: Vector4<f32>) {
unsafe {
let location = gl::GetUniformLocation(self.id, name.as_ptr());
gl::Uniform4f(location, vec.x, vec.y, vec.z, vec.w);
}
}
pub fn set_int(&self, name: &CStr, int: i32) {
unsafe {
let location = gl::GetUniformLocation(self.id, name.as_ptr());
gl::Uniform1i(location, int);
}
}
fn compile_shader(src: &str, ty: GLenum) -> GLuint {
let shader;
unsafe {
shader = gl::CreateShader(ty);
let c_str = CString::new(src.as_bytes()).unwrap();
gl::ShaderSource(shader, 1, &c_str.as_ptr(), ptr::null());
gl::CompileShader(shader);
let mut status = gl::FALSE as GLint;
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
if status != (gl::TRUE as GLint) {
let mut len = 0;
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
let mut buf = Vec::with_capacity(len as usize);
buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
gl::GetShaderInfoLog(
shader,
len,
ptr::null_mut(),
buf.as_mut_ptr() as *mut GLchar,
);
panic!(
"{}",
str::from_utf8(&buf)
.ok()
.expect("ShaderInfoLog not valid utf8")
);
}
}
shader
}
fn link_program(vs: GLuint, fs: GLuint) -> GLuint {
unsafe {
let program = gl::CreateProgram();
gl::AttachShader(program, vs);
gl::AttachShader(program, fs);
gl::LinkProgram(program);
// Get the link status
let mut status = gl::FALSE as GLint;
gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);
// Fail on error
if status != (gl::TRUE as GLint) {
let mut len: GLint = 0;
gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
let mut buf = Vec::with_capacity(len as usize);
buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
gl::GetProgramInfoLog(
program,
len,
ptr::null_mut(),
buf.as_mut_ptr() as *mut GLchar,
);
panic!(
"{}",
str::from_utf8(&buf)
.ok()
.expect("ProgramInfoLog not valid utf8")
);
}
program
}
}
}

@ -0,0 +1,59 @@
use std::{ffi::c_void, path::Path};
use image::{DynamicImage, GenericImageView};
#[derive(Debug)]
pub struct Texture {
pub id: u32,
pub width: u32,
pub height: u32,
}
impl Texture {
pub unsafe fn new(image: DynamicImage, flip_vertical: bool) -> Self {
let image = if flip_vertical { image.flipv() } else { image };
let width = image.width();
let height = image.width();
let (data, gl_format) = match image {
DynamicImage::ImageRgb8(image) => (image.into_raw(), gl::RGB),
DynamicImage::ImageRgba8(image) => (image.into_raw(), gl::RGBA),
_ => unimplemented!(),
};
let mut id = 0;
gl::CreateTextures(gl::TEXTURE_2D, 1, &mut id);
gl::TextureParameteri(id, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
gl::TextureParameteri(id, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
gl::TextureParameteri(id, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
gl::TextureParameteri(id, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
gl::TextureStorage2D(id, 1, gl::RGBA8, width as i32, height as i32);
gl::TextureSubImage2D(
id,
0,
0,
0,
width as i32,
height as i32,
gl_format,
gl::UNSIGNED_BYTE,
data.as_ptr() as *const c_void,
);
Self { id, width, height }
}
pub unsafe fn from_path<P>(path: P, flip_vertical: bool) -> Self
where
P: AsRef<Path>,
{
let image = image::open(path).expect("Couldn't open image");
Self::new(image, flip_vertical)
}
pub unsafe fn bind_to_unit(&self, unit: u32) {
gl::BindTextureUnit(unit, self.id);
}
}

@ -0,0 +1,48 @@
use super::vertex_buffer::VertexBuffer;
#[derive(Debug)]
pub struct VertexArray {
pub id: u32,
pub elements: u32,
}
impl VertexArray {
pub unsafe fn new(buffers: &[VertexBuffer]) -> Self {
let mut id = 0;
gl::CreateVertexArrays(1, &mut id);
let mut attrib_index = 0;
for (binding_index, buffer) in buffers.iter().enumerate() {
let stride = buffer
.layout
.iter()
.fold(0, |prev, element| prev + element.mem_size());
gl::VertexArrayVertexBuffer(id, binding_index as u32, buffer.id, 0, stride as i32);
let mut offset = 0;
for element in buffer.layout.iter() {
gl::EnableVertexArrayAttrib(id, attrib_index as u32);
gl::VertexArrayAttribFormat(
id,
attrib_index as u32,
element.size as i32,
element.to_raw_enum(),
gl::FALSE,
offset,
);
gl::VertexArrayAttribBinding(id, attrib_index as u32, binding_index as u32);
offset += element.mem_size();
attrib_index += 1;
}
}
let elements = buffers.iter().min_by_key(|b| b.elements).unwrap().elements;
Self { id, elements }
}
pub unsafe fn bind(&self) {
gl::BindVertexArray(self.id);
}
}

@ -0,0 +1,34 @@
use std::mem;
use gl::types::{GLfloat, GLsizeiptr, GLuint};
use super::VertexBufferElement;
#[derive(Debug)]
pub struct VertexBuffer {
pub id: GLuint,
pub elements: u32,
pub layout: Vec<VertexBufferElement>,
}
impl VertexBuffer {
pub unsafe fn new(data: &[GLfloat], layout: Vec<VertexBufferElement>) -> Self {
let mut id = 0;
gl::CreateBuffers(1, &mut id);
gl::NamedBufferStorage(
id,
(data.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
mem::transmute(&data[0]),
gl::DYNAMIC_STORAGE_BIT,
);
let elements =
data.len() as u32 / layout.iter().fold(0, |prev, element| prev + element.size);
Self {
id,
elements,
layout,
}
}
}
Loading…
Cancel
Save