diff options
author | Yann Herklotz <ymherklotz@gmail.com> | 2018-01-06 11:30:24 +0000 |
---|---|---|
committer | Yann Herklotz <ymherklotz@gmail.com> | 2018-01-06 11:30:24 +0000 |
commit | c7090180503f263c60ec34844992e0e8d4bea85a (patch) | |
tree | 6ecc5b2e16856db49de056738b36e1ba103d3049 /yage/render | |
parent | cf4c73f2a75b470a4d4c4167105f92bc46f1926c (diff) | |
parent | 07012cf0982d3f86aebe83b5bdc4a67332c635da (diff) | |
download | YAGE-c7090180503f263c60ec34844992e0e8d4bea85a.tar.gz YAGE-c7090180503f263c60ec34844992e0e8d4bea85a.zip |
Merge branch 'develop'
Diffstat (limited to 'yage/render')
-rw-r--r-- | yage/render/batch.h | 36 | ||||
-rw-r--r-- | yage/render/drawable.h | 17 | ||||
-rw-r--r-- | yage/render/ellipse.h | 6 | ||||
-rw-r--r-- | yage/render/rectangle.cpp | 38 | ||||
-rw-r--r-- | yage/render/rectangle.h | 23 | ||||
-rw-r--r-- | yage/render/shader.cpp | 132 | ||||
-rw-r--r-- | yage/render/shader.h | 49 | ||||
-rw-r--r-- | yage/render/shape.h | 17 | ||||
-rw-r--r-- | yage/render/sprite.cpp | 95 | ||||
-rw-r--r-- | yage/render/sprite.h | 49 | ||||
-rw-r--r-- | yage/render/spritebatch.cpp | 180 | ||||
-rw-r--r-- | yage/render/spritebatch.h | 97 |
12 files changed, 739 insertions, 0 deletions
diff --git a/yage/render/batch.h b/yage/render/batch.h new file mode 100644 index 00000000..6694fbcb --- /dev/null +++ b/yage/render/batch.h @@ -0,0 +1,36 @@ +#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 render(); +}; + +} // namespace yage + +#endif diff --git a/yage/render/drawable.h b/yage/render/drawable.h new file mode 100644 index 00000000..c126bb09 --- /dev/null +++ b/yage/render/drawable.h @@ -0,0 +1,17 @@ +#ifndef YAGE_CORE_DRAWABLE_H +#define YAGE_CORE_DRAWABLE_H + +#include "spritebatch.h" + +namespace yage +{ + +class Drawable +{ +public: + virtual void draw(SpriteBatch &sp) = 0; +}; + +} // 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/rectangle.cpp b/yage/render/rectangle.cpp new file mode 100644 index 00000000..632c7ceb --- /dev/null +++ b/yage/render/rectangle.cpp @@ -0,0 +1,38 @@ +#include "rectangle.h" + +#include "../data/vertex.h" + +#include <glad/glad.h> + +#include <cstddef> + +namespace yage +{ + +Rectangle::Rectangle(glm::vec4 position) : position_(position) {} + +void Rectangle::render() const +{ + // create vertex array + GLuint rect_vao, rect_vbo; + + // bind vertex array object + glGenVertexArrays(1, &rect_vao); + glBindVertexArray(rect_vao); + + // bind vertex buffer object + glGenBuffers(1, &rect_vbo); + glBindBuffer(GL_ARRAY_BUFFER, rect_vbo); + + // enable vertex attribute arrays + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + + // set the vertex attribute pointers + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, position)); + + glBindVertexArray(0); +} + +} // namepsace yage + diff --git a/yage/render/rectangle.h b/yage/render/rectangle.h new file mode 100644 index 00000000..ad38caa5 --- /dev/null +++ b/yage/render/rectangle.h @@ -0,0 +1,23 @@ +#ifndef YAGE_RENDER_RECTANGLE_H +#define YAGE_RENDER_RECTANGLE_H + +#include "shape.h" + +#include <glm/glm.hpp> + +namespace yage +{ + +class Rectangle : public Shape +{ +public: + Rectangle(glm::vec4 position); + virtual void render() const; + +private: + glm::vec4 position_; +}; + +} // namespace yage + +#endif diff --git a/yage/render/shader.cpp b/yage/render/shader.cpp new file mode 100644 index 00000000..156c2aa1 --- /dev/null +++ b/yage/render/shader.cpp @@ -0,0 +1,132 @@ +/** --------------------------------------------------------------------------- + * @file: shader.cpp + * + * Copyright (c) 2017 Yann Herklotz Grave <ymherklotz@gmail.com> + * MIT License, see LICENSE file for more details. + * ---------------------------------------------------------------------------- + */ + +#include "shader.h" + +#include <fstream> +#include <sstream> +#include <stdexcept> +#include <string> +#include <vector> + +using std::runtime_error; + +namespace yage +{ + +Shader::Shader(const std::string &vertex_path, const std::string &fragment_path) +{ + std::string vertex_source, fragment_source; + std::ifstream vertex_file, fragment_file; + + vertex_file.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fragment_file.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + try { + vertex_file.open(vertex_path); + fragment_file.open(fragment_path); + + std::ostringstream vertex_stream, fragment_stream; + vertex_stream << vertex_file.rdbuf(); + fragment_stream << fragment_file.rdbuf(); + + vertex_file.close(); + fragment_file.close(); + + vertex_source = vertex_stream.str(); + fragment_source = fragment_stream.str(); + } catch (std::ifstream::failure e) { + throw runtime_error("File could not be opened: " + + std::string(e.what())); + } + + const char *vertex_source_c = vertex_source.c_str(); + const char *fragment_source_c = fragment_source.c_str(); + + GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_source_c, NULL); + glCompileShader(vertex_shader); + errorCheck(vertex_shader, "vertex"); + + GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_source_c, NULL); + glCompileShader(fragment_shader); + errorCheck(fragment_shader, "fragment"); + + program_id_ = glCreateProgram(); + glAttachShader(program_id_, vertex_shader); + glAttachShader(program_id_, fragment_shader); + glLinkProgram(program_id_); + errorCheck(program_id_, "program"); + + glDetachShader(program_id_, vertex_shader); + glDetachShader(program_id_, fragment_shader); + + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); +} + +Shader::~Shader() +{ + /// cleans up all the shaders and the program + if (program_id_ != 0) { + glDeleteProgram(program_id_); + } +} + +void Shader::use() const +{ + glUseProgram(program_id_); +} + +void Shader::setUniform(const std::string &name, int value) const +{ + glUniform1i(getUniformLocation(name), static_cast<GLint>(value)); +} + +void Shader::setUniform(const std::string &name, float value) const +{ + glUniform1f(getUniformLocation(name), static_cast<GLfloat>(value)); +} + +void Shader::setUniform(const std::string &name, const glm::mat4 &matrix) const +{ + glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, &(matrix[0][0])); +} + +GLint Shader::getUniformLocation(const std::string &uniform_name) const +{ + GLuint location = glGetUniformLocation(program_id_, uniform_name.c_str()); + if (location == GL_INVALID_INDEX) { + throw std::runtime_error("'" + uniform_name + "' not found"); + } + return location; +} + +void Shader::errorCheck(GLuint shader, const std::string &shader_type) const +{ + int success; + char info_log[1024]; + if (shader_type != std::string("program")) { + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(shader, 1024, NULL, info_log); + throw runtime_error(shader_type + + " failed to compile: " + std::string(info_log)); + } + } else { + glGetProgramiv(shader, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(shader, 1024, NULL, info_log); + throw runtime_error(shader_type + + " failed to link: " + std::string(info_log)); + } + } +} + +} // namespace yage diff --git a/yage/render/shader.h b/yage/render/shader.h new file mode 100644 index 00000000..7f4d2f9d --- /dev/null +++ b/yage/render/shader.h @@ -0,0 +1,49 @@ +/** --------------------------------------------------------------------------- + * @file: shader.h + * + * Copyright (c) 2017 Yann Herklotz Grave <ymherklotz@gmail.com> + * MIT License, see LICENSE file for more details. + * ---------------------------------------------------------------------------- + */ + +#ifndef YAGE_RENDER_SHADER_H +#define YAGE_RENDER_SHADER_H + +#include <glm/glm.hpp> +#include <glad/glad.h> + +#include <string> + +namespace yage +{ + +class Shader +{ +public: + Shader(const std::string &vertex_path, const std::string &fragment_path); + Shader(const Shader &) = delete; + Shader(Shader &&) = delete; + ~Shader(); + + Shader &operator=(const Shader &) = delete; + Shader &operator=(Shader &&) = delete; + + /// compiles vertex and fragment shader + void use() const; + + /// set uniforms of different type + void setUniform(const std::string &name, int value) const; + void setUniform(const std::string &name, float value) const; + void setUniform(const std::string &name, const glm::mat4 &matrix) const; + +private: + /// compiled shader program id + GLuint program_id_ = 0; + + GLint getUniformLocation(const std::string &uniform_name) const; + void errorCheck(GLuint shader, const std::string &shader_type) const; +}; + +} // namespace yage + +#endif diff --git a/yage/render/shape.h b/yage/render/shape.h new file mode 100644 index 00000000..1fdf1c4f --- /dev/null +++ b/yage/render/shape.h @@ -0,0 +1,17 @@ +#ifndef YAGE_RENDER_SHAPE_H +#define YAGE_RENDER_SHAPE_H + +#include "drawable.h" + +namespace yage +{ + +class Shape : public Drawable +{ +public: + 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..98853c94 --- /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 "sprite.h" +#include "../core/resourcemanager.h" +#include "../data/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..51821be5 --- /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 "../data/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..90958246 --- /dev/null +++ b/yage/render/spritebatch.cpp @@ -0,0 +1,180 @@ +/** --------------------------------------------------------------------------- + * @file: spritebatch.cpp + * + * Copyright (c) 2017 Yann Herklotz Grave <ymherklotz@gmail.com> + * MIT License, see LICENSE file for more details. + * ---------------------------------------------------------------------------- + */ + +#include "spritebatch.h" + +#include "../core/logger.h" +#include <algorithm> +#include <iostream> +#include <stdexcept> + +using std::cout; + +#include <GLFW/glfw3.h> + +namespace yage +{ + +const int SpriteBatch::NUM_VERTICES; + +SpriteBatch::SpriteBatch() : vao_(0), vbo_(0) +{ + glGenVertexArrays(1, &vao_); + if (vao_ == 0) { + throw std::runtime_error("failed to generate VAO"); + } + // bind vertex array object + glBindVertexArray(vao_); + + glGenBuffers(1, &vbo_); + if (vbo_ == 0) { + throw std::runtime_error("failed to generate VBO"); + } + // bind vertex buffer object + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + + // 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)); + + // enable vertex attribute arrays + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); +} + +SpriteBatch::~SpriteBatch() +{ + if (vbo_ != 0) { + glDeleteBuffers(1, &vbo_); + } + if (vao_ != 0) { + glDeleteVertexArrays(1, &vao_); + } +} + +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 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); +} + +void SpriteBatch::render() +{ + glBindVertexArray(vao_); + // sort and create render batches + sortGlyphs(); + createRenderBatches(); + glActiveTexture(GL_TEXTURE0); + for (auto &&batch : render_batches_) { + yLogDebug << "Drawing " << batch.num_vertices + << " vertices bound to texture: " << batch.texture; + glBindTexture(GL_TEXTURE_2D, batch.texture); + glDrawArrays(GL_TRIANGLES, batch.offset, batch.num_vertices); + } + + // clear and reset the vectors + glyphs_.clear(); + glyph_ptrs_.clear(); + render_batches_.clear(); +} + +void SpriteBatch::createRenderBatches() +{ + std::vector<Vertex> vertices; + if (glyph_ptrs_.empty()) { + return; + } + + vertices.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]->top_right); + vertices.push_back(glyph_ptrs_[i]->bottom_right); + vertices.push_back(glyph_ptrs_[i]->bottom_left); + } + + // orphan the buffer + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), nullptr, + GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(Vertex), + vertices.data()); +} + +void SpriteBatch::sortGlyphs() +{ + glyph_ptrs_.reserve(glyphs_.size()); + for (auto &glyph : glyphs_) { + glyph_ptrs_.push_back(&glyph); + } + + // sort using introsort or quicksort + std::sort(glyph_ptrs_.begin(), glyph_ptrs_.end(), + [](details::Glyph *a, details::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..fb9f6337 --- /dev/null +++ b/yage/render/spritebatch.h @@ -0,0 +1,97 @@ +/** --------------------------------------------------------------------------- + * @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 "../data/vertex.h" +#include "batch.h" + +#include <glad/glad.h> +#include <glm/glm.hpp> + +#include <vector> + +namespace yage +{ + +namespace details +{ + +struct RenderBatch { + GLint offset; + GLsizei num_vertices; + GLuint texture; + + RenderBatch(GLint offset_i, GLsizei num_vertices_i, GLuint texture_i) + : offset(offset_i), num_vertices(num_vertices_i), texture(texture_i) + { + } +}; + +/** Glyph with information of the texture. + */ +struct Glyph { + GLuint texture; + float depth; + Vertex top_left; + Vertex top_right; + Vertex bottom_right; + Vertex bottom_left; + + Glyph(GLuint texture_i, float depth_i, const Vertex &top_left_i, + const Vertex &top_right_i, const Vertex &bottom_right_i, + const Vertex &bottom_left_i) + : texture(texture_i), depth(depth_i), top_left(top_left_i), + top_right(top_right_i), bottom_right(bottom_right_i), + bottom_left(bottom_left_i) + { + } +}; + +} // namespace details + +class SpriteBatch +{ +public: + static const int NUM_VERTICES = 6; + +private: + GLuint vao_; + GLuint vbo_; + std::vector<details::Glyph> glyphs_; + std::vector<details::Glyph *> glyph_ptrs_; + std::vector<details::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 createRenderBatches(); + void sortGlyphs(); +}; + +} // namespace yage + +#endif |