Draw textures.

main
bog 2023-11-14 01:14:08 +01:00
parent 92887779ba
commit a9567e703c
20 changed files with 834 additions and 5 deletions

BIN
assets/images/walk_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

View File

@ -0,0 +1,19 @@
#version 130
in vec4 _color;
in vec2 _tex_coord;
out vec4 frag_color;
uniform sampler2D sampler;
uniform int has_texture;
void main() {
if (has_texture == 1)
{
frag_color = _color * texture(sampler, _tex_coord);
}
else
{
frag_color = _color;
}
}

View File

@ -0,0 +1,16 @@
#version 130
in vec3 pos;
in vec4 color;
in vec2 tex_coord;
out vec4 _color;
out vec2 _tex_coord;
uniform mat4 proj;
void main() {
gl_Position = proj * vec4(pos, 1.0);
_color = color;
_tex_coord = tex_coord;
}

View File

@ -24,8 +24,25 @@ executable(
'rest-in-dust',
sources: [
'src/main.cpp',
# game
'src/Game.cpp',
'src/BaseScene.cpp',
# arena
'src/arena/Arena.cpp',
# gfx
'src/gfx/Window.cpp',
'src/gfx/Shaders.cpp',
'src/gfx/Canvas.cpp',
'src/gfx/Texture.cpp',
],
dependencies: [
dependency('glew'),
dependency('sdl2'),
dependency('SDL2_image'),
dependency('glm'),
],
install: true
)

13
src/BaseScene.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "BaseScene.hpp"
namespace rid
{
/*explicit*/ BaseScene::BaseScene(Game& game)
: m_game { game }
{
}
/*virtual*/ BaseScene::~BaseScene()
{
}
}

25
src/BaseScene.hpp Normal file
View File

@ -0,0 +1,25 @@
#ifndef rid_BASESCENE_HPP
#define rid_BASESCENE_HPP
#include "gfx/Window.hpp"
#include "Game.hpp"
namespace rid
{
class BaseScene
{
public:
explicit BaseScene(Game& game);
virtual ~BaseScene();
virtual void update(float) {};
virtual void draw(Window const&) {};
protected:
Game& m_game;
private:
};
}
#endif

66
src/Game.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "Game.hpp"
#include "BaseScene.hpp"
#include "arena/Arena.hpp"
namespace rid
{
/*explicit*/ Game::Game()
{
}
/*virtual*/ Game::~Game()
{
}
int Game::exec()
{
m_window = std::make_unique<Window>(640, 480);
SDL_Event event;
float dt = 0.0f;
set_scene(std::make_unique<Arena>(*this));
while (m_window->is_open())
{
auto now = std::chrono::steady_clock::now();
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
m_window->close();
}
}
if (m_current_scene)
{
m_current_scene->update(dt);
}
m_window->clear();
if (m_current_scene)
{
m_current_scene->draw(*m_window);
}
m_window->update();
if (m_next_scene)
{
m_current_scene = std::move(m_next_scene);
m_next_scene.reset();
}
auto elapsed = std::chrono::steady_clock::now() - now;
dt = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count() / 1000000.0f;
}
return 0;
}
void Game::set_scene(std::unique_ptr<BaseScene> scene)
{
m_next_scene = std::move(scene);
}
}

30
src/Game.hpp Normal file
View File

@ -0,0 +1,30 @@
#ifndef rid_GAME_HPP
#define rid_GAME_HPP
#include "conf.hpp"
#include "gfx/Window.hpp"
namespace rid
{
class BaseScene;
class Game
{
public:
explicit Game();
virtual ~Game();
int exec();
// Scenes management
// -----------------
void set_scene(std::unique_ptr<BaseScene> scene);
private:
std::unique_ptr<Window> m_window;
std::unique_ptr<BaseScene> m_current_scene;
std::unique_ptr<BaseScene> m_next_scene;
};
}
#endif

34
src/arena/Arena.cpp Normal file
View File

@ -0,0 +1,34 @@
#include "Arena.hpp"
#include "conf.hpp"
namespace rid
{
/*explicit*/ Arena::Arena(Game& game)
: BaseScene(game)
, m_canvas { std::make_unique<Canvas>() }
{
auto tex = std::make_unique<Texture>();
tex->load(RID_DATADIR / "assets" / "images" / "walk_0.png");
m_canvas->set_texture(std::move(tex));
m_canvas->draw_tex(glm::vec2 {512.0f, 512.0f},
glm::vec2 {96.0f, 96.0f},
glm::vec4 {1.0f, 1.0f, 1.0f, 1.0f},
glm::vec2 {0.f, 0.0f},
glm::vec2 {32.0f, 32.0f});
}
/*virtual*/ Arena::~Arena()
{
}
void Arena::update(float) /*override*/
{
}
void Arena::draw(Window const& win) /*override*/
{
win.draw(*m_canvas);
}
}

