]> git.localhorst.tv Git - blank.git/commitdiff
implemented font redering
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 21 Jul 2015 07:00:40 +0000 (09:00 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 21 Jul 2015 07:00:40 +0000 (09:00 +0200)
16 files changed:
Makefile
TODO
building
src/app/Application.cpp
src/app/Application.hpp
src/app/init.cpp
src/app/init.hpp
src/graphics/BlendedSprite.hpp [new file with mode: 0644]
src/graphics/Font.hpp [new file with mode: 0644]
src/graphics/Format.hpp [new file with mode: 0644]
src/graphics/Texture.hpp [new file with mode: 0644]
src/graphics/render.cpp [new file with mode: 0644]
src/graphics/shader.cpp
src/model/SpriteModel.hpp [new file with mode: 0644]
src/model/model.cpp
src/ui/ui.cpp

index d0a3083a97a225c8026aa0a6c9cff8191967a7ff..cfe9a2ccfd74129f3686ad7e2daccc4b214989b9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 CXX = g++ --std=c++11
 LDXX = g++
 
-LIBS = sdl2 SDL2_image glew
+LIBS = sdl2 SDL2_image SDL2_ttf glew
 
 PKGFLAGS := $(shell pkg-config --cflags $(LIBS))
 PKGLIBS := $(shell pkg-config --libs $(LIBS))
diff --git a/TODO b/TODO
index 4025ad59c5c8cc5c367a6085c8e5de80e8d363d5..e7b2f8f3b76e6157c6fba72f85b798e04f85ee92 100644 (file)
--- a/TODO
+++ b/TODO
@@ -16,10 +16,6 @@ command line
 
        usefull for development and later on world administration
 
-font rendering
-
-       mostly for labelled blocks and some ui elements
-
 networking
 
        exchange of chunks and entities
index 392d6093df114cc68b0a102507616332826caedd..3ba784640fd7dea85c27b8c7553fe244763c139f 100644 (file)
--- a/building
+++ b/building
@@ -1,11 +1,11 @@
 Dependencies
 ============
 
-       GLEW, GLM, SDL2, SDL2_image
+       GLEW, GLM, SDL2, SDL2_image, SDL2_ttf
 
        CppUnit for tests
 
-archlinux: pacman -S glew glm sdl2 sdl2_image cppunit
+archlinux: pacman -S glew glm sdl2 sdl2_image sdl2_ttf cppunit
 
 manual:
        CppUnit http://sourceforge.net/projects/cppunit/
index a5613f9c66357e48874b9158a927b3f7b8fc26ed..80dee369ea5e053cfce2e67e25b1b6caaf7de278 100644 (file)
@@ -12,6 +12,7 @@ namespace blank {
 Application::Application(const Config &config)
 : init_sdl()
 , init_img()
+, init_ttf()
 , init_gl(config.doublebuf, config.multisampling)
 , window()
 , ctx(window.CreateContext())
index 99f0dc4fbad0b168249c20e18fc97797a19524ad..a3c62769164d9f5d7277aff0fb456caae0f5e1d2 100644 (file)
@@ -53,6 +53,7 @@ public:
 private:
        InitSDL init_sdl;
        InitIMG init_img;
+       InitTTF init_ttf;
        InitGL init_gl;
        Window window;
        GLContext ctx;
index a56674c309506b5ac8f0876be5338b8241dfebf3..6fa2dfd6c0b9722c2856de9cc528ac035fdaac7b 100644 (file)
@@ -3,6 +3,7 @@
 #include <algorithm>
 #include <SDL.h>
 #include <SDL_image.h>
+#include <SDL_ttf.h>
 #include <stdexcept>
 #include <string>
 #include <GL/glew.h>
@@ -46,6 +47,17 @@ InitIMG::~InitIMG() {
 }
 
 
+InitTTF::InitTTF() {
+       if (TTF_Init() != 0) {
+               sdl_error("TTF_Init()");
+       }
+}
+
+InitTTF::~InitTTF() {
+       TTF_Quit();
+}
+
+
 InitGL::InitGL(bool double_buffer, int sample_size) {
        if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3) != 0) {
                sdl_error("SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3)");
@@ -158,6 +170,15 @@ void GLContext::EnableBackfaceCulling() noexcept {
        glEnable(GL_CULL_FACE);
 }
 
+void GLContext::EnableAlphaBlending() noexcept {
+       glEnable(GL_BLEND);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+void GLContext::DisableAlphaBlending() noexcept {
+       glDisable(GL_BLEND);
+}
+
 void GLContext::Clear() noexcept {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 }
index 98a30ed9bcc2986c7ced3a58332033aaf0c8e58f..cd0d9be481121ee80d55b87c9cdb9eada719cff3 100644 (file)
@@ -33,6 +33,18 @@ public:
 };
 
 
+class InitTTF {
+
+public:
+       InitTTF();
+       ~InitTTF();
+
+       InitTTF(const InitTTF &) = delete;
+       InitTTF &operator =(const InitTTF &) = delete;
+
+};
+
+
 class InitGL {
 
 public:
@@ -84,6 +96,8 @@ public:
        static void EnableVSync();
        static void EnableDepthTest() noexcept;
        static void EnableBackfaceCulling() noexcept;
+       static void EnableAlphaBlending() noexcept;
+       static void DisableAlphaBlending() noexcept;
 
        static void Clear() noexcept;
        static void ClearDepthBuffer() noexcept;
diff --git a/src/graphics/BlendedSprite.hpp b/src/graphics/BlendedSprite.hpp
new file mode 100644 (file)
index 0000000..167b013
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef BLANK_GRAPHICS_BLENDEDSPRITE_HPP_
+#define BLANK_GRAPHICS_BLENDEDSPRITE_HPP_
+
+#include "Program.hpp"
+
+#include <GL/glew.h>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class Texture;
+
+class BlendedSprite {
+
+public:
+       BlendedSprite();
+
+       void Activate() noexcept;
+
+       void SetM(const glm::mat4 &m) noexcept;
+       void SetProjection(const glm::mat4 &p) noexcept;
+       void SetView(const glm::mat4 &v) noexcept;
+       void SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept;
+       void SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept;
+
+       void SetTexture(Texture &) noexcept;
+
+       const glm::mat4 &Projection() const noexcept { return projection; }
+       const glm::mat4 &View() const noexcept { return view; }
+       const glm::mat4 &GetVP() const noexcept { return vp; }
+
+private:
+       Program program;
+
+       glm::mat4 projection;
+       glm::mat4 view;
+       glm::mat4 vp;
+
+       GLuint mvp_handle;
+       GLuint sampler_handle;
+
+};
+
+}
+
+#endif
diff --git a/src/graphics/Font.hpp b/src/graphics/Font.hpp
new file mode 100644 (file)
index 0000000..2219452
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef BLANK_GRAPHICS_FONT_HPP_
+#define BLANK_GRAPHICS_FONT_HPP_
+
+#include <SDL_ttf.h>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class Texture;
+
+class Font {
+
+public:
+       Font(const char *src, int size, long index = 0);
+       ~Font();
+
+       Font(Font &&) noexcept;
+       Font &operator =(Font &&) noexcept;
+
+       Font(const Font &) = delete;
+       Font &operator =(const Font &) = delete;
+
+public:
+       bool Kerning() const noexcept;
+       void Kerning(bool) noexcept;
+
+       int Height() const noexcept;
+       int Ascent() const noexcept;
+       int Descent() const noexcept;
+       int LineSkip() const noexcept;
+
+       bool HasGlyph(Uint16) const noexcept;
+
+       glm::tvec2<int> TextSize(const char *) const;
+
+       Texture Render(const char *, SDL_Color) const;
+
+private:
+       TTF_Font *handle;
+
+};
+
+}
+
+#endif
diff --git a/src/graphics/Format.hpp b/src/graphics/Format.hpp
new file mode 100644 (file)
index 0000000..26ebfe0
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef BLANK_GRAPHICS_FORMAT_HPP_
+#define BLANK_GRAPHICS_FORMAT_HPP_
+
+#include <SDL.h>
+#include <GL/glew.h>
+
+
+namespace blank {
+
+struct Format {
+
+       GLenum format;
+       GLenum type;
+       GLenum internal;
+
+       void ReadPixelFormat(const SDL_PixelFormat &);
+
+};
+
+}
+
+#endif
diff --git a/src/graphics/Texture.hpp b/src/graphics/Texture.hpp
new file mode 100644 (file)
index 0000000..240e145
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef BLANK_GRAPHICS_TEXTURE_HPP_
+#define BLANK_GRAPHICS_TEXTURE_HPP_
+
+#include "Format.hpp"
+
+#include <GL/glew.h>
+
+struct SDL_Surface;
+
+
+namespace blank {
+
+class Texture {
+
+public:
+       Texture();
+       ~Texture();
+
+       Texture(Texture &&) noexcept;
+       Texture &operator =(Texture &&) noexcept;
+
+       Texture(const Texture &) = delete;
+       Texture &operator =(const Texture &) = delete;
+
+public:
+       GLsizei Width() const noexcept { return width; }
+       GLsizei Height() const noexcept { return height; }
+
+       void Bind() noexcept;
+
+       void Data(const SDL_Surface &, bool pad2 = true) noexcept;
+       void Data(GLsizei w, GLsizei h, const Format &, GLvoid *data) noexcept;
+
+       void FilterNearest() noexcept;
+       void FilterLinear() noexcept;
+       void FilterTrilinear() noexcept;
+
+       static void UnpackAlignment(GLint) noexcept;
+       static int UnpackAlignmentFromPitch(int) noexcept;
+       static void UnpackRowLength(GLint) noexcept;
+
+private:
+       GLuint handle;
+
+       GLsizei width, height;
+
+};
+
+}
+
+#endif
diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp
new file mode 100644 (file)
index 0000000..3d0bbd4
--- /dev/null
@@ -0,0 +1,259 @@
+#include "Font.hpp"
+#include "Format.hpp"
+#include "Texture.hpp"
+
+#include <algorithm>
+#include <cstring>
+#include <memory>
+#include <stdexcept>
+
+
+namespace blank {
+
+Font::Font(const char *src, int size, long index)
+: handle(TTF_OpenFontIndex(src, size, index)) {
+       if (!handle) {
+               throw std::runtime_error(TTF_GetError());
+       }
+}
+
+Font::~Font() {
+       if (handle) {
+               TTF_CloseFont(handle);
+       }
+}
+
+Font::Font(Font &&other) noexcept
+: handle(other.handle) {
+       other.handle = nullptr;
+}
+
+Font &Font::operator =(Font &&other) noexcept {
+       std::swap(handle, other.handle);
+       return *this;
+}
+
+
+bool Font::Kerning() const noexcept {
+       return TTF_GetFontKerning(handle);
+}
+
+void Font::Kerning(bool b) noexcept {
+       TTF_SetFontKerning(handle, b);
+}
+
+
+int Font::Height() const noexcept {
+       return TTF_FontHeight(handle);
+}
+
+int Font::Ascent() const noexcept {
+       return TTF_FontAscent(handle);
+}
+
+int Font::Descent() const noexcept {
+       return TTF_FontDescent(handle);
+}
+
+int Font::LineSkip() const noexcept {
+       return TTF_FontLineSkip(handle);
+}
+
+
+bool Font::HasGlyph(Uint16 c) const noexcept {
+       return TTF_GlyphIsProvided(handle, c);
+}
+
+
+glm::tvec2<int> Font::TextSize(const char *text) const {
+       glm::tvec2<int> size;
+       if (TTF_SizeUTF8(handle, text, &size.x, &size.y) != 0) {
+               throw std::runtime_error(TTF_GetError());
+       }
+       return size;
+}
+
+Texture Font::Render(const char *text, SDL_Color color) const {
+       SDL_Surface *srf = TTF_RenderUTF8_Blended(handle, text, color);
+       if (!srf) {
+               throw std::runtime_error(TTF_GetError());
+       }
+       Texture tex;
+       tex.Bind();
+       tex.Data(*srf, false);
+       tex.FilterLinear();
+       SDL_FreeSurface(srf);
+       return tex;
+}
+
+
+void Format::ReadPixelFormat(const SDL_PixelFormat &fmt) {
+       if (fmt.BytesPerPixel == 4) {
+               if (fmt.Amask == 0xFF) {
+                       if (fmt.Rmask == 0xFF00) {
+                               format = GL_BGRA;
+                       } else {
+                               format = GL_RGBA;
+                       }
+                       type = GL_UNSIGNED_INT_8_8_8_8;
+               } else {
+                       if (fmt.Rmask == 0xFF) {
+                               format = GL_RGBA;
+                       } else {
+                               format = GL_BGRA;
+                       }
+                       type = GL_UNSIGNED_INT_8_8_8_8_REV;
+               }
+               internal = GL_RGBA8;
+       } else {
+               if (fmt.Rmask == 0xFF) {
+                       format = GL_RGB;
+               } else {
+                       format = GL_BGR;
+               }
+               type = GL_UNSIGNED_BYTE;
+               internal = GL_RGB8;
+       }
+}
+
+
+Texture::Texture()
+: handle(0)
+, width(0)
+, height(0) {
+       glGenTextures(1, &handle);
+}
+
+Texture::~Texture() {
+       if (handle != 0) {
+               glDeleteTextures(1, &handle);
+       }
+}
+
+Texture::Texture(Texture &&other) noexcept
+: handle(other.handle) {
+       other.handle = 0;
+       width = other.width;
+       height = other.height;
+}
+
+Texture &Texture::operator =(Texture &&other) noexcept {
+       std::swap(handle, other.handle);
+       width = other.width;
+       height = other.height;
+       return *this;
+}
+
+
+void Texture::Bind() noexcept {
+       glBindTexture(GL_TEXTURE_2D, handle);
+}
+
+namespace {
+       bool ispow2(unsigned int i) {
+               // don't care about i == 0 here
+               return !(i & (i - 1));
+       }
+}
+
+void Texture::Data(const SDL_Surface &srf, bool pad2) noexcept {
+       Format format;
+       format.ReadPixelFormat(*srf.format);
+
+       if (!pad2 || (ispow2(srf.w) && ispow2(srf.h))) {
+               int align = UnpackAlignmentFromPitch(srf.pitch);
+
+               int pitch = (srf.w * srf.format->BytesPerPixel + align - 1) / align * align;
+               if (srf.pitch - pitch >= align) {
+                       UnpackRowLength(srf.pitch / srf.format->BytesPerPixel);
+               } else {
+                       UnpackRowLength(0);
+               }
+
+               Data(srf.w, srf.h, format, srf.pixels);
+
+               UnpackRowLength(0);
+       } else if (srf.w > (1 << 30) || srf.h > (1 << 30)) {
+#ifndef NDEBUG
+               throw std::runtime_error("texture too large");
+#endif
+       } else {
+               GLsizei width = 1, height = 1;
+               while (width < srf.w) {
+                       width <<= 1;
+               }
+               while (height < srf.h) {
+                       height <<= 1;
+               }
+               size_t pitch = width * srf.format->BytesPerPixel;
+               size_t size = pitch * height;
+               size_t row_pad = pitch - srf.pitch;
+               std::unique_ptr<unsigned char[]> data(new unsigned char[size]);
+               unsigned char *src = reinterpret_cast<unsigned char *>(srf.pixels);
+               unsigned char *dst = data.get();
+               for (int row = 0; row < srf.h; ++row) {
+                       std::memcpy(dst, src, srf.pitch);
+                       src += srf.pitch;
+                       dst += srf.pitch;
+                       std::memset(dst, 0, row_pad);
+                       dst += row_pad;
+               }
+               std::memset(dst, 0, (height - srf.h) * pitch);
+               UnpackAlignmentFromPitch(pitch);
+               Data(width, height, format, data.get());
+       }
+
+       UnpackAlignment(4);
+}
+
+void Texture::Data(GLsizei w, GLsizei h, const Format &format, GLvoid *data) noexcept {
+       glTexImage2D(
+               GL_TEXTURE_2D,
+               0, format.internal,
+               w, h,
+               0,
+               format.format, format.type,
+               data
+       );
+       width = w;
+       height = h;
+}
+
+
+void Texture::FilterNearest() noexcept {
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+}
+
+void Texture::FilterLinear() noexcept {
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+}
+
+void Texture::FilterTrilinear() noexcept {
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+       glGenerateMipmap(GL_TEXTURE_2D);
+}
+
+
+void Texture::UnpackAlignment(GLint i) noexcept {
+       glPixelStorei(GL_UNPACK_ALIGNMENT, i);
+}
+
+int Texture::UnpackAlignmentFromPitch(int pitch) noexcept {
+       int align = 8;
+       while (pitch % align) {
+               align >>= 1;
+       }
+       UnpackAlignment(align);
+       return align;
+}
+
+void Texture::UnpackRowLength(GLint i) noexcept {
+       glPixelStorei(GL_UNPACK_ROW_LENGTH, i);
+}
+
+}
index 45634a9896a5b05f217f60475d0d0db0f78017b4..41c1faf57f33be1cfdec0b90a05c767e3f0486a6 100644 (file)
@@ -1,8 +1,10 @@
+#include "BlendedSprite.hpp"
 #include "BlockLighting.hpp"
 #include "DirectionalLighting.hpp"
 #include "Program.hpp"
 #include "Shader.hpp"
 
+#include "Texture.hpp"
 #include "../app/init.hpp"
 
 #include <algorithm>
@@ -319,6 +321,7 @@ BlockLighting::BlockLighting()
 void BlockLighting::Activate() noexcept {
        GLContext::EnableDepthTest();
        GLContext::EnableBackfaceCulling();
+       GLContext::DisableAlphaBlending();
        program.Use();
 }
 
@@ -355,4 +358,80 @@ void BlockLighting::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::ma
        SetM(m);
 }
 
