From 905f72775fa91b0a467f3c0847c60cf0f85a6d80 Mon Sep 17 00:00:00 2001 From: Yann Herklotz Date: Tue, 4 Apr 2017 21:47:16 +0100 Subject: Sprite batching workin --- CMakeLists.txt | 2 + include/YAGE/camera2d.hpp | 10 ++- include/YAGE/inputmanager.hpp | 23 ++++++ include/YAGE/spritebatch.hpp | 67 +++++++++++++---- include/YAGE/window.hpp | 2 + src/camera2d.cpp | 6 ++ src/inputmanager.cpp | 24 ++++++ src/sprite.cpp | 6 ++ src/spritebatch.cpp | 165 ++++++++++++++++++++++++++++++++++-------- src/window.cpp | 13 +++- 10 files changed, 267 insertions(+), 51 deletions(-) create mode 100644 include/YAGE/inputmanager.hpp create mode 100644 src/inputmanager.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a7cd709..bbe8a259 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,10 +7,12 @@ set(YAGE_SOURCES ${PROJECT_SOURCE_DIR}/src/camera2d.cpp ${PROJECT_SOURCE_DIR}/src/glslprogram.cpp ${PROJECT_SOURCE_DIR}/src/imageloader.cpp + ${PROJECT_SOURCE_DIR}/src/inputmanager.cpp ${PROJECT_SOURCE_DIR}/src/iomanager.cpp ${PROJECT_SOURCE_DIR}/src/picopng.cpp ${PROJECT_SOURCE_DIR}/src/resourcemanager.cpp ${PROJECT_SOURCE_DIR}/src/sprite.cpp + ${PROJECT_SOURCE_DIR}/src/spritebatch.cpp ${PROJECT_SOURCE_DIR}/src/texturecache.cpp ${PROJECT_SOURCE_DIR}/src/window.cpp) diff --git a/include/YAGE/camera2d.hpp b/include/YAGE/camera2d.hpp index 030b0a37..0c86fc31 100644 --- a/include/YAGE/camera2d.hpp +++ b/include/YAGE/camera2d.hpp @@ -9,21 +9,23 @@ namespace yage class Camera2D { + // member variables private: bool matrix_needs_update_=true; float scale_=1.f; glm::vec2 position_; glm::mat4 camera_matrix_; glm::mat4 ortho_matrix_; + + // member functions public: Camera2D(int screen_width=1280, int screen_height=720); virtual ~Camera2D(); + // update camera location void update(); - - // setters - void setPosition(const glm::vec2 &new_position) { position_=new_position; matrix_needs_update_=true; } - void setScale(float new_scale) {scale_=new_scale; matrix_needs_update_=true; } + // camera movement + void move(const glm::vec2 &direction); // getters float getScale() { return scale_; } diff --git a/include/YAGE/inputmanager.hpp b/include/YAGE/inputmanager.hpp new file mode 100644 index 00000000..7f49c3b3 --- /dev/null +++ b/include/YAGE/inputmanager.hpp @@ -0,0 +1,23 @@ +#ifndef INPUT_MANAGER_HPP +#define INPUT_MANAGER_HPP + +#include + +namespace yage +{ + +class InputManager +{ + // member variables +private: + std::unordered_map key_map_; + + // member functions +public: + void keyPressed(unsigned key); + void keyReleased(unsigned key); + bool isKeyPressed(unsigned key) const; +}; + +} +#endif diff --git a/include/YAGE/spritebatch.hpp b/include/YAGE/spritebatch.hpp index fcc6faec..3f40a009 100644 --- a/include/YAGE/spritebatch.hpp +++ b/include/YAGE/spritebatch.hpp @@ -11,39 +11,76 @@ namespace yage { -struct Glyph +class Glyph { - GLuint texture; - float depth; - - Vertex top_left; - Vertex top_right; - Vertex bottom_right; - Vertex bottom_left; + // member variables +private: + GLuint texture_; + float depth_; + Vertex top_left_; + Vertex top_right_; + Vertex bottom_right_; + Vertex bottom_left_; + + // member functions +public: + Glyph(GLuint texture, float depth, const Vertex &top_left, const Vertex &top_right, const Vertex &bottom_right, const Vertex &bottom_left); + + inline GLuint texture() const { return texture_; } + inline float depth() const { return depth_; } + inline Vertex top_left() const { return top_left_; } + inline Vertex top_right() const { return top_right_; } + inline Vertex bottom_right() const { return bottom_right_; } + inline Vertex bottom_left() const { return bottom_left_; } +}; + +class RenderBatch +{ + // member variables +public: + GLint offset_; +private: + GLsizei num_vertices_; + GLuint texture_; + + // member functions +public: + RenderBatch(GLint offset, GLsizei num_vertices, GLuint texture); + + // getters + inline GLint offset() const { return offset_; } + inline GLsizei num_vertices() const { return num_vertices_; } + inline GLuint texture() const { return texture_; } }; class SpriteBatch { -public: // member variables + // member variables +public: + static const int NUM_VERTICES=6; private: GLuint vbo_=0; GLuint vao_=0; - std::vector glyphs_; std::vector glyph_ptrs_; - -public: // member functions + std::vector render_batches_; + + // member functions +public: SpriteBatch(); ~SpriteBatch(); + // initialize vaos and vbos + void init(); void begin(); void end(); - + // adds a sprite to the sprite batch to be rendered later void draw(const glm::vec4 &destination_rect, const glm::vec4 &uv_rect, GLuint texture, const Color &color, float depth); - - void renderBatch(); + // render the batch + void render(); private: void createVertexArray(); + void createRenderBatches(); void sortGlyphs(); }; diff --git a/include/YAGE/window.hpp b/include/YAGE/window.hpp index 5f104912..d86c00ac 100644 --- a/include/YAGE/window.hpp +++ b/include/YAGE/window.hpp @@ -34,6 +34,8 @@ public: // member functions void create(const std::string &window_name, int width, int height, unsigned flags=WindowFlags::SHOWN); // swap the buffer void swapBuffer(); + // clear buffer + void clearBuffer(); private: }; diff --git a/src/camera2d.cpp b/src/camera2d.cpp index 5dd25bb3..75bbe9c6 100644 --- a/src/camera2d.cpp +++ b/src/camera2d.cpp @@ -26,4 +26,10 @@ void Camera2D::update() } } +void Camera2D::move(const glm::vec2 &direction) +{ + position_+=direction; + matrix_needs_update_=true; +} + } // yage diff --git a/src/inputmanager.cpp b/src/inputmanager.cpp new file mode 100644 index 00000000..0bcb35f8 --- /dev/null +++ b/src/inputmanager.cpp @@ -0,0 +1,24 @@ +#include "inputmanager.hpp" + +namespace yage +{ + +void InputManager::keyPressed(unsigned key) +{ + key_map_[key]=true; +} + +void InputManager::keyReleased(unsigned key) +{ + key_map_[key]=false; +} + +bool InputManager::isKeyPressed(unsigned key) const +{ + auto key_index=key_map_.find(key); + if(key_index!=key_map_.end()) + return key_index->second; + return false; +} + +} diff --git a/src/sprite.cpp b/src/sprite.cpp index 941a680d..010b43a7 100644 --- a/src/sprite.cpp +++ b/src/sprite.cpp @@ -62,14 +62,20 @@ void Sprite::draw() { glBindTexture(GL_TEXTURE_2D, texture_.id); glBindBuffer(GL_ARRAY_BUFFER, vbo_id_); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, position)); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void *)offsetof(Vertex, color)); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, uv)); glDrawArrays(GL_TRIANGLES, 0, 6); + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(1); glDisableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); } diff --git a/src/spritebatch.cpp b/src/spritebatch.cpp index 1abcc567..8a08ea86 100644 --- a/src/spritebatch.cpp +++ b/src/spritebatch.cpp @@ -1,14 +1,30 @@ #include "spritebatch.hpp" #include +#include namespace yage { +const int SpriteBatch::NUM_VERTICES; + +Glyph::Glyph(GLuint texture, float depth, const Vertex &top_left, const Vertex &top_right, const Vertex &bottom_right, const Vertex &bottom_left) : + texture_(texture), + depth_(depth), + top_left_(top_left), + top_right_(top_right), + bottom_right_(bottom_right), + bottom_left_(bottom_left) +{} + +RenderBatch::RenderBatch(GLint offset, GLsizei num_vertices, GLuint texture) : + offset_(offset), + num_vertices_(num_vertices), + texture_(texture) +{} + SpriteBatch::SpriteBatch() -{ - createVertexArray(); -} +{} SpriteBatch::~SpriteBatch() { @@ -19,48 +35,135 @@ SpriteBatch::~SpriteBatch() glDeleteVertexArrays(1, &vbo_); } +void SpriteBatch::init() +{ + createVertexArray(); +} + void SpriteBatch::begin() -{} +{ + glyphs_.clear(); + glyph_ptrs_.clear(); + render_batches_.clear(); +} void SpriteBatch::end() -{} +{ + sortGlyphs(); + createRenderBatches(); +} void SpriteBatch::draw(const glm::vec4 &destination_rect, const glm::vec4 &uv_rect, GLuint texture, const Color &color, float depth) { - Glyph new_glyph; - new_glyph.texture=texture; - new_glyph.depth=depth; - - new_glyph.top_left.color=color; - new_glyph.top_left.setPosition(destination_rect.x, destination_rect.y+destination_rect.w); - new_glyph.top_left.setUv(uv_rect.x, uv_rect.y+uv_rect.w); - - new_glyph.top_right.color=color; - new_glyph.top_right.setPosition(destination_rect.x+destination_rect.z, destination_rect.y+destination_rect.w); - new_glyph.top_right.setUv(uv_rect.x+uv_rect.z, uv_rect.y+uv_rect.w); - - new_glyph.bottom_right.color=color; - new_glyph.bottom_right.setPosition(destination_rect.x+destination_rect.z, destination_rect.y); - new_glyph.bottom_right.setUv(uv_rect.x+uv_rect.z, uv_rect.y); + Vertex top_left, top_right, bottom_right, bottom_left; + + top_left.color=color; + top_left.setPosition(destination_rect.x, destination_rect.y+destination_rect.w); + top_left.setUv(uv_rect.x, uv_rect.y+uv_rect.w); + + top_right.color=color; + top_right.setPosition(destination_rect.x+destination_rect.z, destination_rect.y+destination_rect.w); + top_right.setUv(uv_rect.x+uv_rect.z, uv_rect.y+uv_rect.w); + + bottom_right.color=color; + bottom_right.setPosition(destination_rect.x+destination_rect.z, destination_rect.y); + bottom_right.setUv(uv_rect.x+uv_rect.z, uv_rect.y); + + bottom_left.color=color; + bottom_left.setPosition(destination_rect.x, destination_rect.y); + bottom_left.setUv(uv_rect.x, uv_rect.y); + + // deal with fragmenting by creating vector of pointers + glyphs_.emplace_back(texture, depth, top_left, top_right, bottom_right, bottom_left); + glyph_ptrs_.push_back(&glyphs_.back()); +} - new_glyph.bottom_right.color=color; - new_glyph.bottom_right.setPosition(destination_rect.x, destination_rect.y); - new_glyph.bottom_right.setUv(uv_rect.x, uv_rect.y); +void SpriteBatch::render() +{ + glBindVertexArray(vao_); + for(auto &&batch : render_batches_) + { + glBindTexture(GL_TEXTURE_2D, batch.texture()); + glDrawArrays(GL_TRIANGLES, batch.offset(), batch.num_vertices()); + } + glBindVertexArray(0); +} - // deal with fragmenting - glyphs_.push_back(new_glyph); - glyph_ptrs_.push_back(&glyphs_.back()); +void SpriteBatch::createVertexArray() +{ + if(vao_==0) + { + glGenVertexArrays(1, &vao_); + if(vao_==0) + throw std::runtime_error("glGenVertexArrays failed"); + } + // bind vertex array object + glBindVertexArray(vao_); + + if(vbo_==0) + { + glGenBuffers(1, &vbo_); + if(vbo_==0) + throw std::runtime_error("glGenBuffers failed"); + } + // bind vertex buffer object + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + + // enable vertex attribute arrays + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + + // set the vertex attribute pointers + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, position)); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void *)offsetof(Vertex, color)); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, uv)); + glDrawArrays(GL_TRIANGLES, 0, 6); + + // unbind vertex array object + glBindVertexArray(0); } -void SpriteBatch::renderBatch() -{} +void SpriteBatch::createRenderBatches() +{ + std::vector vertices; + if(glyph_ptrs_.empty()) + return; + + render_batches_.reserve(glyph_ptrs_.size()*NUM_VERTICES); + + for(int i=0; i<(int)glyph_ptrs_.size(); ++i) + { + if(i==0 || (i>0 && (glyph_ptrs_[i]->texture()!=glyph_ptrs_[i-1]->texture()))) + render_batches_.emplace_back(i*NUM_VERTICES, NUM_VERTICES, glyph_ptrs_[i]->texture()); + else + render_batches_.back().offset_+=NUM_VERTICES; + + vertices.push_back(glyph_ptrs_[i]->bottom_left()); + vertices.push_back(glyph_ptrs_[i]->top_left()); + vertices.push_back(glyph_ptrs_[i]->top_right()); + vertices.push_back(glyph_ptrs_[i]->bottom_left()); + vertices.push_back(glyph_ptrs_[i]->bottom_right()); + vertices.push_back(glyph_ptrs_[i]->top_right()); + + // bind vbo + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + // orphan the buffer + glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(Vertex), nullptr, GL_DYNAMIC_DRAW); + // upload the data + glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size()*sizeof(Vertex), vertices.data()); + // unbind buffer + glBindBuffer(GL_ARRAY_BUFFER, 0); + } +} void SpriteBatch::sortGlyphs() { + // sort using introsort or quicksort std::sort(glyph_ptrs_.begin(), glyph_ptrs_.end(), [] (Glyph *a, Glyph *b)->bool { - if(a->depth==b->depth) - return a->texturetexture; - return a->depthdepth; + if(a->depth()==b->depth()) + return a->texture()texture(); + return a->depth()depth(); }); } diff --git a/src/window.cpp b/src/window.cpp index ac2304c9..dc2a743a 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -58,7 +58,10 @@ void Window::create(const std::string &window_name, int width, int height, unsig // set vsync on instead of custom fps limiting SDL_GL_SetSwapInterval(1); // set the clear color to black - glClearColor(0.f, 0.f, 0.f, 1.f); + glClearColor(0.f, 0.5f, 0.f, 1.f); + // set alpha blending + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } void Window::swapBuffer() @@ -66,5 +69,13 @@ void Window::swapBuffer() // swap the window buffer SDL_GL_SwapWindow(window_); } + +void Window::clearBuffer() +{ + // set the clear depth + glClearDepth(1.f); + // clears buffer with clear color + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} } // yage -- cgit