aboutsummaryrefslogtreecommitdiffstats
path: root/yage/render
diff options
context:
space:
mode:
authorYann Herklotz <ymherklotz@gmail.com>2017-12-25 13:54:09 +0000
committerYann Herklotz <ymherklotz@gmail.com>2017-12-25 13:54:09 +0000
commitf949692714e72a0e2d45ebb6a5d698424ab71dee (patch)
treecfab638d8c4d35c297e981773cfee1a9af3490ee /yage/render
parent022a4bdd81332ce67d799be6a06afb42ae45ac2e (diff)
downloadYAGE-f949692714e72a0e2d45ebb6a5d698424ab71dee.tar.gz
YAGE-f949692714e72a0e2d45ebb6a5d698424ab71dee.zip
[Broken] Reorganising and fixing.
Diffstat (limited to 'yage/render')
-rw-r--r--yage/render/batch.h39
-rw-r--r--yage/render/drawable.h15
-rw-r--r--yage/render/ellipse.h6
-rw-r--r--yage/render/glslprogram.cpp215
-rw-r--r--yage/render/glslprogram.h58
-rw-r--r--yage/render/rectangle.h15
-rw-r--r--yage/render/shape.h16
-rw-r--r--yage/render/sprite.cpp95
-rw-r--r--yage/render/sprite.h49
-rw-r--r--yage/render/spritebatch.cpp200
-rw-r--r--yage/render/spritebatch.h90
11 files changed, 798 insertions, 0 deletions
diff --git a/yage/render/batch.h b/yage/render/batch.h
new file mode 100644
index 00000000..e301531c
--- /dev/null
+++ b/yage/render/batch.h
@@ -0,0 +1,39 @@
+#ifndef YAGE_CORE_BATCH_H
+#define YAGE_CORE_BATCH_H
+
+namespace yage
+{
+
+/** The Batch class will be the base class for all the different batching
+ * processes that might use different shaders and attributes. This is necessary
+ * because when we use a different shader, we have to bind a specific number
+ * of attributes, and we might not always want to have a texture, colours and
+ * coordinates, for example, when only using simple shapes.
+ *
+ * Batching
+ * ========
+ * The purpose of batching is to combine all sprites that use the same textures
+ * so that the textures does not have to be switched out on the gpu very often.
+ * This produces a much more efficient rendering process. An implementation of
+ * this can be seen in the SpriteBatch class, as it sorts and renders the
+ * objects you give it.
+ *
+ * The reason this base class exists, is because it makes it easier to also
+ * render objects that may not need a texture, or may require multiple textures
+ * or different attributes.
+ */
+class Batch
+{
+public:
+ virtual bool init();
+ virtual void begin();
+ virtual void end();
+ virtual void draw();
+ virtual void render();
+
+protected:
+};
+
+} // namespace yage
+
+#endif
diff --git a/yage/render/drawable.h b/yage/render/drawable.h
new file mode 100644
index 00000000..0e4b6843
--- /dev/null
+++ b/yage/render/drawable.h
@@ -0,0 +1,15 @@
+#ifndef YAGE_CORE_DRAWABLE_H
+#define YAGE_CORE_DRAWABLE_H
+
+namespace yage
+{
+
+class Drawable
+{
+public:
+ virtual void render() const;
+};
+
+} // namespace yage
+
+#endif
diff --git a/yage/render/ellipse.h b/yage/render/ellipse.h
new file mode 100644
index 00000000..bd0b0d3e
--- /dev/null
+++ b/yage/render/ellipse.h
@@ -0,0 +1,6 @@
+#ifndef YAGE_RENDER_ELLIPSE_H
+#define YAGE_RENDER_ELLIPSE_H
+
+
+
+#endif
diff --git a/yage/render/glslprogram.cpp b/yage/render/glslprogram.cpp
new file mode 100644
index 00000000..13abbba3
--- /dev/null
+++ b/yage/render/glslprogram.cpp
@@ -0,0 +1,215 @@
+/** ---------------------------------------------------------------------------
+ * @file: glslprogram.cpp
+ *
+ * Copyright (c) 2017 Yann Herklotz Grave <ymherklotz@gmail.com>
+ * MIT License, see LICENSE file for more details.
+ * ----------------------------------------------------------------------------
+ */
+
+#include "glslprogram.h"
+
+#include <fstream>
+#include <stdexcept>
+#include <vector>
+
+namespace yage
+{
+
+GlslProgram::~GlslProgram()
+{
+ /// cleans up all the shaders and the program
+ if (fragment_shader_id_ != 0) {
+ glDeleteShader(fragment_shader_id_);
+ }
+
+ if (vertex_shader_id_ != 0) {
+ glDeleteShader(vertex_shader_id_);
+ }
+
+ if (program_id_ != 0) {
+ glDeleteProgram(program_id_);
+ }
+}
+
+void GlslProgram::compileShader(GLuint shader, const std::string &shaderContent)
+{
+ // cast source to a c string to get the address of it and input it for
+ // compilation
+ const auto *vertex_source = (const GLchar *)shaderContent.c_str();
+ glShaderSource(shader, 1, &vertex_source, nullptr);
+ glCompileShader(shader);
+
+ // check if compilation was successful
+ GLint is_compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, (int *)&is_compiled);
+
+ // if it isn't compiled throw exception to clean up
+ if (is_compiled == GL_FALSE) {
+ GLint max_length = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &max_length);
+
+ std::vector<GLchar> error_log(max_length);
+ glGetShaderInfoLog(shader, max_length, &max_length, &error_log[0]);
+ std::string error_log_str((const char *)&error_log[0]);
+
+ std::string shaderName;
+ if (shader == vertex_shader_id_)
+ shaderName = "vertex shader";
+ else
+ shaderName = "fragment shader";
+
+ throw std::runtime_error("Couldn't compile the " + shaderName + " : " +
+ error_log_str);
+ }
+}
+
+void GlslProgram::compileShaderFromFile(GLuint shader,
+ const std::string &file_path)
+{
+ // get a string with the input from the shader file
+ std::ifstream file(file_path);
+ if (!file.is_open()) {
+ throw std::runtime_error("Failed to open '" + file_path + "'");
+ }
+
+ std::string content = "";
+ std::string line;
+
+ while (std::getline(file, line)) {
+ content += line + "\n";
+ }
+ file.close();
+
+ compileShader(shader, content);
+}
+
+void GlslProgram::initShaderId()
+{
+ // create the program that will be run on GPU
+ program_id_ = glCreateProgram();
+
+ // create vertex shader
+ vertex_shader_id_ = glCreateShader(GL_VERTEX_SHADER);
+ if (vertex_shader_id_ == 0) {
+ throw std::runtime_error("Vertex shader failed to be created");
+ }
+
+ // create fragment shader
+ fragment_shader_id_ = glCreateShader(GL_FRAGMENT_SHADER);
+ if (fragment_shader_id_ == 0) {
+ throw std::runtime_error("Fragment shader failed to be created");
+ }
+}
+
+void GlslProgram::compileShaders(const std::string &vertexShader,
+ const std::string fragmentShader)
+{
+ initShaderId();
+
+ compileShader(vertex_shader_id_, vertexShader);
+ compileShader(fragment_shader_id_, fragmentShader);
+}
+
+void GlslProgram::compileShadersFromFile(
+ const std::string &vertex_shader_path,
+ const std::string &fragment_shader_path)
+{
+ initShaderId();
+
+ // compile the two shaders
+ compileShaderFromFile(vertex_shader_id_, vertex_shader_path);
+ compileShaderFromFile(fragment_shader_id_, fragment_shader_path);
+}
+
+void GlslProgram::linkShaders()
+{
+ // attach the shaders that we want
+ glAttachShader(program_id_, vertex_shader_id_);
+ glAttachShader(program_id_, fragment_shader_id_);
+
+ // link our program
+ glLinkProgram(program_id_);
+
+ GLint is_linked = 0;
+ glGetProgramiv(program_id_, GL_LINK_STATUS, (int *)&is_linked);
+ if (is_linked == GL_FALSE) {
+ GLint max_length = 0;
+ glGetProgramiv(program_id_, GL_INFO_LOG_LENGTH, &max_length);
+
+ std::vector<GLchar> error_log(max_length);
+ glGetProgramInfoLog(program_id_, max_length, &max_length,
+ &error_log[0]);
+
+ std::string error_log_str((const char *)&error_log[0]);
+
+ throw std::runtime_error("Could not link program: " + error_log_str);
+ }
+
+ // detach shaders after successful link
+ glDetachShader(program_id_, fragment_shader_id_);
+ glDetachShader(program_id_, vertex_shader_id_);
+
+ // we can then delete the shaders once we have the program
+ glDeleteShader(fragment_shader_id_);
+ glDeleteShader(vertex_shader_id_);
+}
+
+void GlslProgram::addAttribute(const std::string &attribute_name)
+{
+ glBindAttribLocation(program_id_, attribute_index_++,
+ attribute_name.c_str());
+}
+
+GLint GlslProgram::getUniformLocation(const std::string &uniform_name)
+{
+ GLint location = glGetUniformLocation(program_id_, uniform_name.c_str());
+ if ((GLuint)location == GL_INVALID_INDEX) {
+ throw std::runtime_error("'" + uniform_name + "' not found");
+ }
+ return location;
+}
+
+void GlslProgram::use()
+{
+ glUseProgram(program_id_);
+ for (int i = 0; i < attribute_index_; ++i) {
+ glEnableVertexAttribArray(i);
+ }
+}
+
+void GlslProgram::unuse()
+{
+ for (int i = 0; i < attribute_index_; ++i) {
+ glDisableVertexAttribArray(i);
+ }
+ glUseProgram(0);
+}
+
+void GlslProgram::defaultSetup()
+{
+ std::string vertexShader =
+ "#version 130\n\nin vec2 vertex_position;\nin vec4 vertex_colour;\nin "
+ "vec2 vertex_uv;\n\nout vec2 fragment_position;\nout vec4 "
+ "fragment_colour;\nout vec2 fragment_uv;\n\nuniform mat4 P;\n\nvoid "
+ "main()\n{\n gl_Position.xy = (P*vec4(vertex_position, 0.0, "
+ "1.0)).xy;\n gl_Position.z = 0.0;\n gl_Position.w = 1.0;\n\n "
+ "fragment_position = vertex_position;\n fragment_colour = "
+ "vertex_colour;\n fragment_uv = vec2(vertex_uv.x, "
+ "1-vertex_uv.y);\n\n}";
+
+ std::string fragmentShader =
+ "#version 130\n\nin vec2 fragment_position;\nin vec4 "
+ "fragment_colour;\nin vec2 fragment_uv;\n\nout vec4 colour;\n\nuniform "
+ "sampler2D texture_sampler;\n\nvoid main()\n{\n vec4 texture_color "
+ "= texture(texture_sampler, fragment_uv);\n\n colour = "
+ "texture_color;\n}";
+
+ compileShaders(vertexShader, fragmentShader);
+ addAttribute("vertex_position");
+ addAttribute("vertex_colour");
+ addAttribute("vertex_uv");
+
+ linkShaders();
+}
+
+} // namespace yage
diff --git a/yage/render/glslprogram.h b/yage/render/glslprogram.h
new file mode 100644
index 00000000..0617bc1e
--- /dev/null
+++ b/yage/render/glslprogram.h
@@ -0,0 +1,58 @@
+/** ---------------------------------------------------------------------------
+ * @file: glslprogram.h
+ *
+ * Copyright (c) 2017 Yann Herklotz Grave <ymherklotz@gmail.com>
+ * MIT License, see LICENSE file for more details.
+ * ----------------------------------------------------------------------------
+ */
+
+#ifndef GLSL_PROGRAM_H
+#define GLSL_PROGRAM_H
+
+#include <glad/glad.h>
+
+#include <string>
+
+namespace yage
+{
+
+class GlslProgram
+{
+public:
+ GlslProgram() = default;
+ GlslProgram(const GlslProgram &) = delete;
+ GlslProgram(GlslProgram &&) = delete;
+ ~GlslProgram();
+
+ GlslProgram &operator=(const GlslProgram &) = delete;
+ GlslProgram &operator=(GlslProgram &&) = delete;
+
+ /// compiles vertex and fragment shader
+ void compileShaders(const std::string &vertexShader,
+ const std::string fragmentShader);
+ void compileShadersFromFile(const std::string &vertex_shader_path,
+ const std::string &fragment_shader_path);
+ void linkShaders();
+ void addAttribute(const std::string &attribute_name);
+ GLint getUniformLocation(const std::string &uniform_name);
+ void use();
+ void unuse();
+
+ void defaultSetup();
+
+private:
+ /// compiled shader program id
+ GLuint program_id_ = 0;
+ GLuint vertex_shader_id_ = 0;
+ GLuint fragment_shader_id_ = 0;
+ int attribute_index_ = 0;
+
+ /// compiles one shader
+ void compileShader(GLuint shader, const std::string &shaderContent);
+ void compileShaderFromFile(GLuint shader, const std::string &file_path);
+ void initShaderId();
+};
+
+} // namespace yage
+
+#endif
diff --git a/yage/render/rectangle.h b/yage/render/rectangle.h
new file mode 100644
index 00000000..11df8a18
--- /dev/null
+++ b/yage/render/rectangle.h
@@ -0,0 +1,15 @@
+#ifndef YAGE_RENDER_RECTANGLE_H
+#define YAGE_RENDER_RECTANGLE_H
+
+#include "shape.h"
+
+namespace yage {
+
+class Rectangle
+{
+ virtual void render() const;
+};
+
+} // namespace yage
+
+#endif
diff --git a/yage/render/shape.h b/yage/render/shape.h
new file mode 100644
index 00000000..bdf318ed
--- /dev/null
+++ b/yage/render/shape.h
@@ -0,0 +1,16 @@
+#ifndef YAGE_RENDER_SHAPE_H
+#define YAGE_RENDER_SHAPE_H
+
+#include "drawable.h"
+
+namespace yage
+{
+
+class Shape : public Drawable
+{
+ virtual void render() const;
+};
+
+} // namespace yage
+
+#endif
diff --git a/yage/render/sprite.cpp b/yage/render/sprite.cpp
new file mode 100644
index 00000000..6862f910
--- /dev/null
+++ b/yage/render/sprite.cpp
@@ -0,0 +1,95 @@
+/** ---------------------------------------------------------------------------
+ * @file: sprite.cpp
+ *
+ * Copyright (c) 2017 Yann Herklotz Grave <ymherklotz@gmail.com>
+ * MIT License, see LICENSE file for more details.
+ * ----------------------------------------------------------------------------
+ */
+
+#include <yage/core/resourcemanager.h>
+#include <yage/core/sprite.h>
+#include <yage/core/vertex.h>
+
+#include <cstddef>
+
+namespace yage
+{
+
+Sprite::~Sprite()
+{
+ if (vbo_id_ != 0) {
+ glDeleteBuffers(1, &vbo_id_);
+ }
+}
+
+void Sprite::init(float x, float y, float width, float height,
+ const std::string &texture_path)
+{
+ x_ = x;
+ y_ = y;
+ width_ = width;
+ height_ = height;
+ texture_ = ResourceManager::getTexture(texture_path);
+
+ if (vbo_id_ == 0) {
+ glGenBuffers(1, &vbo_id_);
+ }
+
+ Vertex vertex_data[6];
+
+ vertex_data[0].setPosition(x + width, y + height);
+ vertex_data[0].setUv(1.f, 1.f);
+
+ vertex_data[1].setPosition(x, y + height);
+ vertex_data[1].setUv(0.f, 1.f);
+
+ vertex_data[2].setPosition(x, y);
+ vertex_data[2].setUv(0.f, 0.f);
+
+ vertex_data[3].setPosition(x, y);
+ vertex_data[3].setUv(0.f, 0.f);
+
+ vertex_data[4].setPosition(x + width, y + height);
+ vertex_data[4].setUv(1.f, 1.f);
+
+ vertex_data[5].setPosition(x + width, y);
+ vertex_data[5].setUv(1.f, 0.f);
+
+ for (auto &i : vertex_data) {
+ i.setColour(255, 0, 255, 255);
+ }
+
+ vertex_data[1].setColour(0, 255, 255, 255);
+ vertex_data[4].setColour(255, 0, 0, 255);
+
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_id_);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data,
+ GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+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, colour));
+ 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);
+}
+
+} // namespace yage
diff --git a/yage/render/sprite.h b/yage/render/sprite.h
new file mode 100644
index 00000000..852f4f28
--- /dev/null
+++ b/yage/render/sprite.h
@@ -0,0 +1,49 @@
+/** ---------------------------------------------------------------------------
+ * @file: sprite.h
+ *
+ * Copyright (c) 2017 Yann Herklotz Grave <ymherklotz@gmail.com>
+ * MIT License, see LICENSE file for more details.
+ * ----------------------------------------------------------------------------
+ */
+
+#ifndef SPRITE_H
+#define SPRITE_H
+
+#include "texture.h"
+
+#include <glad/glad.h>
+
+#include <string>
+
+namespace yage
+{
+
+/** @deprecated Use SpriteBatch instead
+ */
+class Sprite
+{
+private:
+ float x_;
+ float y_;
+ float width_;
+ float height_;
+ GLuint vbo_id_ = 0;
+ Texture texture_;
+
+public:
+ Sprite() = default;
+ Sprite(const Sprite &) = delete;
+ Sprite(Sprite &&) = delete;
+ ~Sprite();
+
+ Sprite &operator=(const Sprite &) = delete;
+ Sprite &operator=(Sprite &&) = delete;
+
+ void init(float x, float y, float width, float height,
+ const std::string &texture_path);
+ void draw();
+};
+
+} // namespace yage
+
+#endif
diff --git a/yage/render/spritebatch.cpp b/yage/render/spritebatch.cpp
new file mode 100644
index 00000000..d65340d7
--- /dev/null
+++ b/yage/render/spritebatch.cpp
@@ -0,0 +1,200 @@
+/** ---------------------------------------------------------------------------
+ * @file: spritebatch.cpp
+ *
+ * Copyright (c) 2017 Yann Herklotz Grave <ymherklotz@gmail.com>
+ * MIT License, see LICENSE file for more details.
+ * ----------------------------------------------------------------------------
+ */
+
+#include "spritebatch.h"
+
+#include <algorithm>
+#include <stdexcept>
+
+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)
+ : num_vertices_(num_vertices), offset_(offset), texture_(texture)
+{
+}
+
+SpriteBatch::SpriteBatch()
+{
+ createVertexArray();
+}
+
+SpriteBatch::~SpriteBatch()
+{
+ if (vao_ != 0) {
+ glDeleteVertexArrays(1, &vao_);
+ }
+
+ if (vbo_ != 0) {
+ glDeleteVertexArrays(1, &vbo_);
+ }
+}
+
+void SpriteBatch::begin()
+{
+ glyphs_.clear();
+ glyph_ptrs_.clear();
+ render_batches_.clear();
+}
+
+void SpriteBatch::end()
+{
+ sortGlyphs();
+ createRenderBatches();
+}
+
+void SpriteBatch::draw(const yage::Vector4f &destination_rect,
+ const yage::Vector4f &uv_rect, GLuint texture,
+ const Colour &colour, float depth)
+{
+ Vertex top_left, top_right, bottom_right, bottom_left;
+
+ top_left.colour = colour;
+ 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.colour = colour;
+ 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.colour = colour;
+ 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.colour = colour;
+ 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());
+}
+
+void SpriteBatch::render()
+{
+ // sort and create render batches
+ sortGlyphs();
+ createRenderBatches();
+
+ glBindVertexArray(vao_);
+ for (auto &&batch : render_batches_) {
+ glBindTexture(GL_TEXTURE_2D, batch.texture());
+ glDrawArrays(GL_TRIANGLES, batch.offset(), batch.num_vertices());
+ }
+ glBindVertexArray(0);
+
+ // clear and reset the vectors
+ glyphs_.clear();
+ glyph_ptrs_.clear();
+ render_batches_.clear();
+}
+
+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, colour));
+ 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::createRenderBatches()
+{
+ std::vector<Vertex> 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().num_vertices_ += 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->texture() < b->texture();
+ }
+ return a->depth() < b->depth();
+ });
+}
+
+} // namespace yage
diff --git a/yage/render/spritebatch.h b/yage/render/spritebatch.h
new file mode 100644
index 00000000..c018bdc6
--- /dev/null
+++ b/yage/render/spritebatch.h
@@ -0,0 +1,90 @@
+/** ---------------------------------------------------------------------------
+ * @file: spritebatch.h
+ *
+ * Copyright (c) 2017 Yann Herklotz Grave <ymherklotz@gmail.com>
+ * MIT License, see LICENSE file for more details.
+ * ----------------------------------------------------------------------------
+ */
+
+#ifndef YAGE_SPRITE_BATCH_H
+#define YAGE_SPRITE_BATCH_H
+
+#include "batch.h"
+#include "vertex.h"
+
+#include <glad/glad.h>
+#include <glm/glm.hpp>
+
+#include <vector>
+
+namespace yage
+{
+
+class SpriteBatch;
+
+/** Glyph with information of the texture.
+ */
+class Glyph
+{
+private:
+ GLuint texture_;
+ float depth_;
+ Vertex top_left_;
+ Vertex top_right_;
+ Vertex bottom_right_;
+ Vertex bottom_left_;
+
+public:
+ Glyph(GLuint texture, float depth, const Vertex &top_left,
+ const Vertex &top_right, const Vertex &bottom_right,
+ const Vertex &bottom_left);
+
+ GLuint texture() const { return texture_; }
+ float depth() const { return depth_; }
+ Vertex top_left() const { return top_left_; }
+ Vertex top_right() const { return top_right_; }
+ Vertex bottom_right() const { return bottom_right_; }
+ Vertex bottom_left() const { return bottom_left_; }
+};
+
+
+class SpriteBatch : public Batch
+{
+public:
+ static const int NUM_VERTICES = 6;
+
+private:
+ GLuint vbo_ = 0;
+ GLuint vao_ = 0;
+ std::vector<Glyph> glyphs_;
+ std::vector<Glyph *> glyph_ptrs_;
+ std::vector<RenderBatch> render_batches_;
+
+public:
+ SpriteBatch();
+ SpriteBatch(const SpriteBatch &) = delete;
+ SpriteBatch(SpriteBatch &&) = delete;
+ ~SpriteBatch();
+
+ SpriteBatch &operator=(const SpriteBatch &) = delete;
+ SpriteBatch &operator=(SpriteBatch &&) = delete;
+
+ // initialize vaos and vbos
+ 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 Colour &colour, float depth);
+ // render the batch
+ void render();
+
+private:
+ void createVertexArray();
+ void createRenderBatches();
+ void sortGlyphs();
+};
+
+} // namespace yage
+
+#endif