Last active
August 29, 2015 14:05
-
-
Save apples/03125c06809c82dc46e2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ULTIMATE VERTEX ARRAY OBJECT EXAMPLE | |
// No guarantee of correctness. | |
// Warranty void upon successful compile. | |
// No warranty. | |
// This software is released into the public domain. | |
#include <GL/glew.h> // or whatever loader you want. | |
#include <glm/glm.hpp> // or whatever math library you want. | |
#include <cstddef> | |
#include <vector> | |
// Resource handle for a VBO. No logic. | |
class VBO { | |
GLuint handle; | |
public: | |
VBO() { | |
glGenBuffers(1, &handle); | |
// We should probably throw an exception if glGenBuffers() fails. | |
} | |
// VBOs should not be copied. | |
VBO(VBO const&) = delete; | |
VBO& operator=(VBO const&) = delete; | |
// VBOs can easily be moved. | |
VBO(VBO&& other) : handle(other.handle) { | |
other.handle = 0; | |
} | |
VBO& operator=(VBO&& other) { | |
this->~VBO(); | |
new (this) VBO(std::move(other)); | |
return *this; | |
} | |
~VBO() { | |
glDeleteBuffers(1, &handle); | |
} | |
GLuint get() const { | |
return handle; | |
} | |
}; | |
// Resource handle for a VAO. No logic. Very similar to VBO. | |
class VAO { | |
GLuint handle; | |
public: | |
VAO() { | |
glGenVertexArrays(1, &handle); | |
// This is another good place for an exception. | |
} | |
// VAOs should not be copied. | |
VAO(VAO const&) = delete; | |
VAO& operator=(VAO const&) = delete; | |
// VAOs can easily be moved. | |
VAO(VAO&& other) : handle(other.handle) { | |
other.handle = 0; | |
} | |
VAO& operator=(VAO&& other) { | |
this->~VAO(); | |
new (this) VAO(std::move(other)); | |
return *this; | |
} | |
~VAO() { | |
glDeleteVertexArrays(1, &handle); | |
} | |
GLuint get() const { | |
return handle; | |
} | |
}; | |
// A simple 3D vertex. | |
struct Vertex { | |
glm::vec3 position; | |
glm::vec3 normal; | |
glm::vec2 texcoord; | |
}; | |
// A triangle is a set of 3 indices into a Vertex array. | |
struct Triangle { | |
GLuint verts[3]; | |
}; | |
// A few basic assertions to ensure proper alignment. | |
static_assert(sizeof(Vertex) == sizeof(GLfloat)*8, "Vertex misaligned!"); | |
static_assert(sizeof(Triangle) == sizeof(GLuint)*3, "Triangle misaligned!"); | |
// A simple non-animated model. | |
struct Model { | |
std::vector<Vertex> vertices; | |
std::vector<Triangle> triangles; | |
}; | |
// A container for a VAO with an element count. | |
struct TriangleVAO { | |
VAO vao; | |
GLsizei count = 0; | |
}; | |
// Let's upload a model onto the GPU. | |
TriangleVAO upload_model(Model const& model) { | |
TriangleVAO rv; // This will be a handle to our Model after we send it to the GPU. | |
VBO vertex_vbo; // This VBO will hold the Vertex data. | |
VBO element_vbo; // This VBO will hold the Triangle data. | |
// Step 1: Upload the Vertex data. | |
// The Vertex data is not directly associated with any VAO, | |
// so we can upload it without having a VAO bound yet. | |
// We'll be using GL_STATIC_DRAW, since we won't need to modify the data | |
// after uploading. | |
glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo.get()); | |
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*model.vertices.size(), &model.vertices[0], GL_STATIC_DRAW); | |
// Step 2: Create the VAO. | |
// Once bound, the OpenGL state machine will be in a special mode. | |
// During this time, certain procedures will act upon the VAO itself, | |
// instead of affecting global state. | |
// Note: The ARRAY_BUFFER could have be bound and uploaded while the VAO | |
// is bound, but it would not be attached to the VAO. ARRAY_BUFFER is | |
// completely agnostic to VAOs. | |
glBindVertexArray(rv.vao.get()); | |
// We must enable a Vertex Attribute for each datum in a Vertex. | |
// These are only enabled for the currently bound VAO. | |
// Note: These do not have to be enabled until we want to draw the VAO, | |
// but since they stick with the VAO, we might as well go ahead and do so. | |
glEnableVertexAttribArray(0); // Position | |
glEnableVertexAttribArray(1); // Normal | |
glEnableVertexAttribArray(2); // Texture Coordinate | |
// Now that we've enabled the Attributes, we have to tell the GPU where | |
// to find the data for each Attribute. | |
// In our case, we're using an interleaved format, so we can use the | |
// same VBO for all Attributes. | |
// If a VBO is currently bound to ARRAY_BUFFER, then these pointers will | |
// be treated as offsets into that buffer. | |
// We've bound vertex_vbo to that buffer, so it will be used as the data | |
// source when we draw the VAO. | |
// A small helper, for readability. | |
auto setAttribute = [](GLuint index, GLint size, std::size_t offset) { | |
GLvoid* offset_v = static_cast<char*>(nullptr) + offset; | |
return glVertexAttribPointer(index, size, GL_FLOAT, GL_FALSE, sizeof(Vertex), offset_v); | |
}; | |
setAttribute(0, 3, offsetof(Vertex, position)); | |
setAttribute(1, 3, offsetof(Vertex, normal)); | |
setAttribute(2, 2, offsetof(Vertex, texcoord)); | |
// Step 3: Upload the Triangle data. | |
// Now, the only thing to do is upload the polygon data as an Element Array. | |
// The ELEMENT_ARRAY_BUFFER works exactly like a normal ARRAY_BUFFER, | |
// except it will be attached to the VAO instead of global state. | |
// The Element Array will be used as the indices into the Vertex VBO | |
// while drawing. | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_vbo.get()); | |
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Triangle)*model.triangles.size(), &model.triangles[0], GL_STATIC_DRAW); | |
// Don't forget to set the element count! | |
rv.count = model.triangles.size(); | |
// Done! Let's get out of here. | |
glBindVertexArray(0); | |
glBindBuffer(GL_ARRAY_BUFFER, 0); | |
return rv; | |
} | |
// Alright, time to draw it. This is going to be complicated; pay attention. | |
void draw_vao(TriangleVAO const& mesh) { | |
glBindVertexArray(mesh.vao.get()); | |
glDrawElements(GL_TRIANGLES, mesh.count, GL_UNSIGNED_INT, nullptr); | |
glBindVertexArray(0); | |
} | |
// Fin. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Yeah, this is much easier to understand than some of the stuff out there, and i'm assuming that this is more up to date than 90% of the crap I found. Just need to clean it up, #include proper headers, fix some bugs/typos, and it'll be ready to turn into a black box that does the magic I need.