24
src/arena/Arena.hpp Normal file
View File

@ -0,0 +1,24 @@
#ifndef rid_ARENA_HPP
#define rid_ARENA_HPP
#include "conf.hpp"
#include "../BaseScene.hpp"
#include "../gfx/Canvas.hpp"
namespace rid
{
class Arena: public BaseScene
{
public:
explicit Arena(Game& game);
virtual ~Arena();
void update(float dt) override;
void draw(Window const& win) override;
private:
std::unique_ptr<Canvas> m_canvas;
};
}
#endif

View File

@ -1,10 +1,26 @@
#ifndef rid_CONF_HPP
#define rid_CONF_HPP
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <filesystem>
#include <stdexcept>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <fstream>
#include <chrono>
#define RID_VERSION std::string("@version@")
#define RID_DATADIR std::filesystem::path("@datadir@")
#include <filesystem>
#include <string>
#define RID_ERROR(NAME) \
struct NAME : public std::runtime_error { \
explicit NAME (std::string const& what) : std::runtime_error { what } {} \
}
#endif

152
src/gfx/Canvas.cpp Normal file
View File

@ -0,0 +1,152 @@
#include "Canvas.hpp"
namespace rid
{
/*explicit*/ Canvas::Canvas()
: m_shaders { std::make_unique<Shaders>("canvas") }
{
glGenVertexArrays(1, &m_vao);
use();
glGenBuffers(1, &m_vbo);
m_shaders->use();
m_shaders->set_int("has_texture", 0);
update();
}
/*virtual*/ Canvas::~Canvas()
{
glDeleteVertexArrays(1, &m_vao);
glDeleteBuffers(1, &m_vbo);
}
void Canvas::set_texture(std::unique_ptr<Texture> texture)
{
m_shaders->use();
m_shaders->set_int("has_texture", 1);
m_texture = std::move(texture);
}
void Canvas::draw_tex(glm::vec2 pos,
glm::vec2 size,
glm::vec4 color,
glm::vec2 tex_pos,
glm::vec2 tex_size)
{
// A -- B
// | |
// D -- C
float tx = tex_pos.x;
float ty = tex_pos.y;
float tw = tex_size.x;
float th = tex_size.y;
if (m_texture)
{
tx /= static_cast<float>(m_texture->width());
ty /= static_cast<float>(m_texture->height());
tw /= static_cast<float>(m_texture->width());
th /= static_cast<float>(m_texture->height());
}
Vertex a {
pos.x - size.x/2.0f, pos.y - size.y/2.0f, 0.0f,
color.x, color.y, color.z, color.w,
tx, ty
};
Vertex b {
pos.x + size.x/2.0f, pos.y - size.y/2.0f, 0.0f,
color.x, color.y, color.z, color.w,
tx + tw, ty
};
Vertex c {
pos.x + size.x/2.0f, pos.y + size.y/2.0f, 0.0f,
color.x, color.y, color.z, color.w,
tx + tw, ty + th
};
Vertex d {
pos.x - size.x/2.0f, pos.y + size.y/2.0f, 0.0f,
color.x, color.y, color.z, color.w,
tx, ty + th
};
m_vertices.push_back(a);
m_vertices.push_back(b);
m_vertices.push_back(c);
m_vertices.push_back(a);
m_vertices.push_back(c);
m_vertices.push_back(d);
update();
}
void Canvas::draw(glm::mat4 transform) const
{
use();
m_shaders->set_mat4("proj", transform);
glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
}
void Canvas::use() const
{
glBindVertexArray(m_vao);
m_shaders->use();
if (m_texture)
{
m_texture->use();
}
}
void Canvas::update() const
{
use();
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER,
sizeof(Vertex) * m_vertices.size(),
m_vertices.data(),
GL_STATIC_DRAW);
// Position
// --------
GLint pos = m_shaders->attrib("pos");
glEnableVertexAttribArray(pos);
glVertexAttribPointer(pos,
3,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
nullptr);
// Color
// -----
GLint color = m_shaders->attrib("color");
glEnableVertexAttribArray(color);
glVertexAttribPointer(color,
4,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, r)));
// Tex coord
// ---------
GLint tex = m_shaders->attrib("tex_coord");
glEnableVertexAttribArray(tex);
glVertexAttribPointer(tex,
2,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, u)));
}
}

