]> git.localhorst.tv Git - blank.git/blobdiff - src/graphics/render.cpp
move common exceptions to app/error
[blank.git] / src / graphics / render.cpp
index 747bfa2dbee75a01d88e14bf55255f15007289c5..f0ead9ba78073f602d40bac8ffb6da04575ecf05 100644 (file)
@@ -1,14 +1,16 @@
-#include "BlendedSprite.hpp"
-#include "FixedText.hpp"
+#include "ArrayTexture.hpp"
+#include "CubeMap.hpp"
 #include "Font.hpp"
 #include "Format.hpp"
-#include "MessageBox.hpp"
-#include "Text.hpp"
 #include "Texture.hpp"
+#include "TextureBase.hpp"
 #include "Viewport.hpp"
 
+#include "../app/error.hpp"
+
 #include <algorithm>
 #include <cstring>
+#include <iostream>
 #include <memory>
 #include <stdexcept>
 
@@ -18,7 +20,7 @@ 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());
+               throw TTFError("TTF_OpenFontIndex");
        }
 }
 
@@ -104,10 +106,10 @@ bool Font::HasGlyph(Uint16 c) const noexcept {
 }
 
 
-glm::tvec2<int> Font::TextSize(const char *text) const {
-       glm::tvec2<int> size;
+glm::ivec2 Font::TextSize(const char *text) const {
+       glm::ivec2 size;
        if (TTF_SizeUTF8(handle, text, &size.x, &size.y) != 0) {
-               throw std::runtime_error(TTF_GetError());
+               throw TTFError("TTF_SizeUTF8");
        }
        return size;
 }
@@ -121,7 +123,7 @@ Texture Font::Render(const char *text) const {
 void Font::Render(const char *text, Texture &tex) const {
        SDL_Surface *srf = TTF_RenderUTF8_Blended(handle, text, { 0xFF, 0xFF, 0xFF, 0xFF });
        if (!srf) {
-               throw std::runtime_error(TTF_GetError());
+               throw TTFError("TTF_RenderUTF8_Blended");
        }
        tex.Bind();
        tex.Data(*srf, false);
@@ -129,8 +131,32 @@ void Font::Render(const char *text, Texture &tex) const {
        SDL_FreeSurface(srf);
 }
 
-
-void Format::ReadPixelFormat(const SDL_PixelFormat &fmt) {
+Format::Format() noexcept
+: format(GL_BGRA)
+, type(GL_UNSIGNED_INT_8_8_8_8_REV)
+, internal(GL_RGBA8) {
+       sdl_format.format = SDL_PIXELFORMAT_ARGB8888;
+       sdl_format.palette = nullptr;
+       sdl_format.BitsPerPixel = 32;
+       sdl_format.BytesPerPixel = 4;
+       sdl_format.Rmask = 0x00FF0000;
+       sdl_format.Gmask = 0x0000FF00;
+       sdl_format.Bmask = 0x000000FF;
+       sdl_format.Amask = 0xFF000000;
+       sdl_format.Rloss = 0;
+       sdl_format.Gloss = 0;
+       sdl_format.Bloss = 0;
+       sdl_format.Aloss = 0;
+       sdl_format.Rshift = 16;
+       sdl_format.Gshift = 8;
+       sdl_format.Bshift = 0;
+       sdl_format.Ashift = 24;
+       sdl_format.refcount = 1;
+       sdl_format.next = nullptr;
+}
+
+Format::Format(const SDL_PixelFormat &fmt) noexcept
+: sdl_format(fmt) {
        if (fmt.BytesPerPixel == 4) {
                if (fmt.Amask == 0xFF) {
                        if (fmt.Rmask == 0xFF00) {
@@ -159,137 +185,59 @@ void Format::ReadPixelFormat(const SDL_PixelFormat &fmt) {
        }
 }
 
-
-MessageBox::MessageBox(const Font &f)
-: font(f)
-, lines()
-, max_lines(10)
-, pos(0.0f)
-, adv(0.0f, font.LineSkip(), 0.0f)
-, bg(1.0f, 1.0f, 1.0f, 0.0f)
-, fg(1.0f, 1.0f, 1.0f, 1.0f)
-, grav(Gravity::NORTH_WEST) {
-
+bool Format::Compatible(const Format &other) const noexcept {
+       return format == other.format && type == other.type && internal == other.internal;
 }
 
-void MessageBox::Position(const glm::vec3 &p, Gravity g) noexcept {
-       pos = p;
-       grav = g;
-       if (get_y(g) == Align::END) {
-               adv.y = -font.LineSkip();
-       } else {
-               adv.y = font.LineSkip();
-       }
-       for (Text &txt : lines) {
-               txt.Pivot(g);
-       }
-}
-
-void MessageBox::PushLine(const char *text) {
-       lines.emplace_front();
-       Text &txt = lines.front();
-       txt.Set(font, text);
-       txt.Pivot(grav);
 
-       while (lines.size() > max_lines) {
-               lines.pop_back();
-       }
+template<GLenum TARGET, GLsizei COUNT>
+TextureBase<TARGET, COUNT>::TextureBase() {
+       glGenTextures(COUNT, handle);
 }
 
-void MessageBox::Render(Viewport &viewport) noexcept {
-       BlendedSprite &prog = viewport.SpriteProgram();
-       prog.SetBG(bg);
-       prog.SetFG(fg);
-       viewport.SetCursor(pos, grav);
-       for (Text &txt : lines) {
-               prog.SetM(viewport.Cursor());
-               txt.Render(viewport);
-               viewport.MoveCursor(adv);
-       }
+template<GLenum TARGET, GLsizei COUNT>
+TextureBase<TARGET, COUNT>::~TextureBase() {
+       glDeleteTextures(COUNT, handle);
 }
 
-
-Text::Text() noexcept
-: tex()
-, sprite()
-, size(0.0f)
-, pivot(Gravity::NORTH_WEST)
-, dirty(false) {
-
+template<GLenum TARGET, GLsizei COUNT>
+TextureBase<TARGET, COUNT>::TextureBase(TextureBase &&other) noexcept {
+       std::memcpy(handle, other.handle, sizeof(handle));
+       std::memset(other.handle, 0, sizeof(handle));
 }
 
-FixedText::FixedText() noexcept
-: Text()
-, bg(1.0f, 1.0f, 1.0f, 0.0f)
-, fg(1.0f, 1.0f, 1.0f, 1.0f)
-, pos(0.0f)
-, grav(Gravity::NORTH_WEST)
-, visible(false) {
-
-}
-
-void Text::Set(const Font &font, const char *text) {
-       font.Render(text, tex);
-       size = font.TextSize(text);
-       dirty = true;
-}
-
-void Text::Update() {
-       sprite.LoadRect(size.x, size.y, align(pivot, size));
-       dirty = false;
-}
-
-void FixedText::Render(Viewport &viewport) noexcept {
-       BlendedSprite &prog = viewport.SpriteProgram();
-       viewport.SetCursor(pos, grav);
-       prog.SetM(viewport.Cursor());
-       prog.SetBG(bg);
-       prog.SetFG(fg);
-       Text::Render(viewport);
-}
-
-void Text::Render(Viewport &viewport) noexcept {
-       if (dirty) {
-               Update();
-       }
-       BlendedSprite &prog = viewport.SpriteProgram();
-       prog.SetTexture(tex);
-       sprite.Draw();
+template<GLenum TARGET, GLsizei COUNT>
+TextureBase<TARGET, COUNT> &TextureBase<TARGET, COUNT>::operator =(TextureBase &&other) noexcept {
+       std::swap(handle, other.handle);
+       return *this;
 }
 
 
 Texture::Texture()
-: handle(0)
+: TextureBase()
 , 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;
+: TextureBase(std::move(other)) {
        width = other.width;
        height = other.height;
 }
 
 Texture &Texture::operator =(Texture &&other) noexcept {
-       std::swap(handle, other.handle);
+       TextureBase::operator =(std::move(other));
        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
@@ -298,8 +246,7 @@ namespace {
 }
 
 void Texture::Data(const SDL_Surface &srf, bool pad2) noexcept {
-       Format format;
-       format.ReadPixelFormat(*srf.format);
+       Format format(*srf.format);
 
        if (!pad2 || (ispow2(srf.w) && ispow2(srf.h))) {
                int align = UnpackAlignmentFromPitch(srf.pitch);
@@ -315,8 +262,11 @@ void Texture::Data(const SDL_Surface &srf, bool pad2) noexcept {
 
                UnpackRowLength(0);
        } else if (srf.w > (1 << 30) || srf.h > (1 << 30)) {
+               // That's at least one gigapixel in either or both dimensions.
+               // If this is not an error, that's an insanely large or high
+               // resolution texture.
 #ifndef NDEBUG
-               throw std::runtime_error("texture too large");
+               std::cerr << "texture size exceeds 2^30, aborting data import" << std::endl;
 #endif
        } else {
                GLsizei width = 1, height = 1;
@@ -361,25 +311,6 @@ void Texture::Data(GLsizei w, GLsizei h, const Format &format, GLvoid *data) noe
 }
 
 
-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);
 }
@@ -397,4 +328,140 @@ void Texture::UnpackRowLength(GLint i) noexcept {
        glPixelStorei(GL_UNPACK_ROW_LENGTH, i);
 }
 
+
+ArrayTexture::ArrayTexture()
+: TextureBase()
+, width(0)
+, height(0)
+, depth(0) {
+
+}
+
+ArrayTexture::~ArrayTexture() {
+
+}
+
+ArrayTexture::ArrayTexture(ArrayTexture &&other) noexcept
+: TextureBase(std::move(other)) {
+       width = other.width;
+       height = other.height;
+       depth = other.depth;
+}
+
+ArrayTexture &ArrayTexture::operator =(ArrayTexture &&other) noexcept {
+       TextureBase::operator =(std::move(other));
+       width = other.width;
+       height = other.height;
+       depth = other.depth;
+       return *this;
+}
+
+
+void ArrayTexture::Reserve(GLsizei w, GLsizei h, GLsizei d, const Format &f) noexcept {
+       glTexStorage3D(
+               GL_TEXTURE_2D_ARRAY, // which
+               1,                   // mipmap count
+               f.internal,          // format
+               w, h,                // dimensions
+               d                    // layer count
+       );
+       width = w;
+       height = h;
+       depth = d;
+       format = f;
+}
+
+void ArrayTexture::Data(GLsizei l, const SDL_Surface &srf) {
+       Format fmt(*srf.format);
+       if (format.Compatible(fmt)) {
+               Data(l, fmt, srf.pixels);
+       } else {
+               SDL_Surface *converted = SDL_ConvertSurface(
+                       const_cast<SDL_Surface *>(&srf),
+                       &format.sdl_format,
+                       0
+               );
+               if (!converted) {
+                       throw SDLError("SDL_ConvertSurface");
+               }
+               Format new_fmt(*converted->format);
+               if (!format.Compatible(new_fmt)) {
+                       SDL_FreeSurface(converted);
+                       throw std::runtime_error("unable to convert texture input");
+               }
+               Data(l, new_fmt, converted->pixels);
+               SDL_FreeSurface(converted);
+       }
+}
+
+void ArrayTexture::Data(GLsizei l, const Format &f, GLvoid *data) noexcept {
+       glTexSubImage3D(
+               GL_TEXTURE_2D_ARRAY, // which
+               0,                   // mipmap lavel
+               0, 0,                // dest X and Y offset
+               l,                   // layer offset
+               width, height,
+               1,                   // layer count
+               f.format, f.type,
+               data
+       );
+}
+
+
+CubeMap::CubeMap()
+: TextureBase() {
+
+}
+
+CubeMap::~CubeMap() {
+
+}
+
+CubeMap::CubeMap(CubeMap &&other) noexcept
+: TextureBase(std::move(other)) {
+
+}
+
+CubeMap &CubeMap::operator =(CubeMap &&other) noexcept {
+       TextureBase::operator =(std::move(other));
+       return *this;
+}
+
+
+void CubeMap::Data(Face f, const SDL_Surface &srf) {
+       Format format;
+       Format fmt(*srf.format);
+       if (format.Compatible(fmt)) {
+               Data(f, srf.w, srf.h, fmt, srf.pixels);
+       } else {
+               SDL_Surface *converted = SDL_ConvertSurface(
+                       const_cast<SDL_Surface *>(&srf),
+                       &format.sdl_format,
+                       0
+               );
+               if (!converted) {
+                       throw SDLError("SDL_ConvertSurface");
+               }
+               Format new_fmt(*converted->format);
+               if (!format.Compatible(new_fmt)) {
+                       SDL_FreeSurface(converted);
+                       throw std::runtime_error("unable to convert texture input");
+               }
+               Data(f, converted->w, converted->h, new_fmt, converted->pixels);
+               SDL_FreeSurface(converted);
+       }
+}
+
+void CubeMap::Data(Face face, GLsizei w, GLsizei h, const Format &f, GLvoid *data) noexcept {
+       glTexImage2D(
+               face,             // which
+               0,                // mipmap level
+               f.internal,       // internal format
+               w, h,             // size
+               0,                // border
+               f.format, f.type, // pixel format
+               data              // pixel data
+       );
+}
+
 }