From 7fb5f660914c939588045bc6d1e6a98208b10609 Mon Sep 17 00:00:00 2001 From: Matthias Blankertz Date: Thu, 30 Apr 2015 16:16:48 +0200 Subject: [PATCH] WIP: SDL/OpenGL renderer. Make mvedecode use renderer for playback. --- .gitignore | 2 +- Makefile | 9 ++- MveDecoder.cc | 100 +++++++++++++++++++++---- MveDecoder.hh | 49 +++++++++--- Singleton.hh | 24 ++++++ game/GSMvePlay.cc | 94 +++++++++++++++++++++++ game/GSMvePlay.hh | 32 ++++++++ game/GameState.hh | 25 +++++++ mvedecode.cc | 22 ++++-- render/Drawable.hh | 23 ++++++ render/GlResource.cc | 25 +++++-- render/GlResource.hh | 123 +++++++++++++++++------------- render/Overlay.cc | 97 ++++++++++++++++++++++++ render/Overlay.hh | 33 ++++++++ render/ProgramProvider.cc | 99 ++++++++++++++++++++++++ render/ProgramProvider.hh | 41 ++++++++++ render/Renderer.cc | 148 +++++++++++++++++++++++++++++++++++- render/Renderer.hh | 32 ++++++-- render/VBOManager.cc | 154 ++++++++++++++++++++++++++++++++++++++ render/VBOManager.hh | 124 ++++++++++++++++++++++++++++++ render/renderutil.cc | 42 +++++++++++ render/renderutil.hh | 10 +++ render/sdlutil.hh | 141 ++++++++++++++++++++++++++++++++++ shaders/overlay.fs | 12 +++ shaders/overlay.vs | 16 ++++ util.cc | 23 ++++++ util.hh | 2 + 27 files changed, 1399 insertions(+), 103 deletions(-) create mode 100644 Singleton.hh create mode 100644 game/GSMvePlay.cc create mode 100644 game/GSMvePlay.hh create mode 100644 game/GameState.hh create mode 100644 render/Drawable.hh create mode 100644 render/Overlay.cc create mode 100644 render/Overlay.hh create mode 100644 render/ProgramProvider.cc create mode 100644 render/ProgramProvider.hh create mode 100644 render/VBOManager.cc create mode 100644 render/VBOManager.hh create mode 100644 render/renderutil.cc create mode 100644 render/renderutil.hh create mode 100644 render/sdlutil.hh create mode 100644 shaders/overlay.fs create mode 100644 shaders/overlay.vs diff --git a/.gitignore b/.gitignore index 9b93e24..49c6238 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -game/ +data/ objs/ tmp/ *~ diff --git a/Makefile b/Makefile index fc20af9..f2a1123 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,9 @@ CXX=g++ -CXXOPTS=-Og -ggdb -fvar-tracking-assignments -Wall -Wextra -pedantic -std=c++14 -march=native -fstack-protector-strong --param=ssp-buffer-size=4 -flto +CXXOPTS=-Og -ggdb -fvar-tracking-assignments -Wall -Wextra -Wno-unused-function -pedantic -std=c++14 -march=native -fstack-protector-strong --param=ssp-buffer-size=4 -flto -I. LDOPTS=-Wl,--sort-common,--as-needed -render_CXXSRCS ::= render/Renderer.cc render/GlResource.cc +render_CXXSRCS ::= render/Renderer.cc render/GlResource.cc render/ProgramProvider.cc render/renderutil.cc render/Overlay.cc render/VBOManager.cc +game_CXXSRCS ::= game/GSMvePlay.cc iffexplore_CXXSRCS ::= iffexplore.cc IffFile.cc util.cc exceptions.cc iffexplore_LIBS ::= @@ -13,8 +14,8 @@ treexplore_LIBS ::= font2png_CXXSRCS ::= font2png.cc font2png_LIBS ::= -lpng -mvedecode_CXXSRCS ::= mvedecode.cc TreFile.cc IffFile.cc util.cc MveDecoder.cc exceptions.cc decompress.cc $(render_CXXSRCS) -mvedecode_LIBS ::= -lSDL2 +mvedecode_CXXSRCS ::= mvedecode.cc TreFile.cc IffFile.cc util.cc MveDecoder.cc exceptions.cc decompress.cc $(render_CXXSRCS) $(game_CXXSRCS) +mvedecode_LIBS ::= -lSDL2 -lglbinding progs ::= iffexplore treexplore mvedecode diff --git a/MveDecoder.cc b/MveDecoder.cc index d737d0f..fb6d0cf 100644 --- a/MveDecoder.cc +++ b/MveDecoder.cc @@ -207,6 +207,76 @@ std::vector parsePixels_(char const* data, size_t len) } } +MveDecoder::Movie MveDecoder::open(size_t branch) const +{ + auto& br = branches_.at(branch); + + return Movie(br.cbegin(), br.cend(), + *this); +} + +MveDecoder::Movie::Movie(std::vector::const_iterator shotIt, + std::vector::const_iterator shotEnd, + MveDecoder const& mve) + : shotIt_(shotIt), shotEnd_(shotEnd), + frameIt_(shotIt_->VGAs.cbegin()), + audioIt_(shotIt_->AUDIs.cbegin()), + mve_(mve) +{ +} + +bool MveDecoder::Movie::hasNext() const { + if (shotIt_ == shotEnd_) + return false; + + return true; +} + +std::tuple MveDecoder::Movie::decodeNext() +{ + frame_ = mve_.parseVGA(**frameIt_++, &frame_); + auto& palette = shotIt_->palt; + + if (frameIt_ == shotIt_->VGAs.cend()) { + ++shotIt_; + if (shotIt_ != shotEnd_) { + frameIt_ = shotIt_->VGAs.cbegin(); + audioIt_ = shotIt_->AUDIs.cbegin(); + } + } + + return std::make_tuple(frame_, palette); +} + +bool MveDecoder::Movie::hasAudio() const +{ + return (audioIt_ != shotIt_->AUDIs.cend()); +} + +MveDecoder::Audio MveDecoder::Movie::getNextAudio() +{ + MveDecoder::Audio ret; + if (audioIt_ != shotIt_->AUDIs.cend()) { + ret.reserve((shotIt_->AUDIs.cend()-audioIt_)*2940); + + for(;audioIt_ != shotIt_->AUDIs.cend();++audioIt_) + std::copy((*audioIt_)->begin(), (*audioIt_)->end(), + std::back_inserter(ret)); + } + + return ret; +} + +unsigned MveDecoder::Movie::getWidth() const +{ + return mve_.width_; +} + +unsigned MveDecoder::Movie::getHeight() const +{ + return mve_.height_; +} + MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const* prev) const { // Parse header @@ -242,7 +312,7 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const* // Interpret command stream to render frame Frame ret; - ret.pixels.reserve(width_*height_); + ret.reserve(width_*height_); bool flag = false; size_t seg2Idx = 0; @@ -320,12 +390,12 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const* #endif if (!prev) throw FormatException{"Reference to non-existing prev. frame"}; - if (ret.pixels.size()+size > prev->pixels.size()) + if (ret.size()+size > prev->size()) throw FormatException{"Copy from prev exceeds frame"}; - std::copy(prev->pixels.begin()+ret.pixels.size(), - prev->pixels.begin()+ret.pixels.size()+size, - std::back_inserter(ret.pixels)); + std::copy(prev->begin()+ret.size(), + prev->begin()+ret.size()+size, + std::back_inserter(ret)); } else { #ifdef CMDDEBUG_ printf("DataCopy: %u @%lu\n", size, seg4Idx); @@ -335,7 +405,7 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const* std::copy(pixels.begin()+seg4Idx, pixels.begin()+seg4Idx+size, - std::back_inserter(ret.pixels)); + std::back_inserter(ret)); seg4Idx += size; } } else { @@ -352,25 +422,25 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const* const uint8_t mpos = vga.begin()[segOfs[2]+seg3Idx++]; const signed x = sextend(mpos>>4, 4), y = sextend(mpos&0xf,4); const signed delta = y*width_+x; - if ((delta < 0) && (ret.pixels.size() < static_cast(-delta))) + if ((delta < 0) && (ret.size() < static_cast(-delta))) throw FormatException{"Motion vector outside frame"}; - const unsigned ref = ret.pixels.size()+delta; + const unsigned ref = ret.size()+delta; #ifdef CMDDEBUG_ printf("@%lu %.2hhx -> (%d, %d) -> d %d -> %u\n", seg3Idx-1, mpos, x, y, delta, ref); #endif - if (ref+size > prev->pixels.size()) + if (ref+size > prev->size()) throw FormatException{"Copy from prev exceeds frame"}; - std::copy(prev->pixels.begin()+ref, - prev->pixels.begin()+ref+size, - std::back_inserter(ret.pixels)); + std::copy(prev->begin()+ref, + prev->begin()+ref+size, + std::back_inserter(ret)); flag = false; } } - if (ret.pixels.size() != width_*height_) + if (ret.size() != width_*height_) throw FormatException{"Decoded frame dimension mismatch"}; #ifndef NDEBUG @@ -380,7 +450,7 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const* return ret; } - +/* #include struct AudioCBData { @@ -651,4 +721,4 @@ void MveDecoder::play(unsigned branch) const printf("Frame times: [%u, %u]\n", minFT, maxFT); } - +*/ diff --git a/MveDecoder.hh b/MveDecoder.hh index 3369951..32f6171 100644 --- a/MveDecoder.hh +++ b/MveDecoder.hh @@ -14,25 +14,52 @@ public: return branches_.size(); } - void play(unsigned branch) const; -private: - IffFile iff_; - + using Frame = std::vector; + using Audio = std::vector; using Palette = std::array; - - struct Frame { - std::vector pixels; - }; +private: + using AUDIVec = std::vector; + using VGAVec = std::vector; struct Shot { Palette const& palt; - std::vector AUDIs; - std::vector VGAs; + AUDIVec AUDIs; + VGAVec VGAs; }; + +public: + class Movie { + public: + bool hasNext() const; + std::tuple decodeNext(); + + + bool hasAudio() const; + Audio getNextAudio(); + + unsigned getWidth() const; + unsigned getHeight() const; + + private: + Movie(std::vector::const_iterator shotIt, + std::vector::const_iterator shotEnd, + MveDecoder const& mve); + + std::vector::const_iterator shotIt_, shotEnd_; + VGAVec::const_iterator frameIt_; + AUDIVec::const_iterator audioIt_; + Frame frame_; + MveDecoder const& mve_; + friend class MveDecoder; + }; + + Movie open(size_t branch) const; + +private: + IffFile iff_; unsigned width_, height_; - //std::vector shots_; std::vector palts_; std::vector > branches_; diff --git a/Singleton.hh b/Singleton.hh new file mode 100644 index 0000000..f76bb5b --- /dev/null +++ b/Singleton.hh @@ -0,0 +1,24 @@ +#ifndef WC3RE_SINGLETON_HH__ +#define WC3RE_SINGLETON_HH__ + +#include +#include + +template +class Singleton { +public: + static T& getInstance() { + call_once(instance_flag, init); + return *instance; + } + +private: + static std::unique_ptr instance; + static std::once_flag instance_flag; + + static void init() { + instance.reset(new T{}); + } +}; + +#endif diff --git a/game/GSMvePlay.cc b/game/GSMvePlay.cc new file mode 100644 index 0000000..beddac9 --- /dev/null +++ b/game/GSMvePlay.cc @@ -0,0 +1,94 @@ +#include "MveDecoder.hh" + +#include "GSMvePlay.hh" +#include "render/Overlay.hh" +#include "render/Renderer.hh" + +namespace game { + GSMvePlay::GSMvePlay(render::Renderer& renderer, MveDecoder::Movie& movie) + : GameState(renderer), movie_(movie), + overlay_(nullptr), delta_(0), + nextFT_(1000/15.0f), frameRGB_() + { + int scrWidth = renderer_.getWidth(), scrHeight = renderer_.getHeight(); + const float mvePAR = 1.2f; + int mveWidth = movie_.getWidth(), mveHeight = movie_.getHeight(); + int top = 0, left = 0; + + if ((static_cast(mveWidth)/(mveHeight*mvePAR)) > (static_cast(scrWidth)/scrHeight)) { + // letterbox + int newScrHeight = scrWidth/(static_cast(mveWidth)/(mveHeight*mvePAR)); + top = (scrHeight-newScrHeight)/2; + scrHeight = newScrHeight; + + } else { + // pillarbox + int newScrWidth = scrHeight*(static_cast(mveWidth)/(mveHeight*mvePAR)); + left = (scrWidth-newScrWidth)/2; + scrWidth = newScrWidth; + } + + overlay_ = std::make_unique(renderer_, scrWidth, scrHeight, left, top, + mveWidth, mveHeight); + + auto align4Width = (mveWidth%4)?(mveWidth+(4-(mveWidth%4))):mveWidth; + frameRGB_.resize(align4Width*mveHeight*3); + + decode_(); + overlay_->setContentRGB8(frameRGB_.data()); + decode_(); + } + + GSMvePlay::~GSMvePlay() + { + } + + void GSMvePlay::decode_() + { + if (movie_.hasNext()) { + auto dec = movie_.decodeNext(); + auto& frame = std::get<0>(dec); + auto& palette = std::get<1>(dec); + + size_t i = 0; + for (unsigned y = 0;y < movie_.getHeight();++y) { + for (unsigned x = 0;x < movie_.getWidth();++x) { + auto idx = frame[y*movie_.getWidth()+x]; + std::copy(&palette[idx*3], &palette[idx*3+3], + &frameRGB_[i]); + i += 3; + } + if ((i%4) != 0) + i += 4-(i%4); + } + } else + frameRGB_.resize(0); + } + + + void GSMvePlay::draw(unsigned delta_ms) + { + const float frameTime = 1000/15.0f; + + delta_ += delta_ms; + if (delta_ >= nextFT_) { + if (!frameRGB_.size()) { + renderer_.popGS(); + return; + } + + if (delta_-nextFT_ <= frameTime) + nextFT_ = frameTime-(delta_-nextFT_); + else + nextFT_ = frameTime; + + delta_ = 0; + + overlay_->setContentRGB8(frameRGB_.data()); + + // Decode next frame + decode_(); + } + overlay_->draw(); + } +} diff --git a/game/GSMvePlay.hh b/game/GSMvePlay.hh new file mode 100644 index 0000000..3446a72 --- /dev/null +++ b/game/GSMvePlay.hh @@ -0,0 +1,32 @@ +#ifndef WC3RE_GAME_GSMVEPLAY_HH__ +#define WC3RE_GAME_GSMVEPLAY_HH__ + +#include "GameState.hh" +#include "render/sdlutil.hh" + +class MveDecoder; +namespace render { + class Overlay; +} + +namespace game { + class GSMvePlay : public GameState { + public: + GSMvePlay(render::Renderer& renderer, MveDecoder::Movie& movie); + + ~GSMvePlay() override; + + void draw(unsigned delta_ms) override; + + private: + MveDecoder::Movie& movie_; + std::unique_ptr overlay_; + unsigned delta_; + float nextFT_; + std::vector frameRGB_; + + void decode_(); + }; +} + +#endif diff --git a/game/GameState.hh b/game/GameState.hh new file mode 100644 index 0000000..f24fe82 --- /dev/null +++ b/game/GameState.hh @@ -0,0 +1,25 @@ +#ifndef WC3RE_GAME_GAMESTATE_HH__ +#define WC3RE_GAME_GAMESTATE_HH__ + +namespace render { + class Renderer; +} + +namespace game { + class GameState { + public: + GameState(render::Renderer& renderer) + : renderer_(renderer) { + } + + virtual ~GameState() { + } + + virtual void draw(unsigned delta_ms) = 0; + + protected: + render::Renderer& renderer_; + }; +} + +#endif diff --git a/mvedecode.cc b/mvedecode.cc index a2f7467..2db445e 100644 --- a/mvedecode.cc +++ b/mvedecode.cc @@ -12,6 +12,10 @@ #include "TreFile.hh" #include "IffFile.hh" #include "MveDecoder.hh" +#include "render/Renderer.hh" +#include "game/GSMvePlay.hh" + +using render::Renderer; void usage(char *argv0) { fprintf(stderr, "Usage: %s [-h] (tre-file name/crc)/iff-file\n", argv0); @@ -88,11 +92,19 @@ int main(int argc, char *argv[]) { mve = std::make_unique(mmap.data(), mmap.size()); if (play) { - if (branch >= 0) - mve->play(branch); - else - for(unsigned i = 0;i < mve->numBranches();++i) - mve->play(i); + Renderer renderer; + + if (branch >= 0) { + auto movie = mve->open(branch); + renderer.pushGS(std::make_unique(renderer, movie)); + renderer.run(); + } else { + for(unsigned i = 0;i < mve->numBranches();++i) { + auto movie = mve->open(i); + renderer.pushGS(std::make_unique(renderer, movie)); + renderer.run(); + } + } } } catch (POSIXException &ex) { fflush(stdout); diff --git a/render/Drawable.hh b/render/Drawable.hh new file mode 100644 index 0000000..49c45d1 --- /dev/null +++ b/render/Drawable.hh @@ -0,0 +1,23 @@ +#ifndef WC3RE_RENDER_DRAWABLE_HH__ +#define WC3RE_RENDER_DRAWABLE_HH__ + +namespace render { + class Renderer; + + class Drawable { + public: + Drawable(Renderer& renderer) + : renderer_(renderer) { + } + + virtual ~Drawable() { + } + + virtual void draw() = 0; + + protected: + Renderer& renderer_; + }; +} + +#endif diff --git a/render/GlResource.cc b/render/GlResource.cc index 37aa792..eea4749 100644 --- a/render/GlResource.cc +++ b/render/GlResource.cc @@ -4,10 +4,25 @@ using namespace gl; -void TextureDeleter::operator()(gl::GLuint tex) const { - glDeleteTextures(1, &tex); -} +namespace render { -void TextureDeleter::operator() (gl::GLsizei count, gl::GLuint tex[]) const { - glDeleteTextures(count, tex); + void TextureDeleter::operator()(gl::GLuint tex) const { + glDeleteTextures(1, &tex); + } + + void TextureDeleter::operator() (gl::GLsizei count, gl::GLuint tex[]) const { + glDeleteTextures(count, tex); + } + + void ProgramDeleter::operator()(gl::GLuint prog) const { + glDeleteProgram(prog); + } + + void ShaderDeleter::operator()(gl::GLuint shader) const { + glDeleteShader(shader); + } + + void VertexArrayDeleter::operator()(gl::GLuint varr) const { + glDeleteVertexArrays(1, &varr); + } } diff --git a/render/GlResource.hh b/render/GlResource.hh index 3fb0ddf..9c7aa54 100644 --- a/render/GlResource.hh +++ b/render/GlResource.hh @@ -3,69 +3,90 @@ #include -struct TextureDeleter { - void operator() (gl::GLuint tex) const; - void operator() (gl::GLsizei count, gl::GLuint tex[]) const; -}; +namespace render { + struct TextureDeleter { + void operator() (gl::GLuint tex) const; + void operator() (gl::GLsizei count, gl::GLuint tex[]) const; + }; -struct FramebufferDeleter { - void operator() (gl::GLuint tex) const; -}; + struct FramebufferDeleter { + void operator() (gl::GLuint tex) const; + }; -struct ShaderDeleter { - void operator() (gl::GLuint tex) const; -}; + struct ShaderDeleter { + void operator() (gl::GLuint tex) const; + }; -struct ProgramDeleter { - void operator() (gl::GLuint tex) const; -}; + struct ProgramDeleter { + void operator() (gl::GLuint tex) const; + }; -struct VertexArrayDeleter { - void operator() (gl::GLuint tex) const; -}; + struct VertexArrayDeleter { + void operator() (gl::GLuint tex) const; + }; -struct BufferDeleter { - void operator() (gl::GLuint tex) const; -}; + struct BufferDeleter { + void operator() (gl::GLuint tex) const; + }; -template -class GlResource { -public: - GlResource() - : handle_() { - } + template + class GlResource { + public: + GlResource() + : handle_() { + } - GlResource(Handle handle) - : handle_(handle) { - } + GlResource(Handle handle) + : handle_(handle) { + } - ~GlResource() { - if (handle_) - Deleter()(handle_); - handle_ = Handle(); - } + GlResource(GlResource const& copy) = delete; + GlResource& operator=(GlResource const& copy) = delete; - operator Handle() const { - return handle_; - } + GlResource(GlResource && move) + : handle_(move.handle_) { + move.handle_ = Handle(); + } - Handle& get() { - return handle_; - } + GlResource& operator=(GlResource && move) { + if (handle_) + Deleter()(handle_); + handle_ = move.handle_; + move.handle_ = Handle(); + } + + ~GlResource() { + if (handle_) + Deleter()(handle_); + } - Handle const& get() const { - return handle_; - } + operator Handle() const { + return handle_; + } + + operator Handle&() { + return handle_; + } + + Handle& get() { + return handle_; + } + + Handle const& get() const { + return handle_; + } -private: - Handle handle_; -}; + private: + Handle handle_; + }; -using TextureResource = GlResource; -using FramebufferResource = GlResource; -using ShaderResource = GlResource; -using ProgramResource = GlResource; -using VertexArrayResource = GlResource; -using BufferResource = GlResource; + using TextureResource = GlResource; + using FramebufferResource = GlResource; + using ShaderResource = GlResource; + using ProgramResource = GlResource; + using VertexArrayResource = GlResource; + using BufferResource = GlResource; + +} #endif diff --git a/render/Overlay.cc b/render/Overlay.cc new file mode 100644 index 0000000..8cb508e --- /dev/null +++ b/render/Overlay.cc @@ -0,0 +1,97 @@ +#include +#include + +#include +#include + +#include "Overlay.hh" +#include "renderutil.hh" +#include "VBOManager.hh" +#include "ProgramProvider.hh" +#include "Renderer.hh" + +using namespace gl; + +namespace render { + namespace { + struct VertexAttribs { + int16_t vertex[2]; + uint16_t texCoords[2]; + } __attribute__((__packed__)); + } + + Overlay::Overlay(Renderer& renderer, int width, int height, int left, int top, int intWidth, int intHeight) + : Drawable(renderer), texture_(create2DTexture(intWidth, intHeight, false, 1)), + vbo_(VBOManager::getInstance().alloc(sizeof(VertexAttribs)*6)), + width_(width), height_(height), top_(top), left_(left), intWidth_(intWidth), intHeight_(intHeight) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast(GL_LINEAR)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast(GL_CLAMP_TO_EDGE)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast(GL_CLAMP_TO_EDGE)); + program_ = ProgramProvider::getInstance().getProgram("overlay", "overlay"); + + glGenVertexArrays(1, &vertexArray_.get()); + short int t = top_, l = left_, b = height+top_, r = width+left_; + 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}}}; + + glBindBuffer(GL_ARRAY_BUFFER, vbo_.getVBOId()); + glBindVertexArray(vertexArray_); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_SHORT, GL_FALSE, sizeof(VertexAttribs), + vbo_.getOfs(offsetof(VertexAttribs, vertex))); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(VertexAttribs), + vbo_.getOfs(offsetof(VertexAttribs, texCoords))); + + glBufferSubData(GL_ARRAY_BUFFER, vbo_.getBase(), + sizeof(VertexAttribs)*6, + vertexAttribs.data()); + + ovlProj_ = glm::ortho(0.0f, static_cast(renderer_.getWidth()), + static_cast(renderer_.getHeight()), 0.0f); + } + + void Overlay::draw() + { + glDisable(GL_DEPTH_TEST); + glUseProgram(program_); + glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(ovlProj_)); + glUniform1i(1, 0); + + glBindTexture(GL_TEXTURE_2D, texture_); + glBindVertexArray(vertexArray_); + + glDrawArrays(GL_TRIANGLES, 0, 6); + } + + void Overlay::setContent(SDL_Surface *content) + { + if (!content || + (content->h != intHeight_) || + (content->w != intWidth_)) + throw Exception{"null or mismatched surface"}; + + glBindTexture(GL_TEXTURE_2D, texture_); + + if (content->format->format != SDL_PIXELFORMAT_RGB24) { + SDLSurfaceUPtr tmpSurf(SDL_ConvertSurfaceFormat(content, SDL_PIXELFORMAT_RGB24, 0)); + if (!tmpSurf) + throw SDLException{}; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tmpSurf->w, tmpSurf->h, GL_RGB, GL_UNSIGNED_BYTE, tmpSurf->pixels); + } else + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, content->w, content->h, GL_RGB, GL_UNSIGNED_BYTE, content->pixels); + } + + void Overlay::setContentRGB8(void *data) + { + glBindTexture(GL_TEXTURE_2D, texture_); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, intWidth_, intHeight_, GL_RGB, GL_UNSIGNED_BYTE, data); + } +} diff --git a/render/Overlay.hh b/render/Overlay.hh new file mode 100644 index 0000000..e8275ed --- /dev/null +++ b/render/Overlay.hh @@ -0,0 +1,33 @@ +#ifndef WC3RE_RENDER_OVERLAY_HH__ +#define WC3RE_RENDER_OVERLAY_HH__ + +#include + +#define GLM_FORCE_RADIANS +#include + +#include "Drawable.hh" +#include "GlResource.hh" +#include "VBOManager.hh" + +namespace render { + /* A two-dimensional surface which can be displayed */ + class Overlay : public Drawable { + public: + Overlay(Renderer& renderer, int width, int height, int left, int top, int intWidth, int intHeight); + + void draw() override; + + void setContent(SDL_Surface *content); + void setContentRGB8(void *data); + private: + TextureResource texture_; + VertexArrayResource vertexArray_; + gl::GLuint program_; + VBOManager::VBOAlloc vbo_; + int width_, height_, top_, left_, intWidth_, intHeight_; + glm::mat4 ovlProj_; + }; +} + +#endif diff --git a/render/ProgramProvider.cc b/render/ProgramProvider.cc new file mode 100644 index 0000000..6fdbb03 --- /dev/null +++ b/render/ProgramProvider.cc @@ -0,0 +1,99 @@ +#include +#include + +#include "ProgramProvider.hh" +#include "exceptions.hh" +#include "util.hh" + +using namespace gl; + +template<> +std::unique_ptr Singleton::instance{nullptr}; +template<> +std::once_flag Singleton::instance_flag{}; + +namespace render { + namespace { + class ShaderException : public Exception { + public: + ShaderException(std::string const& msg) + : Exception(msg) + { + } + + std::string toString() const override { + return "ShaderException: " + msg_; + } + }; + } + + ProgramProvider::ProgramProvider() + { + } + + void shaderCompile_(std::string const& shaderName, ShaderResource& shader) + { + std::string prog = fileToString("shaders/" + shaderName); + const char* const arr[] = {prog.c_str()}; + glShaderSource(shader, 1, arr, NULL); + glCompileShader(shader); + + int state; + glGetShaderiv(shader, GL_COMPILE_STATUS, &state); + + if (state == 0) { + int logLength; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength); + + char *log = new char[logLength]; + glGetShaderInfoLog(shader, logLength, NULL, log); + std::string msg(log); + delete[] log; + + throw ShaderException(msg); + } + } + + GLuint ProgramProvider::getProgram(std::string const& vertexShader, std::string const& fragShader) + { + auto key = make_tuple(vertexShader, fragShader); + auto it = programCache_.find(key); + if (it != programCache_.end()) + return it->second.get(); + + ShaderResource vs(glCreateShader(GL_VERTEX_SHADER)), + fs(glCreateShader(GL_FRAGMENT_SHADER)); + + shaderCompile_(vertexShader + ".vs", vs); + shaderCompile_(fragShader + ".fs", fs); + + ProgramResource prog(glCreateProgram()); + glAttachShader(prog, vs); + glAttachShader(prog, fs); + + glLinkProgram(prog); + + int state; + glGetProgramiv(prog, GL_LINK_STATUS, &state); + + if (state == 0) { + int logLength; + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength); + + char *log = new char[logLength]; + glGetProgramInfoLog(prog, logLength, NULL, log); + std::string msg(log); + delete[] log; + + throw ShaderException(msg); + } + + glDetachShader(prog, vs); + glDetachShader(prog, fs); + + std::tie(it, std::ignore) = programCache_.insert(make_pair(key, std::move(prog))); + + return it->second.get(); + } + +} diff --git a/render/ProgramProvider.hh b/render/ProgramProvider.hh new file mode 100644 index 0000000..39e3950 --- /dev/null +++ b/render/ProgramProvider.hh @@ -0,0 +1,41 @@ +#ifndef WC3RE_RENDER_PROGRAMPROVIDER_HH__ +#define WC3RE_RENDER_PROGRAMPROVIDER_HH__ + +#include + +#include "Singleton.hh" +#include "GlResource.hh" +namespace render { + using ProgramProviderKeyType = std::tuple; +} + +namespace std +{ + template<> + struct hash { + typedef render::ProgramProviderKeyType argument_type; + typedef std::size_t result_type; + + result_type operator()(argument_type const& s) const { + const result_type h1{std::hash()(std::get<0>(s))}; + const result_type h2{std::hash()(std::get<1>(s))}; + return h1 ^ h2; + } + }; +} + +namespace render { + class ProgramProvider : public Singleton { + private: + ProgramProvider(); + friend class Singleton; + + public: + gl::GLuint getProgram(std::string const& vertexShader, std::string const& fragShader); + + private: + std::unordered_map programCache_; + }; +} + +#endif diff --git a/render/Renderer.cc b/render/Renderer.cc index b8b49c1..37de136 100644 --- a/render/Renderer.cc +++ b/render/Renderer.cc @@ -1,12 +1,152 @@ #include +#include +#include #include "Renderer.hh" #include "GlResource.hh" +#include "game/GameState.hh" +#include "exceptions.hh" using namespace gl; -Renderer::Renderer() -{ - TextureResource testTex; - glGenTextures(1, &testTex.get()); +namespace render { + class GLException : public Exception { + public: + GLException(GLenum err) : Exception(), err_(err) { + } + + GLenum getErr() const {return err_;} + + std::string errToString() const { + std::string ret; + if (err_ == GL_INVALID_ENUM) + ret += "GL_INVALID_ENUM "; + if (err_ == GL_INVALID_VALUE) + ret += "GL_INVALID_VALUE "; + if (err_ == GL_INVALID_OPERATION) + ret += "GL_INVALID_OPERATION "; + if (err_ == GL_INVALID_FRAMEBUFFER_OPERATION) + ret += "GL_INVALID_FRAMEBUFFER_OPERATION "; + if (err_ == GL_OUT_OF_MEMORY) + ret += "GL_OUT_OF_MEMORY "; + if (err_ == GL_STACK_UNDERFLOW) + ret += "GL_STACK_UNDERFLOW "; + if (err_ == GL_STACK_OVERFLOW) + ret += "GL_STACK_OVERFLOW "; + + return ret; + } + + std::string toString() const override { + return "GLException: " + errToString() + "(" + std::to_string(static_cast(err_)) + ")"; + } + + private: + GLenum err_; + }; + + void glAfterCallback(glbinding::FunctionCall const& fc) + { + glbinding::setCallbackMask(glbinding::CallbackMask::None); + GLenum error = glGetError(); + glbinding::setCallbackMask(glbinding::CallbackMask::After); + if (error != GL_NO_ERROR) + throw GLException(error); + } + + + + Renderer::Renderer() + : sdlInit_(), window_(), context_() + { + glbinding::Binding::initialize(); + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + + window_.reset(SDL_CreateWindow("WC3 RE", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + 0, 0, + SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_OPENGL)); + if (!window_) + throw SDLException{}; + + context_ = SDLGLContext(window_.get()); + + SDL_GetWindowSize(window_.get(), &width_, &height_); + + glbinding::setCallbackMask(glbinding::CallbackMask::After); + glbinding::setAfterCallback(glAfterCallback); + +#ifndef NDEBUG + { + int depth, stencil, aa, major, minor; + SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth); + SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil); + SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &aa); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); + + printf("Depth: %d, Stencil: %d, AA: %d, GL %d.%d\n", + depth, stencil, aa, major, minor); + } +#endif + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glViewport(0, 0, width_, height_); + } + + void Renderer::run() + { + bool close = false; + auto last = SDL_GetTicks(); + while (!close && gamestates_.size()) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_q) + close = true; + break; + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_CLOSE) + close = true; + break; + } + } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + auto now = SDL_GetTicks(); + + gamestates_.back()->draw(now-last); + + SDL_GL_SwapWindow(window_.get()); + last = now; + } + } + + void Renderer::pushGS(std::unique_ptr gs) + { + gamestates_.emplace_back(std::move(gs)); + } + + std::unique_ptr Renderer::popGS() + { + auto ret = std::move(gamestates_.back()); + gamestates_.pop_back(); + return ret; + } + + int Renderer::getWidth() const + { + return width_; + } + + int Renderer::getHeight() const + { + return height_; + } } diff --git a/render/Renderer.hh b/render/Renderer.hh index a0bfae2..c58fc75 100644 --- a/render/Renderer.hh +++ b/render/Renderer.hh @@ -1,14 +1,32 @@ #ifndef WC3RE_RENDER_RENDERER_HH__ #define WC3RE_RENDER_RENDERER_HH__ -#include +#include "sdlutil.hh" -class Renderer { -public: - Renderer(); +namespace game { + class GameState; +} -private: - SDL_Window *win; -}; +namespace render { + class Renderer { + public: + Renderer(); + + void run(); + + int getWidth() const; + int getHeight() const; + + void pushGS(std::unique_ptr gs); + std::unique_ptr popGS(); + private: + SDLInit sdlInit_; + SDLWindowUPtr window_; + SDLGLContext context_; + std::vector > gamestates_; + int width_, height_; + }; + +} #endif diff --git a/render/VBOManager.cc b/render/VBOManager.cc new file mode 100644 index 0000000..19c17f0 --- /dev/null +++ b/render/VBOManager.cc @@ -0,0 +1,154 @@ +#include +#include +#include + +#include "VBOManager.hh" + +using namespace gl; + +template<> +std::unique_ptr Singleton::instance{nullptr}; +template<> +std::once_flag Singleton::instance_flag{}; + +namespace render { + VBOManager::VBOManager() + { + } + + VBOManager::~VBOManager() + { + } + + VBOManager::VBOAlloc::~VBOAlloc() + { + if(_vbo) + _vbo->free(*this); + _vbo = nullptr; + } + + VBOManager::VBOAlloc::VBOAlloc(VBOAlloc&& move) + : _vbo(move._vbo), _ofs(move._ofs), _size(move._size) + { + move._vbo = nullptr; + } + + VBOManager::VBOAlloc& VBOManager::VBOAlloc::operator=(VBOAlloc&& move) + { + _vbo = move._vbo; + _ofs = move._ofs; + _size = move._size; + move._vbo = nullptr; + return *this; + } + + VBOManager::VBOAlloc::VBOAlloc() + : _vbo(nullptr), _ofs(0), _size(0) + { + } + + VBOManager::VBOAlloc::VBOAlloc(VBO &vbo, size_t ofs, size_t size) + : _vbo(&vbo), _ofs(ofs), _size(size) + { + } + + VBOManager::VBOAlloc VBOManager::alloc(size_t size, GLenum type) + { + for(auto& vbo : _vbos[type]) { + try { + return vbo.alloc(size); + } catch (AllocFailed &ex) { + continue; + } + } + + _vbos[type].emplace_back((size>default_size)?size:default_size, type); + + return _vbos[type].back().alloc(size); + } + + VBOManager::VBO::VBO(size_t size, GLenum type) + : _bufID(0) + { + glGenBuffers(1, &_bufID); + glBindBuffer(GL_ARRAY_BUFFER, _bufID); + glBufferData(GL_ARRAY_BUFFER, size, NULL, type); + _allocs.emplace_back(_Entry{size, false}); + } + + VBOManager::VBO::VBO(VBO&& move) + : _bufID(move._bufID), _allocs(std::move(move._allocs)) + { + move._bufID = 0; + } + + VBOManager::VBO& VBOManager::VBO::operator=(VBO&& move) + { + for (auto ent : _allocs) { + assert(!ent.used); + } + if (_bufID) + glDeleteBuffers(1, &_bufID); + _bufID = move._bufID; + move._bufID = 0; + _allocs = std::move(move._allocs); + + return *this; + } + + VBOManager::VBO::~VBO() + { + for (auto ent : _allocs) { + assert(!ent.used); + } + if (_bufID) + glDeleteBuffers(1, &_bufID); + _bufID = 0; + } + + VBOManager::VBOAlloc VBOManager::VBO::alloc(size_t size) + { + if (size%alignment != 0) + size += (alignment - (size%alignment)); + + size_t pos = 0; + for (auto it = _allocs.begin();it != _allocs.end();++it) { + if (!it->used && (it->size >= size)) { + size_t leftover = it->size - size; + it->used = true; + it->size = size; + if (leftover > 0) + _allocs.insert(++it, _Entry{leftover, false}); + printf("DEBUG: VBO: Allocated %lu @ %lu in %u\n", size, pos, _bufID); + return VBOAlloc(*this, pos, size); + } + pos += it->size; + } + + throw AllocFailed(); + } + + void VBOManager::VBO::free(VBOAlloc& alloc) + { + size_t pos = 0; + for (auto it = _allocs.begin();it != _allocs.end();++it) { + if (pos == alloc._ofs) { + assert(it->size == alloc._size); + printf("DEBUG: VBO: Freed %lu @ %lu in %u\n", alloc._size, pos, _bufID); + it->used = false; + if ((std::next(it) != _allocs.end()) && + !std::next(it)->used) { + it->size += std::next(it)->size; + _allocs.erase(std::next(it)); + } + if ((it != _allocs.begin()) && + !std::prev(it)->used) { + it->size += std::prev(it)->size; + _allocs.erase(std::prev(it)); + } + return; + } + pos += it->size; + } + } +} diff --git a/render/VBOManager.hh b/render/VBOManager.hh new file mode 100644 index 0000000..ae21658 --- /dev/null +++ b/render/VBOManager.hh @@ -0,0 +1,124 @@ +#ifndef WC3RE_RENDER_VBOMANAGER_HH__ +#define WC3RE_RENDER_VBOMANAGER_HH__ + +#include +#include +#include +#include +#include + +#include +#include + +#include "Singleton.hh" + +namespace render { + class VBOManager : public Singleton { + public: + ~VBOManager(); + + VBOManager(VBOManager const& copy) = delete; + VBOManager& operator=(VBOManager const& copy) = delete; + + private: + VBOManager(); + friend class Singleton; + + class VBO; + + static const size_t default_size = 1048576; + static const size_t alignment = 8; + static const gl::GLenum default_type = gl::GL_STATIC_DRAW; + + class AllocFailed {}; + + public: + class VBOAlloc { + public: + ~VBOAlloc(); + + VBOAlloc(VBOAlloc&& move); + + VBOAlloc(VBOAlloc const& copy) = delete; + VBOAlloc& operator=(VBOAlloc const& copy) = delete; + + VBOAlloc& operator=(VBOAlloc&& move); + + VBOAlloc(); + + size_t getBase() const { + assert(_vbo); + return _ofs; + } + + void * getOfs(size_t ofs) const { + assert(_vbo); + assert(ofs < _size); + return reinterpret_cast(_ofs + ofs); + } + + size_t getSize() const { + assert(_vbo); + return _size; + } + + gl::GLuint getVBOId() const { + assert(_vbo); + return _vbo->getID(); + } + + explicit operator bool() const noexcept { + return (_vbo != nullptr); + } + + private: + VBOAlloc(VBO &vbo, size_t ofs, size_t size); + + friend class VBOManager; + friend class VBO; + + VBO *_vbo; + size_t _ofs, _size; + }; + + VBOAlloc alloc(size_t size, gl::GLenum type = default_type); + + private: + + class VBO { + public: + VBO(size_t size = default_size, gl::GLenum type = default_type); + + VBO(VBO const& copy) = delete; + VBO& operator=(VBO const& copy) = delete; + + VBO(VBO&& move); + + VBO& operator=(VBO&& move); + + ~VBO(); + + VBOAlloc alloc(size_t size); + + void free(VBOAlloc& alloc); + + gl::GLuint getID() const { + return _bufID; + } + + private: + gl::GLuint _bufID; + + struct _Entry { + size_t size; + bool used; + }; + std::list<_Entry> _allocs; + + }; + + std::map > _vbos; + }; +} + +#endif diff --git a/render/renderutil.cc b/render/renderutil.cc new file mode 100644 index 0000000..f07627a --- /dev/null +++ b/render/renderutil.cc @@ -0,0 +1,42 @@ +#include +#include + +#include "renderutil.hh" + +using namespace gl; + +namespace render { + namespace { + unsigned ilog2(unsigned in) + { + unsigned ret = 0u; + while (in >>= 1) ++ret; + return ret; + } + } + + TextureResource create2DTexture(unsigned width, unsigned height, bool alpha, unsigned levels) + { + TextureResource tex; + glGenTextures(1, &tex.get()); + + glBindTexture(GL_TEXTURE_2D, tex); + unsigned logWidth = ilog2(width), logHeight = ilog2(height); + if (levels == 0) + levels = std::max(logWidth,logHeight)+1u; + if(SDL_GL_ExtensionSupported("GL_ARB_texture_storage")) + glTexStorage2D(GL_TEXTURE_2D, levels, alpha?GL_RGBA8:GL_RGB8, width, height); + else { + std::printf("Warning: extension GL_ARB_texture_storage not supported!\n"); + for (unsigned i = 0u; i < levels; ++i) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels-1); + glTexImage2D(GL_TEXTURE_2D, i, static_cast(alpha?GL_RGBA8:GL_RGB8), width, height, 0, + alpha?GL_RGBA8:GL_RGB8, GL_UNSIGNED_BYTE, NULL); + width = std::max(1u, (width / 2u)); + height = std::max(1u, (height / 2u)); + } + } + + return tex; + } +} diff --git a/render/renderutil.hh b/render/renderutil.hh new file mode 100644 index 0000000..35bb68b --- /dev/null +++ b/render/renderutil.hh @@ -0,0 +1,10 @@ +#ifndef WC3RE_RENDER_RENDERUTIL_HH__ +#define WC3RE_RENDER_RENDERUTIL_HH__ + +#include "GlResource.hh" + +namespace render { + TextureResource create2DTexture(unsigned width, unsigned height, bool alpha, unsigned levels = 0); +} + +#endif diff --git a/render/sdlutil.hh b/render/sdlutil.hh new file mode 100644 index 0000000..580048c --- /dev/null +++ b/render/sdlutil.hh @@ -0,0 +1,141 @@ +#ifndef WC3RE_RENDER_SDLUTIL_HH__ +#define WC3RE_RENDER_SDLUTIL_HH__ + +#include +#include + +#include + +#include "exceptions.hh" + +namespace render { + + class SDLException : public Exception { + public: + SDLException() : Exception(SDL_GetError()) { + } + + std::string toString() const override { + return "SDLException: " + msg_; + } + }; + // Some helpers to C++11-ify SDL + + struct SDLSurfaceDeleter { + void operator()(SDL_Surface *ptr) const { + SDL_FreeSurface(ptr); + } + }; + + using SDLSurfaceUPtr = std::unique_ptr; + + struct SDLWindowDeleter { + void operator()(SDL_Window *ptr) const { + SDL_DestroyWindow(ptr); + } + }; + + using SDLWindowUPtr = 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)); + } + + // RAII wrapper for SDL_Init + class SDLInit { + public: + SDLInit(uint32_t flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO) { + if (SDL_Init(flags) < 0) + throw SDLException{}; + } + + SDLInit(SDLInit const& copy) = delete; + SDLInit& operator=(SDLInit const& copy) = delete; + + ~SDLInit() { + SDL_Quit(); + } + }; + + // RAII wrapper for SDL_GLContext + class SDLGLContext { + public: + SDLGLContext(SDL_Window *win) + : context_(SDL_GL_CreateContext(win)) { + if (!context_) + throw SDLException{}; + } + + SDLGLContext() + : context_(nullptr) { + } + + SDLGLContext(SDLGLContext const& copy) = delete; + SDLGLContext& operator=(SDLGLContext const& copy) = delete; + + SDLGLContext(SDLGLContext && move): + context_(move.context_) { + move.context_ = nullptr; + } + + SDLGLContext& operator=(SDLGLContext && move) { + if (context_) + SDL_GL_DeleteContext(context_); + context_ = move.context_; + move.context_ = nullptr; + + return *this; + } + + ~SDLGLContext() { + if (context_) + SDL_GL_DeleteContext(context_); + } + + operator SDL_GLContext() const { + return context_; + } + private: + SDL_GLContext context_; + }; + +} + +#endif diff --git a/shaders/overlay.fs b/shaders/overlay.fs new file mode 100644 index 0000000..07c66ed --- /dev/null +++ b/shaders/overlay.fs @@ -0,0 +1,12 @@ +#version 330 core +#extension GL_ARB_explicit_uniform_location : enable + +layout(location = 1) uniform sampler2D texBase; + +in vec2 fragTC; + +out vec4 color; + +void main(void) { + color = texture(texBase, fragTC); +} diff --git a/shaders/overlay.vs b/shaders/overlay.vs new file mode 100644 index 0000000..09e7293 --- /dev/null +++ b/shaders/overlay.vs @@ -0,0 +1,16 @@ +#version 330 core +#extension GL_ARB_shading_language_420pack : enable +#extension GL_ARB_explicit_uniform_location : enable + +layout(location = 0) uniform mat4 projection_matrix; + +layout(location = 0) in vec2 vertex; +layout(location = 1) in vec2 vertexTC; + +out vec2 fragTC; + +void main(void) { + vec4 pos = projection_matrix * vec4(vertex, 0.0, 1.0); + gl_Position = pos; + fragTC = vertexTC; +} diff --git a/util.cc b/util.cc index 428fba9..36991b1 100644 --- a/util.cc +++ b/util.cc @@ -158,3 +158,26 @@ int sextend(unsigned b, unsigned msb) return b&((1< 0) { + buf[p] = '\0'; + ret.append(buf); + } + if (!std::feof(file)) { + std::fclose(file); + throw POSIXException(errno); + } + + std::fclose(file); + return ret; +} diff --git a/util.hh b/util.hh index 327e185..d3d9cc4 100644 --- a/util.hh +++ b/util.hh @@ -42,4 +42,6 @@ uint32_t readU32LE(char const* data); // Sign-extend b starting at msb int sextend(unsigned b, unsigned msb); +// Load simple resource from file +std::string fileToString(std::string const& name); #endif