+
+BlendedSprite::BlendedSprite()
+: program()
+, vp(1.0f)
+, mvp_handle(0)
+, sampler_handle(0) {
+       program.LoadShader(
+               GL_VERTEX_SHADER,
+               "#version 330 core\n"
+               "layout(location = 0) in vec3 vtx_position;\n"
+               "layout(location = 1) in vec2 vtx_tex_uv;\n"
+               "uniform mat4 MVP;\n"
+               "out vec2 frag_tex_uv;\n"
+               "void main() {\n"
+                       "gl_Position = MVP * vec4(vtx_position, 1);\n"
+                       "frag_tex_uv = vtx_tex_uv;\n"
+               "}\n"
+       );
+       program.LoadShader(
+               GL_FRAGMENT_SHADER,
+               "#version 330 core\n"
+               "in vec2 frag_tex_uv;\n"
+               "uniform sampler2D tex_sampler;\n"
+               "out vec4 color;\n"
+               "void main() {\n"
+                       "color = texture(tex_sampler, frag_tex_uv);\n"
+               "}\n"
+       );
+       program.Link();
+       if (!program.Linked()) {
+               program.Log(std::cerr);
+               throw std::runtime_error("link program");
+       }
+
+       mvp_handle = program.UniformLocation("MVP");
+       sampler_handle = program.UniformLocation("tex_sampler");
+}
+
+
+void BlendedSprite::Activate() noexcept {
+       GLContext::EnableAlphaBlending();
+       program.Use();
+}
+
+void BlendedSprite::SetM(const glm::mat4 &m) noexcept {
+       glm::mat4 mvp(vp * m);
+       glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
+}
+
+void BlendedSprite::SetProjection(const glm::mat4 &p) noexcept {
+       projection = p;
+       vp = p * view;
+}
+
+void BlendedSprite::SetView(const glm::mat4 &v) noexcept {
+       view = v;
+       vp = projection * v;
+}
+
+void BlendedSprite::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
+       projection = p;
+       view = v;
+       vp = p * v;
+}
+
+void BlendedSprite::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
+       SetVP(v, p);
+       SetM(m);
+}
+
+void BlendedSprite::SetTexture(Texture &tex) noexcept {
+       glActiveTexture(GL_TEXTURE0);
+       tex.Bind();
+       glUniform1i(sampler_handle, 0);
+}
+
 }
