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/
|
||||
tmp/
|
||||
*~
|
||||
|
||||
9
Makefile
9
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
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
// 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<size_t>(-delta)))
|
||||
if ((delta < 0) && (ret.size() < static_cast<size_t>(-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 <SDL2/SDL.h>
|
||||
|
||||
struct AudioCBData {
|
||||
@@ -651,4 +721,4 @@ void MveDecoder::play(unsigned branch) const
|
||||
printf("Frame times: [%u, %u]\n",
|
||||
minFT, maxFT);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -14,25 +14,52 @@ public:
|
||||
return branches_.size();
|
||||
}
|
||||
|
||||
void play(unsigned branch) const;
|
||||
private:
|
||||
IffFile iff_;
|
||||
|
||||
using Frame = std::vector<uint8_t>;
|
||||
using Audio = std::vector<uint8_t>;
|
||||
using Palette = std::array<uint8_t, 768>;
|
||||
|
||||
struct Frame {
|
||||
std::vector<uint8_t> pixels;
|
||||
};
|
||||
private:
|
||||
using AUDIVec = std::vector<IffFile::Object const*>;
|
||||
using VGAVec = std::vector<IffFile::Object const*>;
|
||||
|
||||
struct Shot {
|
||||
Palette const& palt;
|
||||
std::vector<IffFile::Object const*> AUDIs;
|
||||
std::vector<IffFile::Object const*> VGAs;
|
||||
AUDIVec AUDIs;
|
||||
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_;
|
||||
|
||||
//std::vector<Shot> shots_;
|
||||
std::vector<Palette> palts_;
|
||||
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 "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<MveDecoder>(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<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) {
|
||||
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,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,69 +3,90 @@
|
||||
|
||||
#include <glbinding/gl/types.h>
|
||||
|
||||
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 Deleter, class Handle = gl::GLuint>
|
||||
class GlResource {
|
||||
public:
|
||||
GlResource()
|
||||
: handle_() {
|
||||
}
|
||||
template<class Deleter, class Handle = gl::GLuint>
|
||||
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<TextureDeleter>;
|
||||
using FramebufferResource = GlResource<FramebufferDeleter>;
|
||||
using ShaderResource = GlResource<ShaderDeleter>;
|
||||
using ProgramResource = GlResource<ProgramDeleter>;
|
||||
using VertexArrayResource = GlResource<VertexArrayDeleter>;
|
||||
using BufferResource = GlResource<BufferDeleter>;
|
||||
using TextureResource = GlResource<TextureDeleter>;
|
||||
using FramebufferResource = GlResource<FramebufferDeleter>;
|
||||
using ShaderResource = GlResource<ShaderDeleter>;
|
||||
using ProgramResource = GlResource<ProgramDeleter>;
|
||||
using VertexArrayResource = GlResource<VertexArrayDeleter>;
|
||||
using BufferResource = GlResource<BufferDeleter>;
|
||||
|
||||
}
|
||||
|
||||
#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/Binding.h>
|
||||
#include <glbinding/callbacks.h>
|
||||
|
||||
#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<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__
|
||||
#define WC3RE_RENDER_RENDERER_HH__
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#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<game::GameState> gs);
|
||||
std::unique_ptr<game::GameState> popGS();
|
||||
private:
|
||||
SDLInit sdlInit_;
|
||||
SDLWindowUPtr window_;
|
||||
SDLGLContext context_;
|
||||
std::vector<std::unique_ptr<game::GameState> > gamestates_;
|
||||
int width_, height_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user