From 0146c7b7f02ef5d74116546489aee85383bb4969 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Fri, 18 Mar 2016 12:15:53 +0100 Subject: [PATCH] isolate some GL stuff --- src/graphics/buffer.hpp | 60 +++++++++++++++++++ src/graphics/gl_traits.cpp | 30 ++++++++++ src/graphics/gl_traits.hpp | 119 +++++++++++++++++++++++++++++++++++++ src/graphics/vao.hpp | 86 +++++++++++++++++++++++++++ src/world/Cursor.hpp | 18 ++---- src/world/world.cpp | 83 ++++++++++++-------------- 6 files changed, 338 insertions(+), 58 deletions(-) create mode 100644 src/graphics/buffer.hpp create mode 100644 src/graphics/gl_traits.cpp create mode 100644 src/graphics/gl_traits.hpp create mode 100644 src/graphics/vao.hpp diff --git a/src/graphics/buffer.hpp b/src/graphics/buffer.hpp new file mode 100644 index 0000000..288cdc8 --- /dev/null +++ b/src/graphics/buffer.hpp @@ -0,0 +1,60 @@ +#ifndef TACOS_GRAPHICS_BUFFER_HPP_ +#define TACOS_GRAPHICS_BUFFER_HPP_ + +#include "../app/error.hpp" + +#include +#include + + +namespace tacos { + +template +class MappedBuffer { + +public: + MappedBuffer(GLenum target, GLenum access) + : buf(reinterpret_cast(glMapBuffer(target, access))) + , target(target) { + if (!buf) { + throw GLError("failed to map buffer"); + } + } + MappedBuffer() + : buf(nullptr) + , target(0) { + } + ~MappedBuffer() noexcept { + if (buf) { + glUnmapBuffer(target); + } + } + + MappedBuffer(MappedBuffer &&other) noexcept + : buf(other.buf) + , target(other.target) { + other.buf = nullptr; + } + MappedBuffer &operator =(MappedBuffer &&other) noexcept { + std::swap(buf, other.buf); + std::swap(target, other.target); + } + + MappedBuffer(const MappedBuffer &) = delete; + MappedBuffer &operator =(const MappedBuffer &) = delete; + + explicit operator bool() const noexcept { return buf; } + +public: + T &operator [](std::size_t i) noexcept { return buf[i]; } + const T &operator [](std::size_t i) const noexcept { return buf[i]; } + +private: + T *buf; + GLenum target; + +}; + +} + +#endif diff --git a/src/graphics/gl_traits.cpp b/src/graphics/gl_traits.cpp new file mode 100644 index 0000000..1dd35e7 --- /dev/null +++ b/src/graphics/gl_traits.cpp @@ -0,0 +1,30 @@ +#include "gl_traits.hpp" + + +namespace tacos { + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +} diff --git a/src/graphics/gl_traits.hpp b/src/graphics/gl_traits.hpp new file mode 100644 index 0000000..43caf03 --- /dev/null +++ b/src/graphics/gl_traits.hpp @@ -0,0 +1,119 @@ +#ifndef TACOS_GRAPHICS_GL_TRAITS_HPP_ +#define TACOS_GRAPHICS_GL_TRAITS_HPP_ + +#include +#include + + +namespace tacos { + +template +struct gl_traits { + + /// number of components per generic attribute + /// must be 1, 2, 3, 4 + // static constexpr GLint size; + + /// component type + /// accepted values are: + /// GL_BYTE, GL_UNSIGNED_BYTE, + /// GL_SHORT, GL_UNSIGNED_SHORT, + /// GL_INT, GL_UNSIGNED_INT, + /// GL_HALF_FLOAT, GL_FLOAT, GL_DOUBLE, + /// GL_FIXED, GL_INT_2_10_10_10_REV, GL_UNSIGNED_INT_2_10_10_10_REV + // static constexpr GLenum type; + +}; + + +// basic types + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_BYTE; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_UNSIGNED_BYTE; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_SHORT; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_UNSIGNED_SHORT; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_INT; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_UNSIGNED_INT; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_FLOAT; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_DOUBLE; +}; + +// composite types + +template<> +template +struct gl_traits> { + static constexpr GLint size = 1; + static constexpr GLenum type = gl_traits::type; +}; +template +constexpr GLint gl_traits>::size; +template +constexpr GLenum gl_traits>::type; + +template<> +template +struct gl_traits> { + static constexpr GLint size = 2; + static constexpr GLenum type = gl_traits::type; +}; +template +constexpr GLint gl_traits>::size; +template +constexpr GLenum gl_traits>::type; + +template<> +template +struct gl_traits> { + static constexpr GLint size = 3; + static constexpr GLenum type = gl_traits::type; +}; +template +constexpr GLint gl_traits>::size; +template +constexpr GLenum gl_traits>::type; + +template<> +template +struct gl_traits> { + static constexpr GLint size = 4; + static constexpr GLenum type = gl_traits::type; +}; +template +constexpr GLint gl_traits>::size; +template +constexpr GLenum gl_traits>::type; + +} + +#endif diff --git a/src/graphics/vao.hpp b/src/graphics/vao.hpp new file mode 100644 index 0000000..debed55 --- /dev/null +++ b/src/graphics/vao.hpp @@ -0,0 +1,86 @@ +#ifndef TACOS_GRAPHICS_VAO_HPP_ +#define TACOS_GRAPHICS_VAO_HPP_ + +#include "buffer.hpp" +#include "gl_traits.hpp" + +#include + + +namespace tacos { + +/// Simple vertex array object based on indexed draw calls with attributes +/// interleaved in a single buffer. +template +class SimpleVAO { + +public: + SimpleVAO() + : vao(0) + , buffers{0} { + glGenVertexArrays(1, &vao); + glGenBuffers(2, buffers); + } + ~SimpleVAO() noexcept { + glDeleteBuffers(2, buffers); + glDeleteVertexArrays(1, &vao); + } + + SimpleVAO(const SimpleVAO &) = delete; + SimpleVAO &operator =(const SimpleVAO &) = delete; + +public: + void Bind() const noexcept { + glBindVertexArray(vao); + } + void Unbind() const noexcept { + glBindVertexArray(0); + } + void BindAttributes() const noexcept { + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + } + void BindElements() const noexcept { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + } + void EnableAttribute(GLuint index) noexcept { + glEnableVertexAttribArray(index); + } + void DisableAttribute(GLuint index) noexcept { + glDisableVertexAttribArray(index); + } + template + void AttributePointer(GLuint index, bool normalized, std::size_t offset) noexcept { + glVertexAttribPointer( + index, + gl_traits::size, + gl_traits::type, + normalized, + sizeof(Attributes), + reinterpret_cast(offset) + ); + } + void ReserveAttributes(std::size_t size, GLenum usage) noexcept { + glBufferData(GL_ARRAY_BUFFER, size * sizeof(Attributes), nullptr, usage); + } + MappedBuffer MapAttributes(GLenum access) { + return MappedBuffer(GL_ARRAY_BUFFER, access); + } + void ReserveElements(std::size_t size, GLenum usage) noexcept { + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size * sizeof(Element), nullptr, usage); + } + MappedBuffer MapElements(GLenum access) { + return MappedBuffer(GL_ELEMENT_ARRAY_BUFFER, access); + } + void DrawTriangles(std::size_t size, std::size_t offset = 0) const noexcept { + glDrawElements(GL_TRIANGLES, size, gl_traits::type, ((Element *) nullptr) + offset); + } + +private: + GLuint vao; + GLuint buffers[2]; + +}; + +} + +#endif diff --git a/src/world/Cursor.hpp b/src/world/Cursor.hpp index 828e411..7460319 100644 --- a/src/world/Cursor.hpp +++ b/src/world/Cursor.hpp @@ -1,8 +1,7 @@ #ifndef TACOS_WORLD_CURSOR_HPP_ #define TACOS_WORLD_CURSOR_HPP_ -#include -#include +#include "../graphics/vao.hpp" namespace tacos { @@ -13,10 +12,6 @@ class Cursor { public: Cursor(); - ~Cursor(); - - Cursor(const Cursor &) = delete; - Cursor &operator =(const Cursor &) = delete; /// hide cursor void Hide() noexcept; @@ -27,8 +22,11 @@ public: void Draw() const noexcept; private: - GLuint vao; - GLuint buffers[2]; + struct Attributes { + glm::vec3 position; + }; + + SimpleVAO vao; // side length in vertices (make sure it's between 2 and 8 inclusive) int size; @@ -40,10 +38,6 @@ private: FLOOR, } mode; - struct Attributes { - glm::vec3 position; - }; - }; } diff --git a/src/world/world.cpp b/src/world/world.cpp index b7037f0..03ff197 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -1,6 +1,8 @@ #include "Cursor.hpp" #include "Floor.hpp" +#include "../graphics/buffer.hpp" + #include #include @@ -8,25 +10,16 @@ namespace tacos { Cursor::Cursor() -: vao(0) -, buffers{0} +: vao() , size(3) , offset(0.1f) , mode(HIDDEN) { - glGenVertexArrays(1, &vao); - glGenBuffers(2, buffers); - - glBindVertexArray(vao); - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, 0, sizeof(Attributes), reinterpret_cast(offsetof(Attributes, position))); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glBindVertexArray(0); -} - -Cursor::~Cursor() { - glDeleteBuffers(2, buffers); - glDeleteVertexArrays(1, &vao); + vao.Bind(); + vao.BindAttributes(); + vao.EnableAttribute(0); + vao.AttributePointer(0, false, offsetof(Attributes, position)); + vao.BindElements(); + vao.Unbind(); } void Cursor::Hide() noexcept { @@ -42,37 +35,39 @@ void Cursor::FloorTile(const Floor &floor, int tile_x, int tile_z) { int z_begin = glm::clamp(tile_z, 0, floor.Depth() - size); int z_end = z_begin + size; - glBindVertexArray(vao); - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glBufferData(GL_ARRAY_BUFFER, size * size * sizeof(Attributes), nullptr, GL_DYNAMIC_DRAW); - Attributes *attrib = reinterpret_cast(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); - for (int z = z_begin, index = 0; z < z_end; ++z) { - for (int x = x_begin; x < x_end; ++x, ++index) { - attrib[index].position = glm::vec3(x, floor.GetElevation(x, z) + offset, z); + vao.Bind(); + vao.BindAttributes(); + vao.ReserveAttributes(size * size, GL_DYNAMIC_DRAW); + { + MappedBuffer attrib(vao.MapAttributes(GL_WRITE_ONLY)); + for (int z = z_begin, index = 0; z < z_end; ++z) { + for (int x = x_begin; x < x_end; ++x, ++index) { + attrib[index].position = glm::vec3(x, floor.GetElevation(x, z) + offset, z); + } } } - glUnmapBuffer(GL_ARRAY_BUFFER); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, (size - 1) * (size - 1) * 6, nullptr, GL_DYNAMIC_DRAW); - unsigned char *element = reinterpret_cast(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY)); - for (int z = 0, index = 0; z < size - 1; ++z) { - for (int x = 0; x < size - 1; ++x, ++index) { - element[index * 6 + 0] = (z + 0) * size + (x + 0); - element[index * 6 + 1] = (z + 0) * size + (x + 1); - element[index * 6 + 2] = (z + 1) * size + (x + 0); - element[index * 6 + 3] = (z + 0) * size + (x + 1); - element[index * 6 + 4] = (z + 1) * size + (x + 1); - element[index * 6 + 5] = (z + 1) * size + (x + 0); + + vao.BindElements(); + vao.ReserveElements((size - 1) * (size - 1) * 6, GL_DYNAMIC_DRAW); + { + MappedBuffer element(vao.MapElements(GL_WRITE_ONLY)); + for (int z = 0, index = 0; z < size - 1; ++z) { + for (int x = 0; x < size - 1; ++x, ++index) { + element[index * 6 + 0] = (z + 0) * size + (x + 0); + element[index * 6 + 1] = (z + 0) * size + (x + 1); + element[index * 6 + 2] = (z + 1) * size + (x + 0); + element[index * 6 + 3] = (z + 0) * size + (x + 1); + element[index * 6 + 4] = (z + 1) * size + (x + 1); + element[index * 6 + 5] = (z + 1) * size + (x + 0); + } } } - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); - glBindVertexArray(0); + vao.Unbind(); } void Cursor::Draw() const noexcept { - glBindVertexArray(vao); - glDrawElements(GL_TRIANGLES, (size - 1) * (size - 1) * 6, GL_UNSIGNED_BYTE, nullptr); + vao.Bind(); + vao.DrawTriangles((size - 1) * (size - 1) * 6); } @@ -145,9 +140,7 @@ void Floor::FillElementBuffer(GLuint which, int tile_width, int tile_depth) { glBindVertexArray(0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, which); glBufferData(GL_ELEMENT_ARRAY_BUFFER, tile_width * tile_depth * sizeof(short) * 6, nullptr, GL_STATIC_DRAW); - // TODO: this can return null on error (out of memory in this case) - // might be worth checking eventually - short *data = reinterpret_cast(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY)); + MappedBuffer data(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); for (int z = 0, i = 0; z < tile_depth; ++z) { for (int x = 0; x < tile_width; ++x, ++i) { data[i * 6 + 0] = (z + 0) * (tile_width + 1) + (x + 0); @@ -158,12 +151,11 @@ void Floor::FillElementBuffer(GLuint which, int tile_width, int tile_depth) { data[i * 6 + 5] = (z + 1) * (tile_width + 1) + (x + 0); } } - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); } void Floor::FillAttribBuffer(int vao_x, int vao_z) { glBindBuffer(GL_ARRAY_BUFFER, buffers[vao_z * vao_width + vao_x]); - Attributes *data = reinterpret_cast(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); + MappedBuffer data(GL_ARRAY_BUFFER, GL_WRITE_ONLY); glm::ivec2 tiles(Tiles(vao_x, vao_z)); for (int z = 0, abs_z = vao_z * VAO_DIVISOR, i = 0; z < tiles.y + 1; ++z, ++abs_z) { for (int x = 0, abs_x = vao_x * VAO_DIVISOR; x < tiles.x + 1; ++x, ++abs_x, ++i) { @@ -171,7 +163,6 @@ void Floor::FillAttribBuffer(int vao_x, int vao_z) { data[i].normal = GetNormal(abs_x, abs_z); } } - glUnmapBuffer(GL_ARRAY_BUFFER); } glm::vec3 Floor::GetNormal(int x, int z) const noexcept { -- 2.39.2