WIP: GUI Toolkit

This commit is contained in:
2015-03-09 01:11:13 +01:00
parent ffbc59140a
commit 9d7dd452c7
24 changed files with 1003 additions and 134 deletions

77
LinearLayout.cc Normal file
View File

@@ -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();
}

27
LinearLayout.hh Normal file
View File

@@ -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

View File

@@ -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))

View File

@@ -12,16 +12,15 @@
using namespace gl;
Object::Object(VBOManager& vboManager, std::vector<objVertexAttribs> const& vas,
std::vector<uint16_t> indices, Program& prog)
: _vboManager(vboManager), _prog(prog), _indices(std::move(indices)), _vaID(0)
Object::Object(std::vector<objVertexAttribs> const& vas,
std::vector<uint16_t> 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<objVertexAttribs> 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<objVertexAttribs> 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),
glEnableVertexAttribArray(ATTRIB_VERTEX_POS);
glVertexAttribPointer(ATTRIB_VERTEX_POS, 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,
glEnableVertexAttribArray(ATTRIB_VERTEX_TC);
glVertexAttribPointer(ATTRIB_VERTEX_TC, 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,
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)));
}
}

View File

@@ -7,14 +7,12 @@
#include "objectParser.hh"
#include "VBOManager.hh"
class Program;
class Object {
public:
Object(VBOManager& vboManager, std::vector<objVertexAttribs> const& vas,
std::vector<uint16_t> indices, Program& prog);
Object(std::vector<objVertexAttribs> const& vas,
std::vector<uint16_t> 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<objVertexAttribs> const& vas);
VBOManager& _vboManager;
VBOManager::VBOAlloc _vbo;
Program& _prog;
std::vector<uint16_t> _indices;
gl::GLuint _vaID;
};

View File

@@ -6,10 +6,9 @@
using namespace gl;
Overlay::Overlay(VBOManager& vboManager, std::vector<ovlVertexAttribs> const& vas,
Program& prog)
: vboManager_(vboManager), vbo_(vboManager_.alloc(sizeof(ovlVertexAttribs)*vas.size())),
prog_(prog), vaID_(0), vertices_(vas.size())
Overlay::Overlay(std::vector<ovlVertexAttribs> 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<ovlVertexAttribs> 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),
glEnableVertexAttribArray(ATTRIB_VERTEX_POS);
glVertexAttribPointer(ATTRIB_VERTEX_POS, 2, GL_SHORT, GL_FALSE,
sizeof(ovlVertexAttribs),
(void*)(vbo_.getOfs()+offsetof(ovlVertexAttribs, vertex)));
}
if((al = prog_.getAttribLocation("vertexTC")) != -1) {
glEnableVertexAttribArray(al);
glVertexAttribPointer(al, 2, GL_UNSIGNED_SHORT, GL_TRUE,
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_);
}

View File

@@ -6,29 +6,24 @@
#include "VBOManager.hh"
class Program;
struct ovlVertexAttribs {
uint16_t vertex[2];
int16_t vertex[2];
uint16_t texCoords[2];
} __attribute__((__packed__));
class Overlay {
public:
Overlay(VBOManager& vboManager, std::vector<ovlVertexAttribs> const& vas,
Program& prog);
Overlay(std::vector<ovlVertexAttribs> 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_;
};

84
TextWidget.cc Normal file
View File

@@ -0,0 +1,84 @@
#include <SDL2/SDL_ttf.h>
#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{};
}

37
TextWidget.hh Normal file
View File

@@ -0,0 +1,37 @@
#ifndef __OPENGLPLAYGROUND_TEXTWIDGET_HH__
#define __OPENGLPLAYGROUND_TEXTWIDGET_HH__
#include <string>
#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

View File

@@ -1,10 +1,26 @@
#include <glbinding/gl/gl.h>
#include <cassert>
#include <mutex>
#include "VBOManager.hh"
#include "common.hh"
using namespace gl;
std::unique_ptr<VBOManager> 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()
{
}

View File

@@ -2,20 +2,26 @@
#define __OPENGLPLAYGROUND_VBOMANAGER_HH__
#include <map>
#include <tuple>
#include <vector>
#include <list>
#include <memory>
#include <cassert>
#include <glbinding/gl/types.h>
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<VBOManager> instance;
class VBO;
@@ -54,6 +60,10 @@ public:
return _vbo->getID();
}
explicit operator bool() const noexcept {
return (_vbo != nullptr);
}
private:
VBOAlloc(VBO &vbo, size_t ofs, size_t size);

109
View.cc Normal file
View File

@@ -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<Widget> 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<Widget> 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<Widget> 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);
}
}

51
View.hh Normal file
View File

@@ -0,0 +1,51 @@
#ifndef __OPENGLPLAYGROUND_VIEW_HH__
#define __OPENGLPLAYGROUND_VIEW_HH__
#include <memory>
#include <vector>
#include <tuple>
#include <string>
#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<Widget> child);
Widget& getChildByName(std::string name);
Widget const& getChildByName(std::string name) const;
std::unique_ptr<Widget> removeChild(std::string name);
std::unique_ptr<Widget> removeChild(Widget& child);
void render(SDL_Surface *dst, SDL_Rect *dstRect) const override;
protected:
SDL_Rect childrenBB_;
std::vector<std::tuple<std::string, std::unique_ptr<Widget>, SDL_Rect > > children_;
};
#endif

274
Widget.cc Normal file
View File

@@ -0,0 +1,274 @@
#include <vector>
#define GLM_FORCE_RADIANS
#include <glm/gtc/packing.hpp>
#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<Texture2D>(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> 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<void const*>(vertexAttribs.data()));
renderAttribsValid_ = true;
} else
glBindVertexArray(vaID_);
glDrawArrays(GL_TRIANGLES, 0, NUM_TRIANGLES);
}

114
Widget.hh Normal file
View File

@@ -0,0 +1,114 @@
#ifndef __OPENGLPLAYGROUND_WIDGET_HH__
#define __OPENGLPLAYGROUND_WIDGET_HH__
#include <memory>
#include <SDL2/SDL.h>
#include <glbinding/gl/types.h>
#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<Texture2D> renderTex_;
mutable VBOManager::VBOAlloc vbo_;
mutable gl::GLuint vaID_;
};
#endif

View File

@@ -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<SDL_Surface, SDLSurfaceDeleter>;
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

105
main.cc
View File

@@ -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;
}
@@ -136,8 +142,6 @@ int main(int argc, char *argv[])
// 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<int>(GL_LINEAR_MIPMAP_LINEAR));
if (SDL_GL_ExtensionSupported("GL_EXT_texture_filter_anisotropic"))
@@ -165,6 +169,7 @@ int main(int argc, char *argv[])
// 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<float>(width), static_cast<float>(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<ovlVertexAttribs> 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}}};
Overlay ovl{vboManager, ovlAttribs, ovlProg};
Object box("objects/woodbox.obj");
Object pyramid("objects/pyramid.obj");
Object plane("objects/plane.obj");
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<TextWidget>(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<TextWidget>(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<LinearLayout>();
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();
ovlProg.use();
ovl.draw();
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();

View File

@@ -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;
// }

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);

BIN
textures/button_100x30.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B