From 9d7dd452c71ad4cf778684ba21c0df1d48b6545b Mon Sep 17 00:00:00 2001 From: Matthias Blankertz Date: Mon, 9 Mar 2015 01:11:13 +0100 Subject: [PATCH] WIP: GUI Toolkit --- LinearLayout.cc | 77 +++++++++++ LinearLayout.hh | 27 ++++ Makefile | 4 +- Object.cc | 53 +++---- Object.hh | 12 +- Overlay.cc | 36 ++--- Overlay.hh | 15 +- TextWidget.cc | 84 ++++++++++++ TextWidget.hh | 37 +++++ VBOManager.cc | 16 +++ VBOManager.hh | 18 ++- View.cc | 109 +++++++++++++++ View.hh | 51 +++++++ Widget.cc | 274 +++++++++++++++++++++++++++++++++++++ Widget.hh | 114 +++++++++++++++ common.hh | 46 +++++++ main.cc | 113 ++++++++++----- shaders.cc | 20 +-- shaders.hh | 6 +- shaders/color.vs | 4 +- shaders/overlay.vs | 4 +- texture.cc | 15 +- texture.hh | 2 +- textures/button_100x30.png | Bin 0 -> 281 bytes 24 files changed, 1003 insertions(+), 134 deletions(-) create mode 100644 LinearLayout.cc create mode 100644 LinearLayout.hh create mode 100644 TextWidget.cc create mode 100644 TextWidget.hh create mode 100644 View.cc create mode 100644 View.hh create mode 100644 Widget.cc create mode 100644 Widget.hh create mode 100644 textures/button_100x30.png diff --git a/LinearLayout.cc b/LinearLayout.cc new file mode 100644 index 0000000..fb2eb6d --- /dev/null +++ b/LinearLayout.cc @@ -0,0 +1,77 @@ + +#include "LinearLayout.hh" + +LinearLayout::LinearLayout(int direction, int width, int height, std::string name) + : View(width, height, std::move(name)), direction_(direction) +{ +} + +LinearLayout::~LinearLayout() +{ +} + +void LinearLayout::setChildPadding(int pad) +{ + if (pad != padChildren_) { + padChildren_ = pad; + + layout(); + } +} + +void LinearLayout::layout() +{ + // TODO: Respect parent padding + if ((width_ == MATCH_PARENT) && parent_) + realWidth_ = parent_->getRealWidth(); + + if ((height_ == MATCH_PARENT) && parent_) + realHeight_ = parent_->getRealHeight(); + + if (direction_ == DIR_VERTICAL) { + int contentWidth = 0; + + if (width_ == WRAP_CONTENT) { + // Determine content width + for(auto& ent : children_) { + auto& child = *(std::get<1>(ent)); + assert(child.getHeight() != MATCH_PARENT); + if (child.getWidth() == MATCH_PARENT) + continue; + if (child.getWidth() == WRAP_CONTENT) + _layout(child); + contentWidth = std::max(contentWidth, child.getRealWidth()); + } + assert(contentWidth > 0); + realWidth_ = contentWidth + padLeft_ + padRight_; + printf("Inner width: %d, outer width: %d\n", contentWidth, realWidth_); + } + + int currentY = padTop_; + + // Layout children + for(auto& ent : children_) { + auto& child = *(std::get<1>(ent)); + if (child.getWidth() == MATCH_PARENT) + _layout(child); + + SDL_Rect cr; + cr.x = padLeft_; + cr.y = currentY; + if ((realWidth_ - padLeft_ - padRight_) > child.getRealWidth()) + cr.w = child.getRealWidth(); + else + cr.w = realWidth_ - padLeft_ - padRight_; + cr.h = child.getRealHeight(); + + std::get<2>(ent) = cr; + + currentY += child.getRealHeight() + padChildren_; + } + + if (height_ == WRAP_CONTENT) + realHeight_ = currentY - padChildren_ + padBottom_; + } + + invalidateGL(); +} diff --git a/LinearLayout.hh b/LinearLayout.hh new file mode 100644 index 0000000..3a3acfd --- /dev/null +++ b/LinearLayout.hh @@ -0,0 +1,27 @@ +#ifndef __OPENGLPLAYGROUND_LINEARLAYOUT_HH__ +#define __OPENGLPLAYGROUND_LINEARLAYOUT_HH__ + +#include "View.hh" + +static const int DIR_VERTICAL = 0; +static const int DIR_HORIZONTAL = 0; + +class LinearLayout : public View { +public: + LinearLayout(int direction = DIR_VERTICAL, int width = WRAP_CONTENT, int height = WRAP_CONTENT, std::string name = ""); + + LinearLayout(LinearLayout const& copy) = delete; + LinearLayout& operator=(LinearLayout const& copy) = delete; + + ~LinearLayout(); + + void setChildPadding(int pad); + +protected: + void layout() override; + + int direction_; + int padChildren_; +}; + +#endif diff --git a/Makefile b/Makefile index 95e6661..af70b99 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ CXX=g++ -CXXOPTS=-O2 -ggdb -Wall -Wextra -pedantic -Wno-unused-function -Wno-unused-parameter -Wno-sign-compare -std=c++14 -flto +CXXOPTS=-Og -ggdb -Wall -Wextra -pedantic -Wno-unused-function -Wno-unused-parameter -Wno-sign-compare -std=c++14 -flto LDOPTS= LIBS=-lglbinding -lSDL2 -lSDL2_image -lobj -lSDL2_ttf -lprotobuf -CXXSRCS=main.cc objectParser.cc shaders.cc Object.cc VBOManager.cc texture.cc font.cc Overlay.cc TextWidget.cc +CXXSRCS=main.cc objectParser.cc shaders.cc Object.cc VBOManager.cc texture.cc font.cc Overlay.cc TextWidget.cc Widget.cc View.cc LinearLayout.cc BINIFY_SRCS=binifyObj.cc objectParser.cc object.pb.cc OBJS=$(addprefix objs/,$(CXXSRCS:.cc=.o)) BINIFY_OBJS=$(addprefix objs/,$(BINIFY_SRCS:.cc=.o)) diff --git a/Object.cc b/Object.cc index 0b0ec91..91aec84 100644 --- a/Object.cc +++ b/Object.cc @@ -12,16 +12,15 @@ using namespace gl; -Object::Object(VBOManager& vboManager, std::vector const& vas, - std::vector indices, Program& prog) - : _vboManager(vboManager), _prog(prog), _indices(std::move(indices)), _vaID(0) +Object::Object(std::vector const& vas, + std::vector indices) + : _indices(std::move(indices)), _vaID(0) { construct(vas); } -Object::Object(VBOManager& vboManager, std::string const& filename, - Program& prog) - : _vboManager(vboManager), _prog(prog), _vaID(0) +Object::Object(std::string const& filename) + : _vaID(0) { auto tmp = readObject(filename); _indices = std::get<1>(tmp); @@ -33,22 +32,17 @@ Object::~Object() glDeleteVertexArrays(1, &_vaID); } -void Object::draw(glm::mat4 const& modelview, Program *override) const +void Object::draw() const { glBindVertexArray(_vaID); - if (override) - override->use(); - else - _prog.use(); - glUniformMatrix4fv(_prog.getUniformLocation("model_matrix"), 1, GL_FALSE, - glm::value_ptr(modelview)); + glDrawElements(GL_TRIANGLES, _indices.size(), GL_UNSIGNED_SHORT, _indices.data()); } void Object::construct(std::vector const& vas) { - _vbo = _vboManager.alloc(sizeof(objVertexAttribs)*vas.size()); + _vbo = VBOManager::getInstance().alloc(sizeof(objVertexAttribs)*vas.size()); glBindBuffer(GL_ARRAY_BUFFER, _vbo.getVBOId()); glBufferSubData(GL_ARRAY_BUFFER, _vbo.getOfs(), sizeof(objVertexAttribs)*vas.size(), (void*)vas.data()); @@ -56,22 +50,17 @@ void Object::construct(std::vector const& vas) glGenVertexArrays(1, &_vaID); glBindVertexArray(_vaID); - GLint al; - if((al = _prog.getAttribLocation("vertex")) != -1) { - glEnableVertexAttribArray(al); - glVertexAttribPointer(al, 3, GL_FLOAT, GL_FALSE, sizeof(objVertexAttribs), - (void*)(_vbo.getOfs()+offsetof(objVertexAttribs, vertex))); - } - if((al = _prog.getAttribLocation("vertexTC")) != -1) { - glEnableVertexAttribArray(al); - glVertexAttribPointer(al, 2, GL_UNSIGNED_SHORT, GL_TRUE, - sizeof(objVertexAttribs), - (void*)(_vbo.getOfs()+offsetof(objVertexAttribs, texCoords))); - } - if((al = _prog.getAttribLocation("vertexNorm")) != -1) { - glEnableVertexAttribArray(al); - glVertexAttribPointer(al, 4, GL_INT_2_10_10_10_REV, GL_TRUE, - sizeof(objVertexAttribs), - (void*)(_vbo.getOfs()+offsetof(objVertexAttribs, normal))); - } + glEnableVertexAttribArray(ATTRIB_VERTEX_POS); + glVertexAttribPointer(ATTRIB_VERTEX_POS, 3, GL_FLOAT, GL_FALSE, sizeof(objVertexAttribs), + (void*)(_vbo.getOfs()+offsetof(objVertexAttribs, vertex))); + + glEnableVertexAttribArray(ATTRIB_VERTEX_TC); + glVertexAttribPointer(ATTRIB_VERTEX_TC, 2, GL_UNSIGNED_SHORT, GL_TRUE, + sizeof(objVertexAttribs), + (void*)(_vbo.getOfs()+offsetof(objVertexAttribs, texCoords))); + + glEnableVertexAttribArray(ATTRIB_VERTEX_NORM); + glVertexAttribPointer(ATTRIB_VERTEX_NORM, 4, GL_INT_2_10_10_10_REV, GL_TRUE, + sizeof(objVertexAttribs), + (void*)(_vbo.getOfs()+offsetof(objVertexAttribs, normal))); } diff --git a/Object.hh b/Object.hh index c5541d5..fbfa896 100644 --- a/Object.hh +++ b/Object.hh @@ -7,14 +7,12 @@ #include "objectParser.hh" #include "VBOManager.hh" -class Program; - class Object { public: - Object(VBOManager& vboManager, std::vector const& vas, - std::vector indices, Program& prog); + Object(std::vector const& vas, + std::vector indice); - Object(VBOManager& vboManager, std::string const& filename, Program& prog); + Object(std::string const& filename); Object(Object const& copy) = delete; @@ -22,14 +20,12 @@ public: Object& operator=(Object const& copy) = delete; - void draw(glm::mat4 const& modelview, Program *override = nullptr) const; + void draw() const; private: void construct(std::vector const& vas); - VBOManager& _vboManager; VBOManager::VBOAlloc _vbo; - Program& _prog; std::vector _indices; gl::GLuint _vaID; }; diff --git a/Overlay.cc b/Overlay.cc index 0ae2bb9..3624e76 100644 --- a/Overlay.cc +++ b/Overlay.cc @@ -6,10 +6,9 @@ using namespace gl; -Overlay::Overlay(VBOManager& vboManager, std::vector const& vas, - Program& prog) - : vboManager_(vboManager), vbo_(vboManager_.alloc(sizeof(ovlVertexAttribs)*vas.size())), - prog_(prog), vaID_(0), vertices_(vas.size()) +Overlay::Overlay(std::vector const& vas) + : vbo_(VBOManager::getInstance().alloc(sizeof(ovlVertexAttribs)*vas.size())), + vaID_(0), vertices_(vas.size()) { glBindBuffer(GL_ARRAY_BUFFER, vbo_.getVBOId()); glBufferSubData(GL_ARRAY_BUFFER, vbo_.getOfs(), @@ -19,18 +18,16 @@ Overlay::Overlay(VBOManager& vboManager, std::vector const& va glGenVertexArrays(1, &vaID_); try { glBindVertexArray(vaID_); - GLint al; - if((al = prog_.getAttribLocation("vertex")) != -1) { - glEnableVertexAttribArray(al); - glVertexAttribPointer(al, 2, GL_SHORT, GL_TRUE, sizeof(ovlVertexAttribs), - (void*)(vbo_.getOfs()+offsetof(ovlVertexAttribs, vertex))); - } - if((al = prog_.getAttribLocation("vertexTC")) != -1) { - glEnableVertexAttribArray(al); - glVertexAttribPointer(al, 2, GL_UNSIGNED_SHORT, GL_TRUE, - sizeof(ovlVertexAttribs), - (void*)(vbo_.getOfs()+offsetof(ovlVertexAttribs, texCoords))); - } + + glEnableVertexAttribArray(ATTRIB_VERTEX_POS); + glVertexAttribPointer(ATTRIB_VERTEX_POS, 2, GL_SHORT, GL_FALSE, + sizeof(ovlVertexAttribs), + (void*)(vbo_.getOfs()+offsetof(ovlVertexAttribs, vertex))); + + glEnableVertexAttribArray(ATTRIB_VERTEX_TC); + glVertexAttribPointer(ATTRIB_VERTEX_TC, 2, GL_UNSIGNED_SHORT, GL_TRUE, + sizeof(ovlVertexAttribs), + (void*)(vbo_.getOfs()+offsetof(ovlVertexAttribs, texCoords))); } catch(...) { glDeleteVertexArrays(1, &vaID_); throw; @@ -42,12 +39,9 @@ Overlay::~Overlay() glDeleteVertexArrays(1, &vaID_); } -void Overlay::draw(Program *override) const +void Overlay::draw() const { glBindVertexArray(vaID_); - if (override) - override->use(); - else - prog_.use(); + glDrawArrays(GL_TRIANGLES, 0, vertices_); } diff --git a/Overlay.hh b/Overlay.hh index bd43f8b..e484cad 100644 --- a/Overlay.hh +++ b/Overlay.hh @@ -6,29 +6,24 @@ #include "VBOManager.hh" -class Program; - struct ovlVertexAttribs { - uint16_t vertex[2]; - uint16_t texCoords[2]; -} __attribute__((__packed__)); + int16_t vertex[2]; + uint16_t texCoords[2]; + } __attribute__((__packed__)); class Overlay { public: - Overlay(VBOManager& vboManager, std::vector const& vas, - Program& prog); + Overlay(std::vector const& vas); Overlay(Overlay const& copy) = delete; Overlay& operator=(Overlay const& copy) = delete; ~Overlay(); - void draw(Program *override = nullptr) const; + void draw() const; private: - VBOManager& vboManager_; VBOManager::VBOAlloc vbo_; - Program& prog_; gl::GLuint vaID_; size_t vertices_; }; diff --git a/TextWidget.cc b/TextWidget.cc new file mode 100644 index 0000000..f102a08 --- /dev/null +++ b/TextWidget.cc @@ -0,0 +1,84 @@ +#include + +#include "font.hh" +#include "TextWidget.hh" + +TextWidget::TextWidget(Font& font, std::string text, int width, int height) + : Widget(width, height), text_(std::move(text)), font_(font), + textSurf_(nullptr) +{ + layout(); +} + +TextWidget::~TextWidget() +{ +} + +void TextWidget::setText(std::string text) +{ + if (text != text_) { + text_ = std::move(text); + invalidateGL(); + layout(); + } +} + +void TextWidget::setForegroundColor(SDL_Color fg) +{ + Widget::setForegroundColor(fg); + + // Trigger text rerender + layout(); +} + +void TextWidget::layout() +{ + // Determine text size + int width, height; + if(TTF_SizeUTF8(font_.getFont(), text_.c_str(), &width, &height) != 0) + throw TTFException{}; + + if ((width > (width_ - padLeft_ - padRight_)) && (width_ != WRAP_CONTENT)) { + textSurf_.reset(TTF_RenderUTF8_Blended_Wrapped(font_.getFont(), + text_.c_str(), + fg_, width_- padLeft_ - padRight_)); + if (!textSurf_) + throw TTFException{}; + + if (height_ == WRAP_CONTENT) + // TODO: Get proper tight bounding box for multiline text + if (realHeight_ != (textSurf_->h - // TTF_FontLineSkip(font_.getFont()) + + padTop_ + padBottom_)) { + realHeight_ = textSurf_->h - // TTF_FontLineSkip(font_.getFont()) + + padTop_ + padBottom_; + invalidateGL(); + } + } else { + textSurf_.reset(TTF_RenderUTF8_Blended(font_.getFont(), text_.c_str(), fg_)); + + if (!textSurf_) + throw TTFException{}; + + if (width_ == WRAP_CONTENT) + if (realWidth_ != (textSurf_->w + padLeft_ + padRight_)) { + realWidth_ = textSurf_->w + padLeft_ + padRight_; + invalidateGL(); + } + if (height_ == WRAP_CONTENT) + if (realHeight_ != (textSurf_->h + padTop_ + padBottom_)) { + realHeight_ = textSurf_->h + padTop_ + padBottom_; + invalidateGL(); + } + } + +} + +void TextWidget::render(SDL_Surface *dst, SDL_Rect *dstRect) const +{ + Widget::render(dst, dstRect); + + SDL_Rect rect = calcContentRect(dst, dstRect, textSurf_->clip_rect); + + if (SDL_BlitSurface(textSurf_.get(), nullptr, dst, &rect) != 0) + throw SDLException{}; +} diff --git a/TextWidget.hh b/TextWidget.hh new file mode 100644 index 0000000..26306de --- /dev/null +++ b/TextWidget.hh @@ -0,0 +1,37 @@ +#ifndef __OPENGLPLAYGROUND_TEXTWIDGET_HH__ +#define __OPENGLPLAYGROUND_TEXTWIDGET_HH__ + +#include + +#include "common.hh" +#include "Widget.hh" + +class Font; + +class TextWidget final : public Widget { +public: + TextWidget(Font& font, std::string text = "", + int width = WRAP_CONTENT, int height = WRAP_CONTENT); + + TextWidget(TextWidget const& copy) = delete; + TextWidget& operator=(TextWidget const& copy) = delete; + + ~TextWidget(); + + void setText(std::string text); + std::string const& getText() const { return text_; } + + void setForegroundColor(SDL_Color fg) override; + + void render(SDL_Surface *dst, SDL_Rect *dstRect) const override; + +protected: + void layout() override; + +private: + std::string text_; + Font& font_; + SDLSurfaceUPtr textSurf_; +}; + +#endif diff --git a/VBOManager.cc b/VBOManager.cc index 64f161e..b0e31f8 100644 --- a/VBOManager.cc +++ b/VBOManager.cc @@ -1,10 +1,26 @@ #include #include +#include #include "VBOManager.hh" +#include "common.hh" using namespace gl; +std::unique_ptr VBOManager::instance{nullptr}; +static std::once_flag instance_flag; + +VBOManager& VBOManager::getInstance() +{ + std::call_once(instance_flag, init); + return *instance; +} + +void VBOManager::init() +{ + instance.reset(new VBOManager{}); +} + VBOManager::VBOManager() { } diff --git a/VBOManager.hh b/VBOManager.hh index 6a52703..6d35bab 100644 --- a/VBOManager.hh +++ b/VBOManager.hh @@ -2,20 +2,26 @@ #define __OPENGLPLAYGROUND_VBOMANAGER_HH__ #include -#include -#include #include +#include #include #include class VBOManager { public: - VBOManager(); - + static VBOManager& getInstance(); + ~VBOManager(); + VBOManager(VBOManager const& copy) = delete; + VBOManager& operator=(VBOManager const& copy) = delete; + private: + VBOManager(); + + static void init(); + static std::unique_ptr instance; class VBO; @@ -53,6 +59,10 @@ public: assert(_vbo); return _vbo->getID(); } + + explicit operator bool() const noexcept { + return (_vbo != nullptr); + } private: VBOAlloc(VBO &vbo, size_t ofs, size_t size); diff --git a/View.cc b/View.cc new file mode 100644 index 0000000..be6c0f4 --- /dev/null +++ b/View.cc @@ -0,0 +1,109 @@ +#include "View.hh" + +View::View(int width, int height, std::string name) + : Widget(width, height, std::move(name)) +{ +} + +View::~View() +{ +} + +void View::addChild(std::unique_ptr child) +{ + _setParent(*child); + children_.push_back(make_tuple(child->getName(), std::move(child), SDL_Rect{0, 0, 0, 0})); + + layout(); +} + +Widget& View::getChildByName(std::string name) +{ + if (name == "") + throw ChildNotFoundException{}; + + for(auto& ent : children_) { + if (std::get<0>(ent) == name) + return *std::get<1>(ent); + } + + throw ChildNotFoundException{}; +} + +Widget const& View::getChildByName(std::string name) const +{ + if (name == "") + throw ChildNotFoundException{}; + + for(auto& ent : children_) { + if (std::get<0>(ent) == name) + return *std::get<1>(ent); + } + + throw ChildNotFoundException{}; +} + +std::unique_ptr View::removeChild(std::string name) +{ + if (name == "") + throw ChildNotFoundException{}; + + for(auto it = children_.begin(); it != children_.end(); ++it) { + if (std::get<0>(*it) == name) { + auto child = std::move(std::get<1>(*it)); + children_.erase(it); + _clearParent(*child); + _layout(*child); + + layout(); + + return child; + } + } + + throw ChildNotFoundException{}; +} + +std::unique_ptr View::removeChild(Widget& child) +{ + for(auto it = children_.begin(); it != children_.end(); ++it) { + if (std::get<1>(*it).get() == &child) { + auto child = std::move(std::get<1>(*it)); + children_.erase(it); + + _clearParent(*child); + _layout(*child); + + layout(); + + return child; + } + } + + throw ChildNotFoundException{}; +} + + +void View::render(SDL_Surface *dst, SDL_Rect *dstRect) const +{ + Widget::render(dst, dstRect); + + SDL_Rect rect; + if (dstRect) + memcpy(&rect, dstRect, sizeof(SDL_Rect)); + else { + rect.x = 0; + rect.y = 0; + rect.w = dst->w; + rect.h = dst->h; + } + + for (auto& ent : children_) { + SDL_Rect childDst = std::get<2>(ent), childDstCliped; + childDst.x += rect.x; + childDst.y += rect.y; + SDL_IntersectRect(&rect, &childDst, &childDstCliped); + + std::get<1>(ent)->render(dst, &childDst); + } +} diff --git a/View.hh b/View.hh new file mode 100644 index 0000000..a34bff6 --- /dev/null +++ b/View.hh @@ -0,0 +1,51 @@ +#ifndef __OPENGLPLAYGROUND_VIEW_HH__ +#define __OPENGLPLAYGROUND_VIEW_HH__ + +#include +#include +#include +#include + +#include "Widget.hh" +#include "common.hh" + +class ChildNotFoundException : public Exception { +public: + ChildNotFoundException() : Exception() { + } + + std::string toString() const override { + return "ChildNotFoundException"s; + } +}; + + +/* Base class for all Widgets containing user-specified other Widgets + (i.e. Views/Layouts) */ +class View : public Widget { +public: + View(int width = WRAP_CONTENT, int height = WRAP_CONTENT, std::string name = ""); + + View(View const& copy) = delete; + View& operator=(View const& copy) = delete; + + ~View(); + + void addChild(std::unique_ptr child); + + Widget& getChildByName(std::string name); + + Widget const& getChildByName(std::string name) const; + + std::unique_ptr removeChild(std::string name); + std::unique_ptr removeChild(Widget& child); + + void render(SDL_Surface *dst, SDL_Rect *dstRect) const override; + +protected: + SDL_Rect childrenBB_; + + std::vector, SDL_Rect > > children_; +}; + +#endif diff --git a/Widget.cc b/Widget.cc new file mode 100644 index 0000000..49a9ba7 --- /dev/null +++ b/Widget.cc @@ -0,0 +1,274 @@ +#include + +#define GLM_FORCE_RADIANS +#include + +#include "Widget.hh" +#include "texture.hh" +#include "shaders.hh" + +Widget::Widget(int width, int height, std::string name) + : name_(std::move(name)), width_(width), height_(height), + background_(nullptr), + bg_(SDL_Color{0, 0, 0, 0}), fg_(SDL_Color{255, 255, 255, 255}), + padLeft_(0), padTop_(0), padRight_(0), padBottom_(0), + alignHoriz_(ALIGN_LEFT), alignVert_(ALIGN_TOP), + renderTop_(0), renderLeft_(0), + renderTexValid_(false), renderAttribsValid_(false), + renderTex_(nullptr), vaID_(0) +{ + if (width_ == WRAP_CONTENT) + realWidth_ = 0; + else + realWidth_ = width_; + + if (height_ == WRAP_CONTENT) + realHeight_ = 0; + else + realHeight_ = height_; +} + +Widget::~Widget() +{ + glDeleteVertexArrays(1, &vaID_); +} + +void Widget::setSize(int width, int height) +{ + if ((width_ != width) || (height_ != height)) { + width_ = width; + height_ = height; + + if (width_ != WRAP_CONTENT) + if (realWidth_ != width_) { + realWidth_ = width_; + } + + if (height_ != WRAP_CONTENT) + if (realHeight_ != height_) { + realHeight_ = height_; + } + + layout(); + } +} + +void Widget::setBackground(SDL_Surface* surf) +{ + if (background_ != surf) { + background_ = surf; + invalidateGL(); + } +} + +void Widget::setBackgroundColor(SDL_Color bg) +{ + if (bg_ != bg) { + bg_ = bg; + invalidateGL(); + } +} + +void Widget::setForegroundColor(SDL_Color fg) +{ + if (fg_ != fg) { + fg_ = fg; + invalidateGL(); + } +} + +void Widget::setPadding(int left, int top, int right, int bottom) +{ + if ((padLeft_ != left) || + (padTop_ != top) || + (padRight_ != right) || + (padBottom_ != bottom)) { + padLeft_ = left; + padTop_ = top; + padRight_ = right; + padBottom_ = bottom; + + layout(); + } +} + +void Widget::setAlignment(int horiz, int vert) { + if ((alignHoriz_ != horiz) || + (alignVert_ != vert)) { + alignHoriz_ = horiz; + alignVert_ = vert; + + invalidateGL(); + } +} + +void Widget::setParent(Widget *parent) +{ + parent_ = parent; +} + +void Widget::setGLRenderPos(int left, int top) +{ + if ((top != renderTop_) || + (left != renderLeft_)) { + renderAttribsValid_ = false; + renderTop_ = top; + renderLeft_ = left; + } +} + +void Widget::render(SDL_Surface *dst, SDL_Rect *dstRect) const +{ + if (background_) { + if (SDL_BlitScaled(background_, nullptr, dst, dstRect) != 0) + throw SDLException{}; + } else { + // Skip completely transparent background + if (bg_.a == 0) + return; + + // Simply write completely intransparent bg + if (bg_.a == 255) { + SDLSurfaceScopedLock lock{dst}; + if (SDL_FillRect(dst, dstRect, + SDL_MapRGBA(dst->format, bg_.r, bg_.g, bg_.b, bg_.a)) != 0) + throw SDLException{}; + + return; + } + + // Do proper blending blit for all other bgs + int w = dstRect?dstRect->w:dst->w; + int h = dstRect?dstRect->h:dst->h; + + uint32_t rmask, gmask, bmask, amask; + int bpp; + if (!SDL_PixelFormatEnumToMasks(dst->format->format, + &bpp, &rmask, &gmask, &bmask, &amask)) + throw SDLException{}; + + SDLSurfaceUPtr tmp{SDL_CreateRGBSurface(0, w, h, bpp, + rmask, gmask, bmask, amask)}; + if (!tmp) + throw SDLException{}; + + { + SDLSurfaceScopedLock lock{tmp}; + if (SDL_FillRect(tmp.get(), nullptr, + SDL_MapRGBA(tmp->format, bg_.r, bg_.g, bg_.b, bg_.a)) != 0) + throw SDLException{}; + } + + if (SDL_BlitSurface(tmp.get(), nullptr, dst, dstRect) != 0) + throw SDLException{}; + } +} + +SDL_Rect Widget::calcContentRect(SDL_Surface *dst, SDL_Rect *dstRect, SDL_Rect contentRect) const +{ + SDL_Rect rect; + + if (dstRect) { + rect.x = dstRect->x + padLeft_; + rect.y = dstRect->y + padTop_; + rect.w = dstRect->w - (padLeft_ + padRight_); + rect.h = dstRect->h - (padTop_ + padBottom_); + } else { + rect.x = padLeft_; + rect.y = padTop_; + rect.w = dst->w - padLeft_ - padRight_; + rect.h = dst->h - padTop_ - padBottom_; + } + + if (contentRect.w < rect.w) { + auto space = rect.w - contentRect.w; + if (alignHoriz_ == ALIGN_RIGHT) { + rect.x += space; + rect.w -= space; + } else if (alignHoriz_ == ALIGN_CENTER) { + rect.x += space/2; + rect.w -= space/2; + } + } + + if (contentRect.h < rect.h) { + auto space = rect.h - contentRect.h; + if (alignHoriz_ == ALIGN_BOTTOM) { + rect.y += space; + rect.h -= space; + } else if (alignHoriz_ == ALIGN_CENTER) { + rect.y += space/2; + rect.h -= space/2; + } + } + + return rect; +} + +void Widget::renderToGL() const +{ + const size_t NUM_TRIANGLES = 6; + + if (!renderTexValid_) { + uint32_t rmask, gmask, bmask, amask; + int bpp; + if (!SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888, + &bpp, &rmask, &gmask, &bmask, &amask)) + throw SDLException{}; + + SDLSurfaceUPtr tmp(SDL_CreateRGBSurface(0, realWidth_, realHeight_, bpp, + rmask, gmask, bmask, amask)); + render(tmp.get(), nullptr); + + if (!renderTex_ || (renderTex_->getWidth() != realWidth_) || + (renderTex_->getHeight() != realHeight_)) { + + renderTex_ = make_unique(tmp.get()); + + // Texture resize invalidates vertex attributes + renderAttribsValid_ = false; + } else { + renderTex_->copyFromSurface(tmp.get()); + } + renderTexValid_ = true; + } else + renderTex_->bind(); + + if (!renderAttribsValid_) { + int t = renderTop_, l = renderLeft_, b = renderTop_+realHeight_, + r = renderLeft_+realWidth_; + std::vector vertexAttribs{ + {{l, t}, {0, 0}}, + {{r, b}, {65535u, 65535u}}, + {{r, t}, {65535u, 0}}, + {{l, t}, {0, 0}}, + {{l, b}, {0, 65535u}}, + {{r, b}, {65535u, 65535u}}}; + + if (!vbo_) { + vbo_ = VBOManager::getInstance().alloc(sizeof(VertexAttribs)*NUM_TRIANGLES); + glBindBuffer(GL_ARRAY_BUFFER, vbo_.getVBOId()); + glGenVertexArrays(1, &vaID_); + glBindVertexArray(vaID_); + + glEnableVertexAttribArray(ATTRIB_VERTEX_POS); + glVertexAttribPointer(ATTRIB_VERTEX_POS, 2, GL_SHORT, GL_FALSE, sizeof(VertexAttribs), + (void*)(vbo_.getOfs()+offsetof(VertexAttribs, vertex))); + + glEnableVertexAttribArray(ATTRIB_VERTEX_TC); + glVertexAttribPointer(ATTRIB_VERTEX_TC, 2, GL_UNSIGNED_SHORT, GL_TRUE, + sizeof(VertexAttribs), + (void*)(vbo_.getOfs()+offsetof(VertexAttribs, texCoords))); + } else { + glBindBuffer(GL_ARRAY_BUFFER, vbo_.getVBOId()); + glBindVertexArray(vaID_); + } + glBufferSubData(GL_ARRAY_BUFFER, vbo_.getOfs(), + sizeof(VertexAttribs)*NUM_TRIANGLES, + static_cast(vertexAttribs.data())); + renderAttribsValid_ = true; + } else + glBindVertexArray(vaID_); + + glDrawArrays(GL_TRIANGLES, 0, NUM_TRIANGLES); +} diff --git a/Widget.hh b/Widget.hh new file mode 100644 index 0000000..4f03bee --- /dev/null +++ b/Widget.hh @@ -0,0 +1,114 @@ +#ifndef __OPENGLPLAYGROUND_WIDGET_HH__ +#define __OPENGLPLAYGROUND_WIDGET_HH__ + +#include +#include +#include + +#include "common.hh" +#include "VBOManager.hh" + +/* Size the widget to wrap the content size */ +static const int WRAP_CONTENT = -1; + +/* Size the widget to match the parent. If there is no parent, behaves like + WRAP_CONTENT */ +static const int MATCH_PARENT = -2; + + +static const int ALIGN_TOP = 0; +static const int ALIGN_BOTTOM = 1; +static const int ALIGN_CENTER = 2; +static const int ALIGN_LEFT = 0; +static const int ALIGN_RIGHT = 1; + +class Texture2D; + +class Widget { +public: + Widget(int width = WRAP_CONTENT, int height = WRAP_CONTENT, std::string name = ""); + + Widget(Widget const& copy) = delete; + Widget& operator=(Widget const& copy) = delete; + + virtual ~Widget(); + + std::string const& getName() const { return name_; } + + Widget* getParent() { return parent_; } + + int getWidth() const { return width_; } + int getHeight() const { return height_; } + int getRealWidth() const { return realWidth_; } + int getRealHeight() const { return realHeight_; } + + virtual void setSize(int width, int height); + + /* Set background image. The image is scaled to the widgets actual size*/ + void setBackground(SDL_Surface* surf); + /* Set background color. Only takes effect when the background image is NULL */ + void setBackgroundColor(SDL_Color bg); + /* Set foreground color. Effect depends on widget type */ + virtual void setForegroundColor(SDL_Color fg); + + /* Set the padding between the widget position and the content + Exact effect depends on widget type. In general, when a widget is rendered + the background is applied to the entire dstRect, and the content is + rendered with padding*/ + void setPadding(int left, int top, int right, int bottom); + + virtual void setAlignment(int horiz, int vert); + + void setGLRenderPos(int left, int top); + + virtual void render(SDL_Surface *dst, SDL_Rect *dstRect) const; + void renderToGL() const; + +protected: + /* Update realWidth_ and realHeight_ where necessary + (width_ or height_ == WRAP_CONTENT) */ + virtual void layout() = 0; + + /* Inform the renderToGL() code that any cached presentations are now invalid */ + void invalidateGL() { renderTexValid_ = false; } + + /* Helper to calculate the SDL_Rect with which to render the content, + respecting alignment and padding */ + SDL_Rect calcContentRect(SDL_Surface *dst, SDL_Rect *dstRect, SDL_Rect contentRect) const; + + void setParent(Widget *parent); + + /* Helpers to allow Views to access protected methods of other Widgets */ + void _setParent(Widget& child) { child.setParent(this); } + void _clearParent(Widget& child) { child.setParent(nullptr); } + void _layout(Widget& child) { child.layout(); } + + std::string name_; + + int width_, height_; + int realWidth_, realHeight_; + + SDL_Surface *background_; + SDL_Color bg_, fg_; + + int padLeft_, padTop_, padRight_, padBottom_; + + int alignHoriz_, alignVert_; + + Widget *parent_; + +private: + struct VertexAttribs { + int16_t vertex[2]; + uint16_t texCoords[2]; + } __attribute__((__packed__)); + + int renderTop_, renderLeft_; + + mutable bool renderTexValid_, renderAttribsValid_; + mutable std::unique_ptr renderTex_; + mutable VBOManager::VBOAlloc vbo_; + mutable gl::GLuint vaID_; +}; + +#endif diff --git a/common.hh b/common.hh index d75db5e..d3f0141 100644 --- a/common.hh +++ b/common.hh @@ -137,6 +137,9 @@ enum class VAFormats { VertexNormalTexcoord }; + +// Some helpers to C++11-ify SDL + struct SDLSurfaceDeleter { void operator()(SDL_Surface* ptr) const { @@ -146,4 +149,47 @@ struct SDLSurfaceDeleter { using SDLSurfaceUPtr = std::unique_ptr; +class SDLSurfaceScopedLock { +public: + SDLSurfaceScopedLock(SDL_Surface *surf) : surf_(surf) { + if (SDL_MUSTLOCK(surf_)) + if (SDL_LockSurface(surf_) != 0) + throw SDLException{}; + } + + SDLSurfaceScopedLock(SDLSurfaceUPtr& surf) : surf_(surf.get()) { + if (SDL_MUSTLOCK(surf_)) + if (SDL_LockSurface(surf_) != 0) + throw SDLException{}; + } + + SDLSurfaceScopedLock(SDLSurfaceScopedLock const& copy) = delete; + SDLSurfaceScopedLock& operator=(SDLSurfaceScopedLock const& copy) = delete; + + ~SDLSurfaceScopedLock() { + if (surf_ && SDL_MUSTLOCK(surf_)) + SDL_UnlockSurface(surf_); + } + + void unlock() { + if (surf_ && SDL_MUSTLOCK(surf_)) + SDL_UnlockSurface(surf_); + surf_ = nullptr; + } + +private: + SDL_Surface *surf_; +}; + +static bool operator==(SDL_Color const& a, SDL_Color const& b) { + return ((a.r == b.r) && (a.g == b.g) && (a.b == b.b) && (a.a == b.a)); +} + +static bool operator!=(SDL_Color const& a, SDL_Color const& b) { + return ((a.r != b.r) || (a.g != b.g) || (a.b != b.b) || (a.a != b.a)); +} + +// Compatibility with C++11 where make_unique was not in std:: yet +using std::make_unique; + #endif diff --git a/main.cc b/main.cc index b15eb1c..0921a29 100644 --- a/main.cc +++ b/main.cc @@ -25,6 +25,8 @@ #include "Object.hh" #include "font.hh" #include "Overlay.hh" +#include "TextWidget.hh" +#include "LinearLayout.hh" using namespace gl; @@ -80,6 +82,8 @@ int main(int argc, char *argv[]) if (!window) { std::printf("Could not create window: %s\n", SDL_GetError()); + TTF_Quit(); + IMG_Quit(); SDL_Quit(); return 1; } @@ -88,6 +92,8 @@ int main(int argc, char *argv[]) if (!glcontext) { std::printf("Could not create GL context: %s\n", SDL_GetError()); SDL_DestroyWindow(window); + TTF_Quit(); + IMG_Quit(); SDL_Quit(); return 1; } @@ -135,9 +141,7 @@ int main(int argc, char *argv[]) // sf::Clock clock; // sf::Time last = clock.getElapsedTime(); auto last = SDL_GetTicks(); - - VBOManager vboManager; - + Texture2D cubeTex("textures/Wood_Box_Texture.jpg"); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast(GL_LINEAR_MIPMAP_LINEAR)); if (SDL_GL_ExtensionSupported("GL_EXT_texture_filter_anisotropic")) @@ -164,7 +168,8 @@ int main(int argc, char *argv[]) // const glm::vec3 lightColor[lights] = {glm::vec3(1.0, 0.9, 0.8), // glm::vec3(0.0, 1.0, 0.0)}; // const float lightIntensity[lights] = {75.0f, 25.0f}; - + + glm::mat4 ovlProj = glm::ortho(0.0f, static_cast(width), static_cast(height), 0.0f); Framebuffer shadowFB; @@ -195,31 +200,46 @@ int main(int argc, char *argv[]) glm::value_ptr(shadowProj)); ovlProg.use(); + glUniformMatrix4fv(ovlProg.getUniformLocation("projection_matrix"), 1, GL_FALSE, + glm::value_ptr(ovlProj)); glUniform1i(ovlProg.getUniformLocation("texBase"), 0); - Object box(vboManager, "objects/woodbox.obj", prog); - Object pyramid(vboManager, "objects/pyramid.obj", prog); - Object plane(vboManager, "objects/plane.obj", prog); - - float px20_width = 2.0f*(60.0f/width); - float px20_height = 2.0f*(20.0f/height); - - std::vector ovlAttribs{ - {{glm::packSnorm1x16(1.0-px20_width), glm::packSnorm1x16(1.0)}, {0, 0}}, - {{glm::packSnorm1x16(1.0), glm::packSnorm1x16(1.0-px20_height)}, {65535u, 65535u}}, - {{glm::packSnorm1x16(1.0), glm::packSnorm1x16(1.0)}, {65535u, 0}}, - {{glm::packSnorm1x16(1.0-px20_width), glm::packSnorm1x16(1.0)}, {0, 0}}, - {{glm::packSnorm1x16(1.0-px20_width), glm::packSnorm1x16(1.0-px20_height)}, {0, 65535u}}, - {{glm::packSnorm1x16(1.0), glm::packSnorm1x16(1.0-px20_height)}, {65535u, 65535u}}}; + Object box("objects/woodbox.obj"); + Object pyramid("objects/pyramid.obj"); + Object plane("objects/plane.obj"); - Overlay ovl{vboManager, ovlAttribs, ovlProg}; - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); bool close = false; - Texture2D fpsTex{64, 64, true}; unsigned fpsTime = 0, fpsCount = 0; + TextWidget fpsText{font, "0 FPS"}; + fpsText.setBackgroundColor(SDL_Color{0, 255, 0, 128}); + fpsText.setPadding(8, 8, 8, 8); + + auto laber = make_unique(font, "Dies ist ein längerer\nText, der umgebrochen werden sollte. Bla laber schwafl, laber bullshit bingo", + 200, WRAP_CONTENT); + laber->setBackgroundColor(SDL_Color{255, 0, 0, 128}); + laber->setPadding(4, 0, 4, 0); + + SDLSurfaceUPtr buttonImg(IMG_Load("textures/button_100x30.png")); + + if (!buttonImg) + throw SDLException(); + + auto button = make_unique(font, "Do stuff!", 100, 30); + button->setPadding(6, 6, 6, 6); + button->setBackground(buttonImg.get()); + button->setForegroundColor(SDL_Color{0, 0, 0, 255}); + button->setAlignment(ALIGN_CENTER, ALIGN_CENTER); + + auto layout = make_unique(); + layout->setBackgroundColor(SDL_Color{0, 255, 255, 128}); + layout->addChild(std::move(laber)); + layout->addChild(std::move(button)); + layout->setChildPadding(4); + layout->setPadding(4, 4, 4, 4); + layout->setGLRenderPos(200, 200); while (!close) { SDL_Event event; @@ -279,10 +299,18 @@ int main(int argc, char *argv[]) glm::mat4 model = glm::translate(glm::vec3(0.5f, 0.5f, -0.5f)) * glm::rotate(SDL_GetTicks()*0.001f, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::translate(glm::vec3(-0.5f, -0.5f, 0.5f)); - box.draw(model, &shadowProg); - plane.draw(glm::translate(glm::vec3(2.0f, -2.5f, 0.0f))* - glm::rotate(0.35f, glm::vec3(0.0f, 1.0f, 0.0f)), &shadowProg); - pyramid.draw(glm::translate(glm::vec3(-2.0f, 0.0f, 0.0f)), &shadowProg); + glUniformMatrix4fv(shadowProg.getUniformLocation("model_matrix"), 1, GL_FALSE, + glm::value_ptr(model)); + box.draw(); + model = glm::translate(glm::vec3(2.0f, -2.5f, 0.0f)) * + glm::rotate(0.35f, glm::vec3(0.0f, 1.0f, 0.0f)); + glUniformMatrix4fv(shadowProg.getUniformLocation("model_matrix"), 1, GL_FALSE, + glm::value_ptr(model)); + plane.draw(); + model = glm::translate(glm::vec3(-2.0f, 0.0f, 0.0f)); + glUniformMatrix4fv(shadowProg.getUniformLocation("model_matrix"), 1, GL_FALSE, + glm::value_ptr(model)); + pyramid.draw(); } } @@ -307,24 +335,37 @@ int main(int argc, char *argv[]) glm::rotate(SDL_GetTicks()*0.001f, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::translate(glm::vec3(-0.5f, -0.5f, 0.5f)); cubeTex.bind(); - + prog.use(); + + glUniformMatrix4fv(prog.getUniformLocation("model_matrix"), 1, GL_FALSE, + glm::value_ptr(model)); + // Shadow maps must be rendered before real drawing can begin glClientWaitSync(fence, SyncObjectMask(), 0xffffffffu); glDeleteSync(fence); - box.draw(model); + + box.draw(); + whiteTex.bind(); - plane.draw(glm::translate(glm::vec3(2.0f, -2.5f, 0.0f))* - glm::rotate(0.35f, glm::vec3(0.0f, 1.0f, 0.0f))); + model = glm::translate(glm::vec3(2.0f, -2.5f, 0.0f)) * + glm::rotate(0.35f, glm::vec3(0.0f, 1.0f, 0.0f)); + glUniformMatrix4fv(prog.getUniformLocation("model_matrix"), 1, GL_FALSE, + glm::value_ptr(model)); + plane.draw(); + redTex.bind(); - pyramid.draw(glm::translate(glm::vec3(-2.0f, 0.0f, 0.0f))); + model = glm::translate(glm::vec3(-2.0f, 0.0f, 0.0f)); + glUniformMatrix4fv(prog.getUniformLocation("model_matrix"), 1, GL_FALSE, + glm::value_ptr(model)); + pyramid.draw(); auto now = SDL_GetTicks(); auto elapsed = now - last; last = now; if (fpsTime+elapsed > 1000) { - const std::string fpsText{std::to_string(fpsCount + 1) + " FPS"s}; - fpsTex = font.render(fpsText); + fpsText.setText(std::to_string(fpsCount + 1) + " FPS"); + fpsCount = 0; fpsTime = 0; } else { @@ -335,9 +376,10 @@ int main(int argc, char *argv[]) glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - fpsTex.bind(); - - ovl.draw(); + ovlProg.use(); + + fpsText.renderToGL(); + layout->renderToGL(); glDisable(GL_BLEND); SDL_GL_SwapWindow(window); @@ -352,6 +394,7 @@ int main(int argc, char *argv[]) SDL_DestroyWindow(window); // Clean up + TTF_Quit(); IMG_Quit(); SDL_Quit(); diff --git a/shaders.cc b/shaders.cc index 8f32c40..b9fccf6 100644 --- a/shaders.cc +++ b/shaders.cc @@ -121,14 +121,14 @@ GLint Program::getUniformLocation(std::string const& name) const return ret; } -GLint Program::getAttribLocation(std::string const& name) const -{ - auto search = _attribLocCache.find(name); - if (search != _attribLocCache.end()) - return search->second; +// GLint Program::getAttribLocation(std::string const& name) const +// { +// auto search = _attribLocCache.find(name); +// if (search != _attribLocCache.end()) +// return search->second; - GLint ret = glGetAttribLocation(_progID, name.c_str()); - if (ret != -1) - _attribLocCache.emplace(name, ret); - return ret; -} +// GLint ret = glGetAttribLocation(_progID, name.c_str()); +// if (ret != -1) +// _attribLocCache.emplace(name, ret); +// return ret; +// } diff --git a/shaders.hh b/shaders.hh index ef6a415..8ed9fa5 100644 --- a/shaders.hh +++ b/shaders.hh @@ -8,6 +8,10 @@ #include "common.hh" +static const gl::GLint ATTRIB_VERTEX_POS = 0; +static const gl::GLint ATTRIB_VERTEX_TC = 1; +static const gl::GLint ATTRIB_VERTEX_NORM = 2; +static const gl::GLint ATTRIB_VERTEX_COLOR = 3; class ShaderException : public GLException { public: @@ -78,7 +82,7 @@ public: gl::GLint getUniformLocation(std::string const& name) const; - gl::GLint getAttribLocation(std::string const& name) const; + // gl::GLint getAttribLocation(std::string const& name) const; private: GeometryShader* _geom; diff --git a/shaders/color.vs b/shaders/color.vs index f34a49e..50b957d 100644 --- a/shaders/color.vs +++ b/shaders/color.vs @@ -3,8 +3,8 @@ uniform mat4 projection_matrix; uniform mat4 modelview_matrix; -in vec3 vertex; -in vec3 vertecColor; +layout(location = 0) in vec3 vertex; +layout(location = 3) in vec3 vertecColor; out vec3 fragColor; diff --git a/shaders/overlay.vs b/shaders/overlay.vs index 85c6936..d53b36c 100644 --- a/shaders/overlay.vs +++ b/shaders/overlay.vs @@ -1,13 +1,15 @@ #version 330 core #extension GL_ARB_shading_language_420pack : enable +uniform mat4 projection_matrix; + layout(location = 0) in vec2 vertex; layout(location = 1) in vec2 vertexTC; out vec2 fragTC; void main(void) { - vec4 pos = vec4(vertex, 0.0, 1.0); + vec4 pos = projection_matrix * vec4(vertex, 0.0, 1.0); gl_Position = pos; fragTC = vertexTC; } diff --git a/texture.cc b/texture.cc index 2cd75c4..f09b96c 100644 --- a/texture.cc +++ b/texture.cc @@ -101,10 +101,10 @@ void TextureCubeMap::bind() const glBindTexture(GL_TEXTURE_CUBE_MAP, _texID); } -Texture2D::Texture2D() - : texID_(0), width_(0), height_(0), alpha_(false) -{ -} +// Texture2D::Texture2D() +// : texID_(0), width_(0), height_(0), alpha_(false) +// { +// } Texture2D::Texture2D(unsigned width, unsigned height, bool alpha) : texID_(0), width_(width), height_(height), alpha_(alpha) @@ -202,14 +202,15 @@ void Texture2D::copyFromSurface(SDL_Surface *src) surf = src; } - if (SDL_MUSTLOCK(surf)) - SDL_LockSurface(surf); - + SDLSurfaceScopedLock lock{surf}; bind(); if(surf->format->Amask == 0) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, surf->w, surf->h, GL_RGB, GL_UNSIGNED_BYTE, surf->pixels); else glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, surf->w, surf->h, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); + + lock.unlock(); + glGenerateMipmap(GL_TEXTURE_2D); } diff --git a/texture.hh b/texture.hh index 880ff4e..59ed264 100644 --- a/texture.hh +++ b/texture.hh @@ -61,7 +61,7 @@ private: class Texture2D { public: - Texture2D(); + // Texture2D(); Texture2D(unsigned width, unsigned height, bool alpha = false); Texture2D(std::string const& file); Texture2D(SDL_Surface *surface); diff --git a/textures/button_100x30.png b/textures/button_100x30.png new file mode 100644 index 0000000000000000000000000000000000000000..b423bcaa49867408accd413d5d653317e10985cb GIT binary patch literal 281 zcmeAS@N?(olHy`uVBq!ia0vp^DL^d8!2~2jEp?UvDYhhUcNc~gAh@l_LklRvS>O>_ z%)r1c48n{Iv*t(u1=&kHeO=k_GjoVqSpL7y%nKBf%?ybsan8@pP0cF-av2z$i&7Iy zQd1PlGfOfQ+&z5*QuI>Uf#S8EE{-7?_uk%g31TN4yj0a3zwX?x?x)`kx?Q!ppB@KMH^pP_OFCZ{igX3J(9_k= JWt~$(697XjVaWgh literal 0 HcmV?d00001