48
src/gfx/Canvas.hpp Normal file
View File

@ -0,0 +1,48 @@
#ifndef rid_CANVAS_HPP
#define rid_CANVAS_HPP
#include "conf.hpp"
#include "Shaders.hpp"
#include "Texture.hpp"
namespace rid
{
struct Vertex
{
float x; float y; float z;
float r; float g; float b; float a;
float u; float v;
};
class Canvas
{
public:
explicit Canvas();
virtual ~Canvas();
void set_texture(std::unique_ptr<Texture> texture);
// Draw stuff
// ----------
void draw_tex(glm::vec2 pos,
glm::vec2 size,
glm::vec4 color,
glm::vec2 tex_pos,
glm::vec2 tex_size);
void draw(glm::mat4 transform) const;
void clear() { m_vertices.clear(); update(); }
private:
std::unique_ptr<Shaders> m_shaders;
std::vector<Vertex> m_vertices;
std::unique_ptr<Texture> m_texture;
GLuint m_vao;
GLuint m_vbo;
void use() const;
void update() const;
};
}
#endif

122
src/gfx/Shaders.cpp Normal file
View File

@ -0,0 +1,122 @@
#include "Shaders.hpp"
#include <filesystem>
namespace rid
{
/*explicit*/ Shaders::Shaders(std::string const& name)
: m_program { glCreateProgram() }
{
use();
load_shader(GL_VERTEX_SHADER, RID_DATADIR / "assets" / "shaders" / (name + "_vertex.glsl"));
load_shader(GL_FRAGMENT_SHADER, RID_DATADIR / "assets" / "shaders" / (name + "_fragment.glsl"));
glLinkProgram(m_program);
GLint status = GL_FALSE;
glGetProgramiv(m_program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
size_t const SZ = 256;
char msg[SZ];
glGetProgramInfoLog(m_program, SZ, nullptr, msg);
throw shaders_error {std::string() + "Cannot link shader program '" + name + "': " + msg};
}
}
/*virtual*/ Shaders::~Shaders()
{
glDeleteProgram(m_program);
}
void Shaders::use() const
{
glUseProgram(m_program);
}
GLint Shaders::attrib(std::string const& name) const
{
GLint value = glGetAttribLocation(m_program, name.c_str());
if (value < 0)
{
throw shaders_error {"Cannot find attribute '" + name + "'."};
}
return value;
}
GLint Shaders::uniform(std::string const& name) const
{
GLint value = glGetUniformLocation(m_program, name.c_str());
if (value < 0)
{
throw shaders_error {"Cannot find uniform '" + name + "'."};
}
return value;
}
void Shaders::set_mat4(std::string const& name, glm::mat4 mat)
{
GLint loc = uniform(name);
glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(mat));
}
void Shaders::set_int(std::string const& name, int value)
{
GLint loc = uniform(name);
glUniform1i(loc, value);
}
GLuint Shaders::load_shader(GLenum shader_type,
std::filesystem::path const& shader_path)
{
GLuint shader = glCreateShader(shader_type);
if (!std::filesystem::is_regular_file(shader_path))
{
throw shaders_error {"Cannot load shader: '"
+ shader_path.string() + "' is not a regular file."};
}
std::ifstream file {shader_path};
if (!file)
{
throw shaders_error {"Cannot load shader: cannot open '"
+ shader_path.string() + "'."};
}
std::stringstream ss;
ss << file.rdbuf();
std::string source = ss.str();
char const* sources[] = { source.c_str() };
glShaderSource(shader, 1, sources, nullptr);
glCompileShader(shader);
GLint status = GL_FALSE;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
size_t const SZ = 256;
char msg[SZ];
glGetShaderInfoLog(shader, SZ, nullptr, msg);
throw shaders_error {"Cannot compile shader '" + shader_path.string() + "': " + msg};
}
glAttachShader(m_program, shader);
return shader;
}
}

31
src/gfx/Shaders.hpp Normal file
View File

@ -0,0 +1,31 @@
#ifndef rid_SHADERS_HPP
#define rid_SHADERS_HPP
#include <GL/glew.h>
#include "conf.hpp"
namespace rid
{
RID_ERROR(shaders_error);
class Shaders
{
public:
explicit Shaders(std::string const& name);
virtual ~Shaders();
void use() const;
GLint attrib(std::string const& name) const;
GLint uniform(std::string const& name) const;
void set_mat4(std::string const& name, glm::mat4 mat);
void set_int(std::string const& name, int value);
private:
GLuint m_program;
GLuint load_shader(GLenum shader_type, std::filesystem::path const& shader_path);
};
}
#endif

