WIP: SDL/OpenGL renderer. Make mvedecode use renderer for playback.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
game/
|
data/
|
||||||
objs/
|
objs/
|
||||||
tmp/
|
tmp/
|
||||||
*~
|
*~
|
||||||
|
|||||||
9
Makefile
9
Makefile
@@ -1,8 +1,9 @@
|
|||||||
CXX=g++
|
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
|
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_CXXSRCS ::= iffexplore.cc IffFile.cc util.cc exceptions.cc
|
||||||
iffexplore_LIBS ::=
|
iffexplore_LIBS ::=
|
||||||
@@ -13,8 +14,8 @@ treexplore_LIBS ::=
|
|||||||
font2png_CXXSRCS ::= font2png.cc
|
font2png_CXXSRCS ::= font2png.cc
|
||||||
font2png_LIBS ::= -lpng
|
font2png_LIBS ::= -lpng
|
||||||
|
|
||||||
mvedecode_CXXSRCS ::= mvedecode.cc TreFile.cc IffFile.cc util.cc MveDecoder.cc exceptions.cc decompress.cc $(render_CXXSRCS)
|
mvedecode_CXXSRCS ::= mvedecode.cc TreFile.cc IffFile.cc util.cc MveDecoder.cc exceptions.cc decompress.cc $(render_CXXSRCS) $(game_CXXSRCS)
|
||||||
mvedecode_LIBS ::= -lSDL2
|
mvedecode_LIBS ::= -lSDL2 -lglbinding
|
||||||
|
|
||||||
progs ::= iffexplore treexplore mvedecode
|
progs ::= iffexplore treexplore mvedecode
|
||||||
|
|
||||||
|
|||||||
100
MveDecoder.cc
100
MveDecoder.cc
@@ -207,6 +207,76 @@ std::vector<uint8_t> 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<Shot>::const_iterator shotIt,
|
||||||
|
std::vector<Shot>::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::Frame const&, MveDecoder::Palette const&> 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
|
MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const* prev) const
|
||||||
{
|
{
|
||||||
// Parse header
|
// Parse header
|
||||||
@@ -242,7 +312,7 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
|
|||||||
|
|
||||||
// Interpret command stream to render frame
|
// Interpret command stream to render frame
|
||||||
Frame ret;
|
Frame ret;
|
||||||
ret.pixels.reserve(width_*height_);
|
ret.reserve(width_*height_);
|
||||||
|
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
size_t seg2Idx = 0;
|
size_t seg2Idx = 0;
|
||||||
@@ -320,12 +390,12 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
|
|||||||
#endif
|
#endif
|
||||||
if (!prev)
|
if (!prev)
|
||||||
throw FormatException{"Reference to non-existing prev. frame"};
|
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"};
|
throw FormatException{"Copy from prev exceeds frame"};
|
||||||
|
|
||||||
std::copy(prev->pixels.begin()+ret.pixels.size(),
|
std::copy(prev->begin()+ret.size(),
|
||||||
prev->pixels.begin()+ret.pixels.size()+size,
|
prev->begin()+ret.size()+size,
|
||||||
std::back_inserter(ret.pixels));
|
std::back_inserter(ret));
|
||||||
} else {
|
} else {
|
||||||
#ifdef CMDDEBUG_
|
#ifdef CMDDEBUG_
|
||||||
printf("DataCopy: %u @%lu\n", size, seg4Idx);
|
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,
|
std::copy(pixels.begin()+seg4Idx,
|
||||||
pixels.begin()+seg4Idx+size,
|
pixels.begin()+seg4Idx+size,
|
||||||
std::back_inserter(ret.pixels));
|
std::back_inserter(ret));
|
||||||
seg4Idx += size;
|
seg4Idx += size;
|
||||||
}
|
}
|
||||||
} else {
|
} 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 uint8_t mpos = vga.begin()[segOfs[2]+seg3Idx++];
|
||||||
const signed x = sextend(mpos>>4, 4), y = sextend(mpos&0xf,4);
|
const signed x = sextend(mpos>>4, 4), y = sextend(mpos&0xf,4);
|
||||||
const signed delta = y*width_+x;
|
const signed delta = y*width_+x;
|
||||||
if ((delta < 0) && (ret.pixels.size() < static_cast<size_t>(-delta)))
|
if ((delta < 0) && (ret.size() < static_cast<size_t>(-delta)))
|
||||||
throw FormatException{"Motion vector outside frame"};
|
throw FormatException{"Motion vector outside frame"};
|
||||||
|
|
||||||
const unsigned ref = ret.pixels.size()+delta;
|
const unsigned ref = ret.size()+delta;
|
||||||
#ifdef CMDDEBUG_
|
#ifdef CMDDEBUG_
|
||||||
printf("@%lu %.2hhx -> (%d, %d) -> d %d -> %u\n", seg3Idx-1, mpos, x, y, delta, ref);
|
printf("@%lu %.2hhx -> (%d, %d) -> d %d -> %u\n", seg3Idx-1, mpos, x, y, delta, ref);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ref+size > prev->pixels.size())
|
if (ref+size > prev->size())
|
||||||
throw FormatException{"Copy from prev exceeds frame"};
|
throw FormatException{"Copy from prev exceeds frame"};
|
||||||
|
|
||||||
std::copy(prev->pixels.begin()+ref,
|
std::copy(prev->begin()+ref,
|
||||||
prev->pixels.begin()+ref+size,
|
prev->begin()+ref+size,
|
||||||
std::back_inserter(ret.pixels));
|
std::back_inserter(ret));
|
||||||
flag = false;
|
flag = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret.pixels.size() != width_*height_)
|
if (ret.size() != width_*height_)
|
||||||
throw FormatException{"Decoded frame dimension mismatch"};
|
throw FormatException{"Decoded frame dimension mismatch"};
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
@@ -380,7 +450,7 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
struct AudioCBData {
|
struct AudioCBData {
|
||||||
@@ -651,4 +721,4 @@ void MveDecoder::play(unsigned branch) const
|
|||||||
printf("Frame times: [%u, %u]\n",
|
printf("Frame times: [%u, %u]\n",
|
||||||
minFT, maxFT);
|
minFT, maxFT);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -14,25 +14,52 @@ public:
|
|||||||
return branches_.size();
|
return branches_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void play(unsigned branch) const;
|
using Frame = std::vector<uint8_t>;
|
||||||
private:
|
using Audio = std::vector<uint8_t>;
|
||||||
IffFile iff_;
|
|
||||||
|
|
||||||
using Palette = std::array<uint8_t, 768>;
|
using Palette = std::array<uint8_t, 768>;
|
||||||
|
private:
|
||||||
struct Frame {
|
using AUDIVec = std::vector<IffFile::Object const*>;
|
||||||
std::vector<uint8_t> pixels;
|
using VGAVec = std::vector<IffFile::Object const*>;
|
||||||
};
|
|
||||||
|
|
||||||
struct Shot {
|
struct Shot {
|
||||||
Palette const& palt;
|
Palette const& palt;
|
||||||
std::vector<IffFile::Object const*> AUDIs;
|
AUDIVec AUDIs;
|
||||||
std::vector<IffFile::Object const*> VGAs;
|
VGAVec VGAs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
class Movie {
|
||||||
|
public:
|
||||||
|
bool hasNext() const;
|
||||||
|
std::tuple<Frame const&, Palette const&> decodeNext();
|
||||||
|
|
||||||
|
|
||||||
|
bool hasAudio() const;
|
||||||
|
Audio getNextAudio();
|
||||||
|
|
||||||
|
unsigned getWidth() const;
|
||||||
|
unsigned getHeight() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Movie(std::vector<Shot>::const_iterator shotIt,
|
||||||
|
std::vector<Shot>::const_iterator shotEnd,
|
||||||
|
MveDecoder const& mve);
|
||||||
|
|
||||||
|
std::vector<Shot>::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_;
|
unsigned width_, height_;
|
||||||
|
|
||||||
//std::vector<Shot> shots_;
|
|
||||||
std::vector<Palette> palts_;
|
std::vector<Palette> palts_;
|
||||||
std::vector<std::vector<Shot> > branches_;
|
std::vector<std::vector<Shot> > branches_;
|
||||||
|
|
||||||
|
|||||||
24
Singleton.hh
Normal file
24
Singleton.hh
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef WC3RE_SINGLETON_HH__
|
||||||
|
#define WC3RE_SINGLETON_HH__
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Singleton {
|
||||||
|
public:
|
||||||
|
static T& getInstance() {
|
||||||
|
call_once(instance_flag, init);
|
||||||
|
return *instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::unique_ptr<T> instance;
|
||||||
|
static std::once_flag instance_flag;
|
||||||
|
|
||||||
|
static void init() {
|
||||||
|
instance.reset(new T{});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
94
game/GSMvePlay.cc
Normal file
94
game/GSMvePlay.cc
Normal file
@@ -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<float>(mveWidth)/(mveHeight*mvePAR)) > (static_cast<float>(scrWidth)/scrHeight)) {
|
||||||
|
// letterbox
|
||||||
|
int newScrHeight = scrWidth/(static_cast<float>(mveWidth)/(mveHeight*mvePAR));
|
||||||
|
top = (scrHeight-newScrHeight)/2;
|
||||||
|
scrHeight = newScrHeight;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// pillarbox
|
||||||
|
int newScrWidth = scrHeight*(static_cast<float>(mveWidth)/(mveHeight*mvePAR));
|
||||||
|
left = (scrWidth-newScrWidth)/2;
|
||||||
|
scrWidth = newScrWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
overlay_ = std::make_unique<render::Overlay>(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();
|
||||||
|
}
|
||||||
|
}
|
||||||
32
game/GSMvePlay.hh
Normal file
32
game/GSMvePlay.hh
Normal file
@@ -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<render::Overlay> overlay_;
|
||||||
|
unsigned delta_;
|
||||||
|
float nextFT_;
|
||||||
|
std::vector<uint8_t> frameRGB_;
|
||||||
|
|
||||||
|
void decode_();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
25
game/GameState.hh
Normal file
25
game/GameState.hh
Normal file
@@ -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
|
||||||
22
mvedecode.cc
22
mvedecode.cc
@@ -12,6 +12,10 @@
|
|||||||
#include "TreFile.hh"
|
#include "TreFile.hh"
|
||||||
#include "IffFile.hh"
|
#include "IffFile.hh"
|
||||||
#include "MveDecoder.hh"
|
#include "MveDecoder.hh"
|
||||||
|
#include "render/Renderer.hh"
|
||||||
|
#include "game/GSMvePlay.hh"
|
||||||
|
|
||||||
|
using render::Renderer;
|
||||||
|
|
||||||
void usage(char *argv0) {
|
void usage(char *argv0) {
|
||||||
fprintf(stderr, "Usage: %s [-h] (tre-file name/crc)/iff-file\n", 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<MveDecoder>(mmap.data(), mmap.size());
|
mve = std::make_unique<MveDecoder>(mmap.data(), mmap.size());
|
||||||
|
|
||||||
if (play) {
|
if (play) {
|
||||||
if (branch >= 0)
|
Renderer renderer;
|
||||||
mve->play(branch);
|
|
||||||
else
|
if (branch >= 0) {
|
||||||
for(unsigned i = 0;i < mve->numBranches();++i)
|
auto movie = mve->open(branch);
|
||||||
mve->play(i);
|
renderer.pushGS(std::make_unique<game::GSMvePlay>(renderer, movie));
|
||||||
|
renderer.run();
|
||||||
|
} else {
|
||||||
|
for(unsigned i = 0;i < mve->numBranches();++i) {
|
||||||
|
auto movie = mve->open(i);
|
||||||
|
renderer.pushGS(std::make_unique<game::GSMvePlay>(renderer, movie));
|
||||||
|
renderer.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (POSIXException &ex) {
|
} catch (POSIXException &ex) {
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|||||||
23
render/Drawable.hh
Normal file
23
render/Drawable.hh
Normal file
@@ -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
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
using namespace gl;
|
using namespace gl;
|
||||||
|
|
||||||
|
namespace render {
|
||||||
|
|
||||||
void TextureDeleter::operator()(gl::GLuint tex) const {
|
void TextureDeleter::operator()(gl::GLuint tex) const {
|
||||||
glDeleteTextures(1, &tex);
|
glDeleteTextures(1, &tex);
|
||||||
}
|
}
|
||||||
@@ -11,3 +13,16 @@ void TextureDeleter::operator()(gl::GLuint tex) const {
|
|||||||
void TextureDeleter::operator() (gl::GLsizei count, gl::GLuint tex[]) const {
|
void TextureDeleter::operator() (gl::GLsizei count, gl::GLuint tex[]) const {
|
||||||
glDeleteTextures(count, tex);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <glbinding/gl/types.h>
|
#include <glbinding/gl/types.h>
|
||||||
|
|
||||||
|
namespace render {
|
||||||
struct TextureDeleter {
|
struct TextureDeleter {
|
||||||
void operator() (gl::GLuint tex) const;
|
void operator() (gl::GLuint tex) const;
|
||||||
void operator() (gl::GLsizei count, gl::GLuint tex[]) const;
|
void operator() (gl::GLsizei count, gl::GLuint tex[]) const;
|
||||||
@@ -39,16 +40,34 @@ public:
|
|||||||
: handle_(handle) {
|
: handle_(handle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GlResource(GlResource const& copy) = delete;
|
||||||
|
GlResource& operator=(GlResource const& copy) = delete;
|
||||||
|
|
||||||
|
GlResource(GlResource && move)
|
||||||
|
: handle_(move.handle_) {
|
||||||
|
move.handle_ = Handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
GlResource& operator=(GlResource && move) {
|
||||||
|
if (handle_)
|
||||||
|
Deleter()(handle_);
|
||||||
|
handle_ = move.handle_;
|
||||||
|
move.handle_ = Handle();
|
||||||
|
}
|
||||||
|
|
||||||
~GlResource() {
|
~GlResource() {
|
||||||
if (handle_)
|
if (handle_)
|
||||||
Deleter()(handle_);
|
Deleter()(handle_);
|
||||||
handle_ = Handle();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
operator Handle() const {
|
operator Handle() const {
|
||||||
return handle_;
|
return handle_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator Handle&() {
|
||||||
|
return handle_;
|
||||||
|
}
|
||||||
|
|
||||||
Handle& get() {
|
Handle& get() {
|
||||||
return handle_;
|
return handle_;
|
||||||
}
|
}
|
||||||
@@ -68,4 +87,6 @@ using ProgramResource = GlResource<ProgramDeleter>;
|
|||||||
using VertexArrayResource = GlResource<VertexArrayDeleter>;
|
using VertexArrayResource = GlResource<VertexArrayDeleter>;
|
||||||
using BufferResource = GlResource<BufferDeleter>;
|
using BufferResource = GlResource<BufferDeleter>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
97
render/Overlay.cc
Normal file
97
render/Overlay.cc
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#include <glbinding/gl/gl.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#include <glm/gtx/transform.hpp>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
|
#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<int>(GL_LINEAR));
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast<int>(GL_CLAMP_TO_EDGE));
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast<int>(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> 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<float>(renderer_.getWidth()),
|
||||||
|
static_cast<float>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
render/Overlay.hh
Normal file
33
render/Overlay.hh
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef WC3RE_RENDER_OVERLAY_HH__
|
||||||
|
#define WC3RE_RENDER_OVERLAY_HH__
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#define GLM_FORCE_RADIANS
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#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
|
||||||
99
render/ProgramProvider.cc
Normal file
99
render/ProgramProvider.cc
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#include <mutex>
|
||||||
|
#include <glbinding/gl/gl.h>
|
||||||
|
|
||||||
|
#include "ProgramProvider.hh"
|
||||||
|
#include "exceptions.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
|
||||||
|
using namespace gl;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::unique_ptr<render::ProgramProvider> Singleton<render::ProgramProvider>::instance{nullptr};
|
||||||
|
template<>
|
||||||
|
std::once_flag Singleton<render::ProgramProvider>::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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
41
render/ProgramProvider.hh
Normal file
41
render/ProgramProvider.hh
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef WC3RE_RENDER_PROGRAMPROVIDER_HH__
|
||||||
|
#define WC3RE_RENDER_PROGRAMPROVIDER_HH__
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "Singleton.hh"
|
||||||
|
#include "GlResource.hh"
|
||||||
|
namespace render {
|
||||||
|
using ProgramProviderKeyType = std::tuple<std::string, std::string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
struct hash<render::ProgramProviderKeyType> {
|
||||||
|
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::string>()(std::get<0>(s))};
|
||||||
|
const result_type h2{std::hash<std::string>()(std::get<1>(s))};
|
||||||
|
return h1 ^ h2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace render {
|
||||||
|
class ProgramProvider : public Singleton<ProgramProvider> {
|
||||||
|
private:
|
||||||
|
ProgramProvider();
|
||||||
|
friend class Singleton<ProgramProvider>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
gl::GLuint getProgram(std::string const& vertexShader, std::string const& fragShader);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<ProgramProviderKeyType, ProgramResource> programCache_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,12 +1,152 @@
|
|||||||
#include <glbinding/gl/gl.h>
|
#include <glbinding/gl/gl.h>
|
||||||
|
#include <glbinding/Binding.h>
|
||||||
|
#include <glbinding/callbacks.h>
|
||||||
|
|
||||||
#include "Renderer.hh"
|
#include "Renderer.hh"
|
||||||
#include "GlResource.hh"
|
#include "GlResource.hh"
|
||||||
|
#include "game/GameState.hh"
|
||||||
|
#include "exceptions.hh"
|
||||||
|
|
||||||
using namespace gl;
|
using namespace gl;
|
||||||
|
|
||||||
Renderer::Renderer()
|
namespace render {
|
||||||
{
|
class GLException : public Exception {
|
||||||
TextureResource testTex;
|
public:
|
||||||
glGenTextures(1, &testTex.get());
|
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<int>(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<game::GameState> gs)
|
||||||
|
{
|
||||||
|
gamestates_.emplace_back(std::move(gs));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<game::GameState> 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_;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,32 @@
|
|||||||
#ifndef WC3RE_RENDER_RENDERER_HH__
|
#ifndef WC3RE_RENDER_RENDERER_HH__
|
||||||
#define WC3RE_RENDER_RENDERER_HH__
|
#define WC3RE_RENDER_RENDERER_HH__
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include "sdlutil.hh"
|
||||||
|
|
||||||
|
namespace game {
|
||||||
|
class GameState;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace render {
|
||||||
class Renderer {
|
class Renderer {
|
||||||
public:
|
public:
|
||||||
Renderer();
|
Renderer();
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
int getWidth() const;
|
||||||
|
int getHeight() const;
|
||||||
|
|
||||||
|
void pushGS(std::unique_ptr<game::GameState> gs);
|
||||||
|
std::unique_ptr<game::GameState> popGS();
|
||||||
private:
|
private:
|
||||||
SDL_Window *win;
|
SDLInit sdlInit_;
|
||||||
|
SDLWindowUPtr window_;
|
||||||
|
SDLGLContext context_;
|
||||||
|
std::vector<std::unique_ptr<game::GameState> > gamestates_;
|
||||||
|
int width_, height_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
154
render/VBOManager.cc
Normal file
154
render/VBOManager.cc
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
#include <glbinding/gl/gl.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "VBOManager.hh"
|
||||||
|
|
||||||
|
using namespace gl;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::unique_ptr<render::VBOManager> Singleton<render::VBOManager>::instance{nullptr};
|
||||||
|
template<>
|
||||||
|
std::once_flag Singleton<render::VBOManager>::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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
124
render/VBOManager.hh
Normal file
124
render/VBOManager.hh
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#ifndef WC3RE_RENDER_VBOMANAGER_HH__
|
||||||
|
#define WC3RE_RENDER_VBOMANAGER_HH__
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <cassert>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <glbinding/gl/types.h>
|
||||||
|
#include <glbinding/gl/enum.h>
|
||||||
|
|
||||||
|
#include "Singleton.hh"
|
||||||
|
|
||||||
|
namespace render {
|
||||||
|
class VBOManager : public Singleton<VBOManager> {
|
||||||
|
public:
|
||||||
|
~VBOManager();
|
||||||
|
|
||||||
|
VBOManager(VBOManager const& copy) = delete;
|
||||||
|
VBOManager& operator=(VBOManager const& copy) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
VBOManager();
|
||||||
|
friend class Singleton<VBOManager>;
|
||||||
|
|
||||||
|
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<void*>(_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<gl::GLenum, std::vector<VBO> > _vbos;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
42
render/renderutil.cc
Normal file
42
render/renderutil.cc
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#include <glbinding/gl/gl.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#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<const int>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
render/renderutil.hh
Normal file
10
render/renderutil.hh
Normal file
@@ -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
|
||||||
141
render/sdlutil.hh
Normal file
141
render/sdlutil.hh
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#ifndef WC3RE_RENDER_SDLUTIL_HH__
|
||||||
|
#define WC3RE_RENDER_SDLUTIL_HH__
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#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<SDL_Surface, SDLSurfaceDeleter>;
|
||||||
|
|
||||||
|
struct SDLWindowDeleter {
|
||||||
|
void operator()(SDL_Window *ptr) const {
|
||||||
|
SDL_DestroyWindow(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using SDLWindowUPtr = std::unique_ptr<SDL_Window, SDLWindowDeleter>;
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
12
shaders/overlay.fs
Normal file
12
shaders/overlay.fs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
16
shaders/overlay.vs
Normal file
16
shaders/overlay.vs
Normal file
@@ -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;
|
||||||
|
}
|
||||||
23
util.cc
23
util.cc
@@ -158,3 +158,26 @@ int sextend(unsigned b, unsigned msb)
|
|||||||
return b&((1<<msb)-1);
|
return b&((1<<msb)-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string fileToString(std::string const& name) {
|
||||||
|
std::FILE *file = std::fopen(name.c_str(), "r");
|
||||||
|
if (!file) {
|
||||||
|
throw POSIXException(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ret;
|
||||||
|
char buf[512];
|
||||||
|
std::size_t p;
|
||||||
|
while((p = std::fread(buf, 1, 511, file)) > 0) {
|
||||||
|
buf[p] = '\0';
|
||||||
|
ret.append(buf);
|
||||||
|
}
|
||||||
|
if (!std::feof(file)) {
|
||||||
|
std::fclose(file);
|
||||||
|
throw POSIXException(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fclose(file);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
2
util.hh
2
util.hh
@@ -42,4 +42,6 @@ uint32_t readU32LE(char const* data);
|
|||||||
// Sign-extend b starting at msb
|
// Sign-extend b starting at msb
|
||||||
int sextend(unsigned b, unsigned msb);
|
int sextend(unsigned b, unsigned msb);
|
||||||
|
|
||||||
|
// Load simple resource from file
|
||||||
|
std::string fileToString(std::string const& name);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user