diff --git a/libopenage/CMakeLists.txt b/libopenage/CMakeLists.txt index bfbf0b0bd8..3f417269b2 100644 --- a/libopenage/CMakeLists.txt +++ b/libopenage/CMakeLists.txt @@ -50,6 +50,7 @@ add_subdirectory("testing") add_subdirectory("unit") add_subdirectory("util") add_subdirectory("rng") +add_subdirectory("renderer") # run codegen, add files to executable codegen_run() diff --git a/libopenage/engine.cpp b/libopenage/engine.cpp index bc60a1846e..c1efcb2131 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -222,6 +222,8 @@ Engine::Engine(util::Dir *data_dir, const char *windowtitle) bind_player_switch(input::action_t::SWITCH_TO_PLAYER_6, 6); bind_player_switch(input::action_t::SWITCH_TO_PLAYER_7, 7); bind_player_switch(input::action_t::SWITCH_TO_PLAYER_8, 8); + + this->text_renderer = std::make_unique(); } Engine::~Engine() { @@ -426,6 +428,8 @@ void Engine::loop() { } } } + + this->text_renderer->render(); } glPopMatrix(); @@ -496,6 +500,10 @@ input::InputManager &Engine::get_input_manager() { return this->input_manager; } +renderer::TextRenderer *Engine::get_text_renderer() { + return this->text_renderer.get(); +} + int64_t Engine::lastframe_duration_nsec() { return this->fps_counter.nsec_lastframe; } @@ -514,7 +522,8 @@ void Engine::render_text(coord::window position, size_t size, const char *format util::vsformat(format, vl, buf); va_end(vl); - font->render_static(position.x, position.y, buf.c_str()); + this->text_renderer->set_font(font); + this->text_renderer->draw(position.x, position.y, buf); } void Engine::move_phys_camera(float x, float y, float amount) { diff --git a/libopenage/engine.h b/libopenage/engine.h index 5998bf10c7..7299ef9bc0 100644 --- a/libopenage/engine.h +++ b/libopenage/engine.h @@ -26,6 +26,7 @@ #include "util/fps.h" #include "util/profiler.h" #include "screenshot.h" +#include "renderer/text_renderer.h" namespace openage { @@ -213,6 +214,11 @@ class Engine : public ResizeHandler, public options::OptionNode { */ input::InputManager &get_input_manager(); + /** + * return this engine's text renderer. + */ + renderer::TextRenderer *get_text_renderer(); + /** * return the number of nanoseconds that have passed * for rendering the last frame. @@ -358,6 +364,8 @@ class Engine : public ResizeHandler, public options::OptionNode { * the engines profiler */ util::Profiler profiler; + + std::unique_ptr text_renderer; }; } // namespace openage diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt new file mode 100644 index 0000000000..4315643ad5 --- /dev/null +++ b/libopenage/renderer/CMakeLists.txt @@ -0,0 +1,4 @@ +add_sources(libopenage + color.cpp + text_renderer.cpp +) diff --git a/libopenage/renderer/color.cpp b/libopenage/renderer/color.cpp new file mode 100644 index 0000000000..0a89d0190a --- /dev/null +++ b/libopenage/renderer/color.cpp @@ -0,0 +1,54 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "color.h" + +namespace openage { +namespace renderer { + +Color::Color() + : + r{0}, + g{0}, + b{0}, + a{255} { + // Empty +} + +Color::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + : + r{r}, + g{g}, + b{b}, + a{a} { + // Empty +} + +Color::Color(const Color &other) + : + r{other.r}, + g{other.g}, + b{other.b}, + a{other.a} { + // Empty +} + +Color &Color::operator=(const Color &other) { + if (this != &other) { + this->r = other.r; + this->g = other.g; + this->b = other.b; + this->a = other.a; + } + + return *this; +} + +bool Color::operator==(const Color &other) const { + return this->r == other.r && this->g == other.g && this->b == other.b && this->a == other.a; +} + +bool Color::operator!=(const Color &other) const { + return this->r != other.r || this->g != other.g || this->b != other.b || this->a != other.a; +} + +}} // openage::renderer diff --git a/libopenage/renderer/color.h b/libopenage/renderer/color.h new file mode 100644 index 0000000000..b41f4e5ba2 --- /dev/null +++ b/libopenage/renderer/color.h @@ -0,0 +1,34 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_COLOR_H_ +#define OPENAGE_RENDERER_COLOR_H_ + +#include + +namespace openage { +namespace renderer { + +class Color { +public: + Color(); + + Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + + Color(const Color &other); + + Color &operator=(const Color &other); + + bool operator==(const Color &other) const; + + bool operator!=(const Color &other) const; + + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; + +}; + +}} // openage::renderer + +#endif diff --git a/libopenage/renderer/text_renderer.cpp b/libopenage/renderer/text_renderer.cpp new file mode 100644 index 0000000000..ce2986b0da --- /dev/null +++ b/libopenage/renderer/text_renderer.cpp @@ -0,0 +1,107 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "text_renderer.h" + +#include +#include + +#include "../log/log.h" +#include "../util/strings.h" +#include "../font.h" + +namespace openage { +namespace renderer { + +TextRenderer::TextRenderer() + : + current_font{nullptr}, + current_color{255, 255, 255, 255}, + is_dirty{true} { + // Empty +} + +TextRenderer::~TextRenderer() { + // Empty +} + +void TextRenderer::set_font(openage::Font *font) { + if (this->current_font == font) { + return; + } + + this->current_font = font; + this->is_dirty = true; +} + +void TextRenderer::set_color(const Color &color) { + if (this->current_color == color) { + return; + } + + this->current_color = color; + this->is_dirty = true; +} + +void TextRenderer::draw(coord::window position, const char *format, ...) { + std::string text; + va_list vl; + va_start(vl, format); + util::vsformat(format, vl, text); + va_end(vl); + + this->draw(position.x, position.y, text); +} + +void TextRenderer::draw(coord::window position, const std::string &text) { + this->draw(position.x, position.y, text); +} + +void TextRenderer::draw(int x, int y, const std::string &text) { + if (this->is_dirty) { + this->render_batches.emplace_back(this->current_font, this->current_color); + this->is_dirty = false; + } + + this->render_batches.back().passes.emplace_back(x, y, text); +} + +void TextRenderer::render() { + // Sort the batches by font + std::sort(std::begin(this->render_batches), std::end(this->render_batches), + [](const text_render_batch &a, const text_render_batch &b) -> bool { + return a.font < b.font; + }); + + // Merge consecutive batches if font and color values are same + for (auto current_batch = std::begin(this->render_batches); current_batch != std::end(this->render_batches); ) { + auto next_batch = current_batch; + next_batch++; + if (next_batch != std::end(this->render_batches) && + current_batch->font == next_batch->font && + current_batch->color == next_batch->color) { + // Merge the render passes of current and next batches and remove the next batch + std::move(std::begin(next_batch->passes), + std::end(next_batch->passes), + std::back_inserter(current_batch->passes)); + this->render_batches.erase(next_batch); + } else { + current_batch++; + } + } + + // Render all the batches + for (auto &batch : this->render_batches) { + glColor4f(batch.color.r / 255.f, + batch.color.g / 255.f, + batch.color.b / 255.f, + batch.color.a / 255.f); + for (auto &pass : batch.passes) { + batch.font->render_static(pass.x, pass.y, pass.text.c_str()); + } + } + + // Clear the render batches for next frame + this->render_batches.clear(); +} + +}} // openage::renderer diff --git a/libopenage/renderer/text_renderer.h b/libopenage/renderer/text_renderer.h new file mode 100644 index 0000000000..39cc1a54c9 --- /dev/null +++ b/libopenage/renderer/text_renderer.h @@ -0,0 +1,110 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_TEXT_RENDERER_H_ +#define OPENAGE_RENDERER_TEXT_RENDERER_H_ + +#include +#include + +#include "../coord/window.h" +#include "color.h" + +namespace openage { + +class Font; + +namespace renderer { + +class TextRenderer { + +public: + TextRenderer(); + + virtual ~TextRenderer(); + + /** + * Set the font to be used for the future text draw calls. + * + * @param font: the font to be used. + */ + void set_font(Font *font); + + /** + * Set the color to be used for the future text draw calls. + * + * @param color: the color to be used. + */ + void set_color(const Color &color); + + /** + * Draw a formatted string at the specified position. + * + * @param position: where the text should be displayed. + * @param format: the text format + */ + void draw(coord::window position, const char *format, ...); + + /** + * Draw text at the specified position. + * + * @param position: where the text should be displayed. + * @param text: the text to be displayed. + */ + void draw(coord::window position, const std::string &text); + + /** + * Draw text at the specified position. + * + * @param x: the position in x-direction. + * @param y: the position in y-direction. + * @param text: the text to be displayed. + */ + void draw(int x, int y, const std::string &text); + + /** + * Render all the text draw requests made during the frame. + */ + void render(); + +private: + /** + * A single text draw request containing the text and position. + */ + struct text_render_batch_pass { + int x; + int y; + std::string text; + + text_render_batch_pass(int x, int y, const std::string &text) + : + x{x}, + y{y}, + text{text} { + } + }; + + /** + * The set of text draw requests with the same font and color. + */ + struct text_render_batch { + openage::Font *font; + Color color; + std::vector passes; + + text_render_batch(openage::Font *font, const Color &color) + : + font{font}, + color{color} { + } + }; + + Font *current_font; + Color current_color; + bool is_dirty; + std::vector render_batches; + +}; + +}} // openage::renderer + +#endif