From: Daniel Karbach Date: Tue, 21 Jul 2015 07:00:40 +0000 (+0200) Subject: implemented font redering X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=282d731ea8f10342efa82012028de7043b3dd639;p=blank.git implemented font redering --- diff --git a/Makefile b/Makefile index d0a3083..cfe9a2c 100644 --- 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 4025ad5..e7b2f8f 100644 --- 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 diff --git a/building b/building index 392d609..3ba7846 100644 --- 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/ diff --git a/src/app/Application.cpp b/src/app/Application.cpp index a5613f9..80dee36 100644 --- a/src/app/Application.cpp +++ b/src/app/Application.cpp @@ -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()) diff --git a/src/app/Application.hpp b/src/app/Application.hpp index 99f0dc4..a3c6276 100644 --- a/src/app/Application.hpp +++ b/src/app/Application.hpp @@ -53,6 +53,7 @@ public: private: InitSDL init_sdl; InitIMG init_img; + InitTTF init_ttf; InitGL init_gl; Window window; GLContext ctx; diff --git a/src/app/init.cpp b/src/app/init.cpp index a56674c..6fa2dfd 100644 --- a/src/app/init.cpp +++ b/src/app/init.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -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); } diff --git a/src/app/init.hpp b/src/app/init.hpp index 98a30ed..cd0d9be 100644 --- a/src/app/init.hpp +++ b/src/app/init.hpp @@ -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 index 0000000..167b013 --- /dev/null +++ b/src/graphics/BlendedSprite.hpp @@ -0,0 +1,47 @@ +#ifndef BLANK_GRAPHICS_BLENDEDSPRITE_HPP_ +#define BLANK_GRAPHICS_BLENDEDSPRITE_HPP_ + +#include "Program.hpp" + +#include +#include + + +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 index 0000000..2219452 --- /dev/null +++ b/src/graphics/Font.hpp @@ -0,0 +1,46 @@ +#ifndef BLANK_GRAPHICS_FONT_HPP_ +#define BLANK_GRAPHICS_FONT_HPP_ + +#include +#include + + +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 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 index 0000000..26ebfe0 --- /dev/null +++ b/src/graphics/Format.hpp @@ -0,0 +1,22 @@ +#ifndef BLANK_GRAPHICS_FORMAT_HPP_ +#define BLANK_GRAPHICS_FORMAT_HPP_ + +#include +#include + + +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 index 0000000..240e145 --- /dev/null +++ b/src/graphics/Texture.hpp @@ -0,0 +1,51 @@ +#ifndef BLANK_GRAPHICS_TEXTURE_HPP_ +#define BLANK_GRAPHICS_TEXTURE_HPP_ + +#include "Format.hpp" + +#include + +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 index 0000000..3d0bbd4 --- /dev/null +++ b/src/graphics/render.cpp @@ -0,0 +1,259 @@ +#include "Font.hpp" +#include "Format.hpp" +#include "Texture.hpp" + +#include +#include +#include +#include + + +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 Font::TextSize(const char *text) const { + glm::tvec2 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 data(new unsigned char[size]); + unsigned char *src = reinterpret_cast(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); +} + +} diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp index 45634a9..41c1faf 100644 --- a/src/graphics/shader.cpp +++ b/src/graphics/shader.cpp @@ -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 @@ -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 index 0000000..d661501 --- /dev/null +++ b/src/model/SpriteModel.hpp @@ -0,0 +1,67 @@ +#ifndef BLANK_MODEL_SPRITEMODEL_HPP_ +#define BLANK_MODEL_SPRITEMODEL_HPP_ + +#include +#include +#include + + +namespace blank { + +class SpriteModel { + +public: + using Position = glm::vec3; + using TexCoord = glm::vec2; + using Index = unsigned short; + + using Positions = std::vector; + using TexCoords = std::vector; + using Indices = std::vector; + +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 diff --git a/src/model/model.cpp b/src/model/model.cpp index 423e00d..4e00080 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -1,6 +1,7 @@ #include "BlockModel.hpp" #include "Model.hpp" #include "OutlineModel.hpp" +#include "SpriteModel.hpp" #include @@ -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 + ); +} + } diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp index ea9786b..8ddd40c 100644 --- a/src/ui/ui.cpp +++ b/src/ui/ui.cpp @@ -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(); }