vertex buffer object class for adding attribute data to gpu

This commit is contained in:
frank 2021-10-17 03:27:53 -04:00
parent 25e2a7ad4f
commit cdf4b8ce0e
4 changed files with 191 additions and 85 deletions

View File

@ -47,7 +47,7 @@
"enabled": true,
"json-save": true,
"json-save-directory": "local/scans",
"barcode": "045496591922",
"barcode": "037600110754",
"capture-device": "/dev/video0"
},
"api":

2
lib/sb

@ -1 +1 @@
Subproject commit 3baaa7624e42880a47d55b2e659038019131ee02
Subproject commit 3e5e0fcbb8aedd5c4ce15aa850e88511af17ad5e

View File

@ -92,30 +92,30 @@ void Pudding::set_pudding_model(
/* triangle that includes top two vertices and first base vertex */
start_vertex = &layer_top_ring[ii];
end_vertex = &layer_top_ring[(ii + 1) % layer_top_ring.size()];
pudding_attributes["vertices"].add(start_vertex->x, layer_top_y, start_vertex->y);
pudding_attributes["position"].add(start_vertex->x, layer_top_y, start_vertex->y);
pudding_attributes["uv"].add(ring_start_vertex_u, layer_top_percent);
pudding_attributes["vertices"].add(end_vertex->x, layer_top_y, end_vertex->y);
pudding_attributes["position"].add(end_vertex->x, layer_top_y, end_vertex->y);
pudding_attributes["uv"].add(ring_start_vertex_u + u_step, layer_top_percent);
pudding_attributes["color"].extend(*layer_top_color, 2);
pudding_attributes["vertices"].add(layer_base_ring[ii].x, layer_base_y, layer_base_ring[ii].y);
pudding_attributes["position"].add(layer_base_ring[ii].x, layer_base_y, layer_base_ring[ii].y);
pudding_attributes["uv"].add(ring_start_vertex_u, layer_base_percent);
pudding_attributes["color"].add(*layer_bottom_color);
/* triangle that includes bottom two vertices and second top vertex */
start_vertex = &layer_base_ring[ii];
pudding_attributes["vertices"].add(start_vertex->x, layer_base_y, start_vertex->y);
pudding_attributes["position"].add(start_vertex->x, layer_base_y, start_vertex->y);
pudding_attributes["uv"].add(ring_start_vertex_u, layer_base_percent);
pudding_attributes["colors"].add(*layer_bottom_color);
pudding_attributes["vertices"].add(end_vertex->x, layer_top_y, end_vertex->y);
pudding_attributes["color"].add(*layer_bottom_color);
pudding_attributes["position"].add(end_vertex->x, layer_top_y, end_vertex->y);
pudding_attributes["uv"].add(ring_start_vertex_u + u_step, layer_top_percent);
pudding_attributes["color"].add(*layer_top_color);
end_vertex = &layer_base_ring[(ii + 1) % layer_base_ring.size()];
pudding_attributes["vertices"].add(end_vertex->x, layer_base_y, end_vertex->y);
pudding_attributes["position"].add(end_vertex->x, layer_base_y, end_vertex->y);
pudding_attributes["uv"].add(ring_start_vertex_u + u_step, layer_base_percent);
pudding_attributes["color"].add(*layer_bottom_color);
ring_start_vertex_u += u_step;
}
}
pudding_triangle_vertex_count = pudding_attributes["vertices"].count();
pudding_triangle_vertex_count = pudding_attributes["position"].count();
/* process the top and bottom of pudding, filling each face with a triangle fan */
float y = max_y;
const glm::vec3* face_color = &PUDDING_BROWN;
@ -123,7 +123,7 @@ void Pudding::set_pudding_model(
for (float radius : {top_radius, base_radius})
{
/* first point in a GL_TRIANGLE_FAN is the center */
pudding_attributes["vertices"].add(0.0f, y, 0.0f);
pudding_attributes["position"].add(0.0f, y, 0.0f);
pudding_attributes["uv"].add(0.0f, 0.0f);
layer_top_ring.clear();
sb::points_on_circle(layer_top_ring, ring_vertex_count, radius);
@ -132,22 +132,25 @@ void Pudding::set_pudding_model(
{
start_vertex = &layer_top_ring[ii];
/* for GL_TRIANGLE_FAN we just need to add an outer vertex */
pudding_attributes["vertices"].add(start_vertex->x, y, start_vertex->y);
pudding_attributes["position"].add(start_vertex->x, y, start_vertex->y);
pudding_attributes["uv"].add(*start_vertex);
/* connect the ring on the last vertex */
if (ii == layer_top_ring.size() - 1)
{
end_vertex = &layer_top_ring[(ii + 1) % layer_top_ring.size()];
pudding_attributes["vertices"].add(end_vertex->x, y, end_vertex->y);
pudding_attributes["position"].add(end_vertex->x, y, end_vertex->y);
pudding_attributes["uv"].add(*end_vertex);
}
}
/* single color for the entire layer_top_ring */
pudding_attributes["colors"].extend(*face_color, layer_top_ring.size() + 2);
pudding_attributes["color"].extend(*face_color, layer_top_ring.size() + 2);
y = min_y;
face_color = &PUDDING_YELLOW;
}
pudding_fan_vertex_count = (pudding_attributes["vertices"].count() - pudding_triangle_vertex_count) / 2;
pudding_fan_vertex_count = (pudding_attributes["position"].count() - pudding_triangle_vertex_count) / 2;
pudding_attributes["position"].index(0);
pudding_attributes["color"].index(1);
pudding_attributes["uv"].index(2);
}
/* Create GL context via super class and load vertices, UV data, and shaders */
@ -184,7 +187,6 @@ void Pudding::load_gl_context()
* will be copied in one after the other, offset to after the previous data location. The same buffer offset will
* be passed to the vertex attributes for each data. */
vbo.generate();
vbo.target(GL_ARRAY_BUFFER);
vbo.bind();
/* Load two shader programs, one for rendering the flat objects, and one for rendering the 3D model. Load and configure
* the flat shader program first. */
@ -193,46 +195,27 @@ void Pudding::load_gl_context()
flat_program = glCreateProgram();
glAttachShader(flat_program, vertex_shader);
glAttachShader(flat_program, fragment_shader);
glBindAttribLocation(flat_program, rectangle_attributes["position"].index(), "in_position");
glBindAttribLocation(flat_program, rectangle_attributes["uv"].index(), "vertex_uv");
glBindAttribLocation(flat_program, rectangle_attributes["position"], "in_position");
glBindAttribLocation(flat_program, rectangle_attributes["uv"], "vertex_uv");
/* load, configure and link the 3D world program */
vertex_shader = load_shader("src/mvp.vert", GL_VERTEX_SHADER);
fragment_shader = load_shader("src/mvp.frag", GL_FRAGMENT_SHADER);
mvp_program = glCreateProgram();
glAttachShader(mvp_program, vertex_shader);
glAttachShader(mvp_program, fragment_shader);
glBindAttribLocation(mvp_program, 2, "in_position");
glBindAttribLocation(mvp_program, 3, "in_color");
glBindAttribLocation(mvp_program, 4, "vertex_uv");
glBindAttribLocation(mvp_program, pudding_attributes["position"], "in_position");
glBindAttribLocation(mvp_program, pudding_attributes["color"], "in_color");
glBindAttribLocation(mvp_program, pudding_attributes["uv"], "vertex_uv");
sb::Log::gl_errors("after loading shaders");
/* allocate space for vertices, UV and colors, and copy rectangle vertices in at initialization */
GLsizeiptr vbo_size = rectangle_attributes["position"].size() + rectangle_attributes["uv"].size() + pudding_attributes["uv"].size() +
pudding_attributes["vertices"].size() + pudding_attributes["colors"].size();
glBufferData(GL_ARRAY_BUFFER, vbo_size, rectangle_attributes["position"], GL_STATIC_DRAW);
/* specify the rectangle vertex attributes as consecutive 2D float coords */
glVertexAttribPointer(rectangle_attributes["position"].index(), rectangle_attributes["position"].dimensions(),
rectangle_attributes["position"].type(), GL_FALSE, 0, nullptr);
/* copy rectangle UV data into the VBO, offset to after the vertex data, set up attributes */
GLintptr offset = rectangle_attributes["position"].size();
glBufferSubData(GL_ARRAY_BUFFER, offset, rectangle_attributes["uv"].size(), rectangle_attributes["uv"]);
glVertexAttribPointer(rectangle_attributes["uv"].index(), rectangle_attributes["uv"].dimensions(),
rectangle_attributes["uv"].type(), GL_FALSE, 0, reinterpret_cast<GLvoid*>(offset));
/* copy pudding vertices into VBO, offset to after the rectangle UV, and set up vertex attributes for 3D */
offset += rectangle_attributes["uv"].size();
glBufferSubData(GL_ARRAY_BUFFER, offset, pudding_attributes["vertices"].size(), pudding_attributes["vertices"]);
glVertexAttribPointer(2, pudding_attributes["vertices"].dimensions(), pudding_attributes["vertices"].type(),
GL_FALSE, 0, reinterpret_cast<GLvoid*>(offset));
/* copy pudding color values into VBO, offset to after pudding vertices and set as 3D */
offset += pudding_attributes["vertices"].size();
glBufferSubData(GL_ARRAY_BUFFER, offset, pudding_attributes["colors"].size(), pudding_attributes["colors"]);
glVertexAttribPointer(3, pudding_attributes["colors"].dimensions(), pudding_attributes["colors"].type(),
GL_FALSE, 0, reinterpret_cast<GLvoid*>(offset));
/* copy pudding UV values into VBO, offset to after pudding color vertices and set as 2D */
offset += pudding_attributes["colors"].size();
glBufferSubData(GL_ARRAY_BUFFER, offset, pudding_attributes["uv"].size(), pudding_attributes["uv"]);
glVertexAttribPointer(4, pudding_attributes["uv"].dimensions(), pudding_attributes["uv"].type(),
GL_FALSE, 0, reinterpret_cast<GLvoid*>(offset));
sb::Log::gl_errors("after loading attributes");
/* Fill VBO with attribute data */
vbo.allocate(rectangle_attributes["position"].size() + rectangle_attributes["uv"].size() + pudding_attributes["uv"].size() +
pudding_attributes["position"].size() + pudding_attributes["color"].size(), GL_STATIC_DRAW);
vbo.add(rectangle_attributes["position"]);
vbo.add(rectangle_attributes["uv"]);
vbo.add(pudding_attributes["uv"]);
vbo.add(pudding_attributes["position"]);
vbo.add(pudding_attributes["color"]);
sb::Log::gl_errors("after filling VBO");
/* link shaders */
link_shader(flat_program);
link_shader(mvp_program);
@ -762,10 +745,11 @@ void Pudding::update()
glUseProgram(flat_program);
glUniform1f(flat_time_uniform_location, time_seconds);
/* disable pudding attributes and enable rectangle attributes */
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glEnableVertexAttribArray(rectangle_attributes["position"].index());
glEnableVertexAttribArray(rectangle_attributes["uv"].index());
glDisableVertexAttribArray(pudding_attributes["position"]);
glDisableVertexAttribArray(pudding_attributes["color"]);
glDisableVertexAttribArray(pudding_attributes["uv"]);
glEnableVertexAttribArray(rectangle_attributes["position"]);
glEnableVertexAttribArray(rectangle_attributes["uv"]);
glUniform1i(flat_texture_uniform_location, 0);
glActiveTexture(GL_TEXTURE0);
tiles[current_tile_index].bind();
@ -787,19 +771,19 @@ void Pudding::update()
/* pass the mvp matrix to the shader */
glUniformMatrix4fv(mvp_uniform_location, 1, GL_FALSE, &mvp[0][0]);
/* disable rectangle attributes and enable pudding attributes */
glDisableVertexAttribArray(rectangle_attributes["position"].index());
glDisableVertexAttribArray(rectangle_attributes["uv"].index());
glEnableVertexAttribArray(2);
glDisableVertexAttribArray(rectangle_attributes["position"]);
glDisableVertexAttribArray(rectangle_attributes["color"]);
glEnableVertexAttribArray(pudding_attributes["position"]);
if (items.size() == 0)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(pudding_attributes["color"]);
}
else
{
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(pudding_attributes["color"]);
glEnableVertexAttribArray(pudding_attributes["uv"]);
GLuint pudding_texture_location = glGetUniformLocation(mvp_program, "pudding_texture");
glUniform1i(pudding_texture_location, 0);
glActiveTexture(GL_TEXTURE0);
@ -825,8 +809,9 @@ void Pudding::update()
/* switch to flat shader for item and camera */
glUseProgram(flat_program);
/* disable pudding attributes and enable rectangle attributes */
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glDisableVertexAttribArray(pudding_attributes["position"]);
glDisableVertexAttribArray(pudding_attributes["color"]);
glDisableVertexAttribArray(pudding_attributes["uv"]);
glEnableVertexAttribArray(rectangle_attributes["position"].index());
glEnableVertexAttribArray(rectangle_attributes["uv"].index());
glDisable(GL_DEPTH_TEST);
@ -909,32 +894,46 @@ void vao_deleter(GLuint* id)
glDeleteVertexArrays(1, id);
}
Buffer::Buffer() : GLObject(buffer_deleter) {}
Buffer::Buffer() : Buffer(GL_INVALID_ENUM) {}
/* Forward the GL generate function to the base class */
Buffer::Buffer(GLenum target) : GLObject(buffer_deleter), buffer_target(target) {}
/* Forward the GL generate function to the base class. */
void Buffer::generate()
{
GLObject::generate(glGenBuffers);
}
/* Set the type of data being held in this buffer */
/* Set the target for this buffer. */
void Buffer::target(GLenum target)
{
buffer_target = target;
}
/* Return the type of data being held in this buffer as a GLenum */
/* Return the GL enum for target */
GLenum Buffer::target() const
{
return buffer_target;
}
/* Bind this Buffer to the current GL context */
/* Bind this Buffer to the current GL context. An exception will be thrown if the target has not
* been set. */
void Buffer::bind() const
{
if (target() == GL_INVALID_ENUM)
{
throw std::invalid_argument("target must be set before binding buffer");
}
glBindBuffer(target(), id());
}
/* Set the target and bind the buffer. */
void Buffer::bind(GLenum target)
{
this->target(target);
bind();
}
/* This function gets passed to the abstract base class for deleting the Buffer data when the ID
* pointer goes out of scope (when all instances of this Buffer and its copies are out of scope) */
void buffer_deleter(GLuint* id)
@ -943,3 +942,54 @@ void buffer_deleter(GLuint* id)
SDL_Log("destroying buffer ID %i", *id);
glDeleteBuffers(1, id);
}
/* Initialize a Vertex Buffer Object. The base class will be initalized with a target of
* GL_ARRAY_BUFFER. */
VBO::VBO() : Buffer(GL_ARRAY_BUFFER) {}
/* Allocate size bytes of vertex attribute memory on the GPU. Usage should be one of GL_STREAM_DRAW,
* GL_STREAM_READ, GL_STREAM_COPY, GL_STATIC_DRAW, GL_STATIC_READ, GL_STATIC_COPY, GL_DYNAMIC_DRAW,
* GL_DYNAMIC_READ, or GL_DYNAMIC_COPY. The memory will be uninitialized. */
void VBO::allocate(GLsizeiptr size, GLenum usage)
{
glBufferData(target(), size, nullptr, usage);
/* Debug */
std::ostringstream message;
message << "After allocating memory for " << *this;
sb::Log::gl_errors(message.str());
}
/* Set memory in the GPU buffer to the values of the attribute data using glBufferSubData. The memory
* set is a contiguous area from the object's current byte offset value to the offset plus the size in
* bytes of the attributes. After the memory is set, the offset is set to the end of the area.
*
* Define an array of vertex attribute data on the GPU by passing the attributes's index, dimensions,
* normalization state, and type to glVertexAttribPointer, along with the buffer object's current
* offset. */
void VBO::add(sb::Attributes& attributes)
{
/* Set memory */
glBufferSubData(target(), offset, attributes.size(), attributes);
/* Debug */
std::ostringstream initialization_message;
initialization_message << "After setting " << attributes.size() << "bytes in " << *this;
sb::Log::gl_errors(initialization_message.str());
/* Define array */
glVertexAttribPointer(
attributes.index(), attributes.dimensions(), attributes.type(), attributes.normalized(), 0, reinterpret_cast<GLvoid*>(offset));
/* Debug */
std::ostringstream pointer_message;
pointer_message << "After defining attribute pointer for <Attributes (" << attributes.dimensions() << "D, id: " <<
attributes.index() << ", size:" << attributes.size() << ")>";
sb::Log::gl_errors(pointer_message.str());
/* Increase offset */
offset += attributes.size();
}
/* Overload the stream operator to support adding the attributes in a VBO. Add each attributes object to
* the output stream. */
std::ostream& operator<<(std::ostream& out, const VBO& vbo)
{
out << "<Vertex Buffer Object (id: " << vbo.id() << ")>";
return out;
}