diff --git a/src/model/SpriteModel.hpp b/src/model/SpriteModel.hpp
new file mode 100644 (file)
index 0000000..d661501
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef BLANK_MODEL_SPRITEMODEL_HPP_
+#define BLANK_MODEL_SPRITEMODEL_HPP_
+
+#include <vector>
+#include <GL/glew.h>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class SpriteModel {
+
+public:
+       using Position = glm::vec3;
+       using TexCoord = glm::vec2;
+       using Index = unsigned short;
+
+       using Positions = std::vector<Position>;
+       using TexCoords = std::vector<TexCoord>;
+       using Indices = std::vector<Index>;
+
+public:
+       Positions vertices;
+       TexCoords coords;
+       Indices indices;
+
+public:
+       SpriteModel() noexcept;
+       ~SpriteModel() noexcept;
+
+       SpriteModel(const SpriteModel &) = delete;
+       SpriteModel &operator =(const SpriteModel &) = delete;
+
+       void Invalidate() noexcept { dirty = true; }
+
+       void Clear() noexcept;
+       void Reserve(int vtx_count, int idx_count);
+
+       void LoadRect(
+               float w, float h,
+               const glm::vec2 &pivot = glm::vec2(0.0f),
+               const glm::vec2 &tex_begin = glm::vec2(0.0f),
+               const glm::vec2 &tex_end = glm::vec2(1.0f, 1.0f)
+       );
+
+       void Draw() noexcept;
+
+private:
+       void Update() noexcept;
+
+private:
+       enum Attribute {
+               ATTRIB_VERTEX,
+               ATTRIB_TEXCOORD,
+               ATTRIB_INDEX,
+               ATTRIB_COUNT,
+       };
+
+       GLuint va;
+       GLuint handle[ATTRIB_COUNT];
+       bool dirty;
+
+};
+
+}
+
+#endif
index 423e00d842150202a6948cc6ec3ad4be879a12b9..4e000806cff56c2812f9aaa42fc459c642cc29ca 100644 (file)
@@ -1,6 +1,7 @@
 #include "BlockModel.hpp"
 #include "Model.hpp"
 #include "OutlineModel.hpp"