53
src/gfx/Texture.cpp Normal file
View File

@ -0,0 +1,53 @@
#include "Texture.hpp"
namespace rid
{
/*explicit*/ Texture::Texture()
{
glGenTextures(1, &m_texture);
}
/*virtual*/ Texture::~Texture()
{
glDeleteTextures(1, &m_texture);
}
void Texture::load(std::filesystem::path const& tex_path)
{
SDL_Surface* surface = IMG_Load(tex_path.c_str());
surface = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA32, 0);
if (!surface)
{
throw texture_error {"Cannot load texture '" + tex_path.string() + "'."};
}
m_width = surface->w;
m_height = surface->h;
use();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
surface->w,
surface->h,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
surface->pixels);
SDL_FreeSurface(surface);
}
void Texture::use() const
{
glBindTexture(GL_TEXTURE_2D, m_texture);
}
}

33
src/gfx/Texture.hpp Normal file
View File

@ -0,0 +1,33 @@
#ifndef rid_TEXTURE_HPP
#define rid_TEXTURE_HPP
#include <GL/glew.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "conf.hpp"
namespace rid
{
RID_ERROR(texture_error);
class Texture
{
public:
explicit Texture();
virtual ~Texture();
int width() const { return m_width; }
int height() const { return m_height; }
void load(std::filesystem::path const& tex_path);
void use() const;
private:
GLuint m_texture;
int m_width = 0;
int m_height = 0;
};
}
#endif

82
src/gfx/Window.cpp Normal file
View File

@ -0,0 +1,82 @@
#include "Window.hpp"
#include "SDL_image.h"
namespace rid
{
/*explicit*/ Window::Window(int width, int height)
: m_width { width }
, m_height { height }
{
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
throw display_error {"Cannot initialize SDL 2."};
}
// Init SDL2 image
{
int flags = IMG_INIT_PNG;
int status = IMG_Init(flags);
if ( (status & flags) != flags )
{
throw display_error {"Cannot initialize sdl2 image."};
}
}
std::string const title = "Rest In Dust v" + RID_VERSION;
m_window = SDL_CreateWindow(title.c_str(),
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
m_width, m_height,
SDL_WINDOW_SHOWN
| SDL_WINDOW_OPENGL
| SDL_WINDOW_MAXIMIZED);
if (!m_window)
{
throw display_error {"Cannot create SDL2 window."};
}
m_context = SDL_GL_CreateContext(m_window);
if (!m_context)
{
throw display_error {"Cannot create SDL2 GL context."};
}
if (glewInit() != GLEW_OK)
{
throw display_error {"Cannot initialize GLEW."};
}
}
/*virtual*/ Window::~Window()
{
SDL_GL_DeleteContext(m_context);
IMG_Quit();
SDL_Quit();
}
void Window::draw(Canvas const& canvas) const
{
int w, h;
SDL_GetWindowSize(m_window, &w, &h);
glm::mat4 proj = glm::ortho(0.0f, static_cast<float>(w),
static_cast<float>(h), 0.0f);
canvas.draw(proj);
}
void Window::clear(glm::vec4 const& color) const
{
glClearColor(color.x, color.y, color.z, color.w);
glClear(GL_COLOR_BUFFER_BIT);
}
void Window::update() const
{
SDL_GL_SwapWindow(m_window);
}
}

48
src/gfx/Window.hpp Normal file
View File

@ -0,0 +1,48 @@
#ifndef rid_WINDOW_HPP
#define rid_WINDOW_HPP
#include <GL/glew.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "conf.hpp"
#include "Canvas.hpp"
namespace rid
{
RID_ERROR(display_error);
class Window
{
public:
explicit Window(int width, int height);
virtual ~Window();
// Drawing
// -------
void draw(Canvas const& canvas) const;
// Update
// ------
void clear(glm::vec4 const& color = {0.0f, 0.0f, 0.0f, 1.0f}) const;
void update() const;
// State
// -----
bool is_open() const { return m_is_open; }
void close() { m_is_open = false; }
// Dimensions
// ----------
int width() const { return m_width; }
int height() const { return m_height; }
private:
SDL_Window* m_window = nullptr;
SDL_GLContext m_context = nullptr;
int m_width;
int m_height;
bool m_is_open = true;
};
}
#endif

View File

@ -1,8 +1,8 @@
#include <iostream>
#include "conf.hpp"
#include "Game.hpp"
int main(int, char**)
{
std::cout << "Rest In Dust V" << RID_VERSION << std::endl;
return 0;
rid::Game game;
return game.exec();
}