WIP: SDL/OpenGL renderer. Make mvedecode use renderer for playback.
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user