View File

@ -16,6 +16,8 @@
#include <algorithm>
#include <thread>
#include <memory>
#include <stdexcept>
#include <functional>
#include <curl/curl.h>
#include "SDL.h"
#include "SDL_image.h"
@ -62,32 +64,45 @@ class Buffer : public GLObject
private:
GLenum buffer_target, buffer_usage;
std::uint32_t buffer_add_use_count = 0, buffer_calculated_size = 0;
GLenum buffer_target = GL_INVALID_ENUM;
protected:
GLenum target() const;
public:
Buffer();
Buffer(GLenum, GLenum);
void generate();
Buffer(GLenum);
void target(GLenum);
GLenum target() const;
void generate();
void bind() const;
void allocate(GLsizeiptr);
void allocate(GLenum, GLenum, GLsizeiptr);
template<typename T>
sb::Attributes add(VAO& vao, std::vector<T> vertex_attributes)
{
vao.increment();
// glVertexAttribPointer(vao.counted(),
}
void bind(GLenum);
};
void buffer_deleter(GLuint*);
class VBO;
std::ostream& operator<<(std::ostream&, const VBO&);
class VBO : public Buffer
{
private:
GLintptr offset = 0;
public:
VBO();
void allocate(GLsizeiptr, GLenum);
void add(sb::Attributes&);
friend std::ostream& operator<<(std::ostream&, const VBO&);
};
class Pudding : public Game
{
@ -134,7 +149,7 @@ private:
flat_time_uniform_location, scroll_uniform_location;
glm::mat4 projection, model = glm::mat4(1.0f), mvp;
std::map<std::string, sb::Attributes> rectangle_attributes, pudding_attributes = {
{"vertices", sb::Attributes()},
{"position", sb::Attributes()},
{"uv", sb::Attributes()},
{"color", sb::Attributes()}
};
@ -144,7 +159,7 @@ private:
Texture capture_texture_front_buffer, capture_texture_back_buffer;
Texture& capture_texture = capture_texture_front_buffer;
VAO vao;
Buffer vbo;
VBO vbo, other_vbo;
void set_pudding_model(float, float, int, int = 1, float = -1, float = 1, float = 0.3f);
void load_gl_context();
@ -183,12 +198,53 @@ class Plane : public Model
{
};
class Billboard : public Plane
/* Apply force until reaching a threshold. Use a connection object to run user functions
* when force reaches threshold and when force goes below threshold. */
class Button
{
/* threshold */
/* force */
/* close */
/* open */
/* apply */
/* remove */
/* weighted depression rate */
/* Animation (?) */
};
class Pad
{
};
class Button : public Billboard
/* An abstract base class of an object in a state of either connected or not connected.
* When the state changes, functions that must be overriden by a derived class are run,
* one for each state change: from on to off, and from off to on. */
class Connection
{
private:
enum State
{
STATE_DISCONNECTED,
STATE_CONNECTED
};
State connection_state = STATE_DISCONNECTED;
protected:
virtual void on_connect() = 0;
virtual void on_disconnect() = 0;
public:
Connection() {}
State state() { return connection_state; }
void connect() { if (state() != STATE_CONNECTED) { connection_state = STATE_CONNECTED; on_connect(); } }
void disconnect() { if (state() != STATE_DISCONNECTED) { connection_state = STATE_DISCONNECTED; on_disconnect(); } }
void toggle() { if (state() == STATE_CONNECTED) disconnect(); else connect(); }
bool connected() { return connection_state == STATE_DISCONNECTED ? false : true; }
};
#endif