Files
wc3re/render/Renderer.cc

206 lines
4.8 KiB
C++

#include <glbinding/gl/gl.h>
#include <glbinding/Binding.h>
#include <glbinding/callbacks.h>
#include <glbinding/Meta.h>
#include "Renderer.hh"
#include "GlResource.hh"
#include "game/GameState.hh"
#include "exceptions.hh"
#include "Overlay.hh"
using namespace gl;
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_(), ttfInit_(), window_(), context_()
{
glbinding::Binding::initialize();
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
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 major, minor;
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
printf("GL %d.%d\n",
major, minor);
}
#endif
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glViewport(0, 0, width_, height_);
fpsFont_.reset(TTF_OpenFont("DejaVuSans.ttf", 10));
if (!fpsFont_)
throw TTFException{};
SDL_Color white;
white.r = 255;
white.g = 255;
white.b = 255;
white.a = 255;
SDLSurfaceUPtr fpsSurf(TTF_RenderUTF8_Blended(fpsFont_.get(), "0 FPS", white));
if (!fpsSurf)
throw TTFException{};
fpsOverlay_ = std::make_unique<Overlay>(*this, fpsSurf->w*2, fpsSurf->h, 0, 0, fpsSurf->w*2, fpsSurf->h);
fpsOverlay_->clear();
fpsOverlay_->setContent(fpsSurf.get());
}
Renderer::~Renderer()
{
}
void Renderer::run()
{
bool close = false;
auto last = SDL_GetTicks();
unsigned fpsTime = 0, fpsCount = 0, lastFps = 0;
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;
continue;
}
break;
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_CLOSE) {
close = true;
continue;
}
break;
}
if (gamestates_.back()->handleEvent(event))
continue;
break;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
auto now = SDL_GetTicks();
auto delta = now-last;
gamestates_.back()->draw(delta);
if (fpsTime+delta > 1000) {
if (fpsCount+1 != lastFps) {
std::string fpsText{std::to_string(fpsCount+1) + " FPS"};
SDLSurfaceUPtr fpsSurf(TTF_RenderUTF8_Blended(fpsFont_.get(), fpsText.c_str(),
SDL_Color({255, 255, 255, 255})));
if (!fpsSurf)
throw TTFException{};
fpsOverlay_->clear();
fpsOverlay_->setContent(fpsSurf.get());
lastFps = fpsCount+1;
}
fpsCount = 0;
fpsTime = 0;
} else {
++fpsCount;
fpsTime += delta;
}
fpsOverlay_->draw();
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_;
}
}