+#include "SpriteModel.hpp"
 
 #include <iostream>
 
@@ -287,4 +288,114 @@ void OutlineModel::Draw() noexcept {
        );
 }
 
+
+SpriteModel::SpriteModel() noexcept
+: vertices()
+, coords()
+, indices()
+, va(0)
+, handle{}
+, dirty(false) {
+       glGenVertexArrays(1, &va);
+       glGenBuffers(ATTRIB_COUNT, handle);
+}
+
+SpriteModel::~SpriteModel() noexcept {
+       glDeleteBuffers(ATTRIB_COUNT, handle);
+       glDeleteVertexArrays(1, &va);
+}
+
+
+void SpriteModel::Clear() noexcept {
+       vertices.clear();
+       coords.clear();
+       indices.clear();
+       Invalidate();
+}
+
+void SpriteModel::Reserve(int v, int i) {
+       vertices.reserve(v);
+       coords.reserve(v);
+       indices.reserve(i);
+}
+
+
+void SpriteModel::LoadRect(
+       float w, float h,
+       const glm::vec2 &pivot,
+       const glm::vec2 &tex_begin,
+       const glm::vec2 &tex_end
+) {
+       Clear();
+       Reserve(4, 6);
+
+       vertices.emplace_back( -pivot.x,  -pivot.y, 0.0f);
+       vertices.emplace_back(w-pivot.x,  -pivot.y, 0.0f);
+       vertices.emplace_back( -pivot.x, h-pivot.y, 0.0f);
+       vertices.emplace_back(w-pivot.x, h-pivot.y, 0.0f);
+
+       coords.emplace_back(tex_begin.x, tex_begin.y);
+       coords.emplace_back(tex_end.x,   tex_begin.y);
+       coords.emplace_back(tex_begin.x, tex_end.y);
+       coords.emplace_back(tex_end.x,   tex_end.y);
+
+       indices.assign({ 0, 2, 1, 1, 2, 3 });
+
+       Invalidate();
+}
+
+
+void SpriteModel::Update() noexcept {
+       glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_VERTEX]);
+       glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Position), vertices.data(), GL_STATIC_DRAW);
+       glEnableVertexAttribArray(ATTRIB_VERTEX);
+       glVertexAttribPointer(
+               ATTRIB_VERTEX, // location (for shader)
+               3,             // size
+               GL_FLOAT,      // type
+               GL_FALSE,      // normalized
+               0,             // stride
+               nullptr        // offset
+       );
+
+#ifndef NDEBUG
+       if (coords.size() < vertices.size()) {
+               std::cerr << "SpriteModel: not enough coords!" << std::endl;
+               coords.resize(vertices.size(), { 1, 1 });
+       }
+#endif
+       glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_TEXCOORD]);
+       glBufferData(GL_ARRAY_BUFFER, coords.size() * sizeof(TexCoord), coords.data(), GL_STATIC_DRAW);
+       glEnableVertexAttribArray(ATTRIB_TEXCOORD);
+       glVertexAttribPointer(
+               ATTRIB_TEXCOORD, // location (for shader)
+               2,               // size
+               GL_FLOAT,        // type
+               GL_FALSE,        // normalized
+               0,               // stride
+               nullptr          // offset
+       );
+
+       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle[ATTRIB_INDEX]);
+       glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(Index), indices.data(), GL_STATIC_DRAW);
+
+       dirty = false;
+}
+
+
+void SpriteModel::Draw() noexcept {
+       glBindVertexArray(va);
+
+       if (dirty) {
+               Update();
+       }
+
+       glDrawElements(
+               GL_TRIANGLES,      // how
+               indices.size(),    // count
+               GL_UNSIGNED_SHORT, // type
+               nullptr            // offset
+       );
+}
+
 }
index ea9786b0382952c4aea69a5ac29b1cd0e7d8ed00..8ddd40c57fb8327a32c5097e890f5d4b6093e326 100644 (file)
@@ -63,16 +63,20 @@ void HUD::Display(const Block &b) {
 
 
 void HUD::Render(DirectionalLighting &program) noexcept {
+       program.SetLightDirection({ 1.0f, 3.0f, 5.0f });
+       // disable distance fog
+       program.SetFogDensity(0.0f);
+       GLContext::ClearDepthBuffer();
+
+       program.SetVP(view, projection);
+
        if (block_visible) {
-               program.SetLightDirection({ 1.0f, 3.0f, 5.0f });
-               // disable distance fog
-               program.SetFogDensity(0.0f);
-               GLContext::ClearDepthBuffer();
-               program.SetMVP(block_transform, view, projection);
+               program.SetM(block_transform);
                block.Draw();
-               program.SetM(crosshair_transform);
-               crosshair.Draw();
        }
+
+       program.SetM(crosshair_transform);
+       crosshair.Draw();
 }