Can draw colored rects.

main
bog 2023-11-11 08:57:45 +01:00
parent ae91f57f93
commit 9b4184aa3b
17 changed files with 664 additions and 2 deletions

View File

@ -0,0 +1,8 @@
#version 130
in vec4 i_color;
out vec4 o_color;
void main() {
o_color = i_color;
}

View File

@ -0,0 +1,13 @@
#version 130
in vec2 pos;
in vec4 color;
out vec4 i_color;
uniform mat4 proj;
void main() {
gl_Position = proj * vec4(pos, 0.0, 1.0);
i_color = color;
}

View File

@ -22,8 +22,23 @@ executable(
'bloody-gun', 'bloody-gun',
sources: [ sources: [
'src/main.cpp', 'src/main.cpp',
# core
'src/Game.cpp',
'src/BaseScene.cpp',
'src/scenes/World.cpp',
# gfx
'src/Renderer.cpp',
'src/Shaders.cpp',
'src/Canvas.cpp',
], ],
dependencies: [ dependencies: [
dependency('glew'),
dependency('glm'),
dependency('sdl2'),
], ],
install: true install: true
) )

13
src/BaseScene.cpp Normal file
View File

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

24
src/BaseScene.hpp Normal file
View File

@ -0,0 +1,24 @@
#ifndef bg_BASESCENE_HPP
#define bg_BASESCENE_HPP
#include "commons.hpp"
#include "Renderer.hpp"
#include "Game.hpp"
namespace bg
{
class BaseScene
{
public:
explicit BaseScene(Game& game);
virtual ~BaseScene();
virtual void update(float /*dt*/) {}
virtual void draw(Renderer const& /*renderer*/) {}
private:
Game& m_game;
};
}
#endif

98
src/Canvas.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "Canvas.hpp"
namespace bg
{
/*explicit*/ Canvas::Canvas()
{
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
glGenBuffers(1, &m_vbo);
}
/*virtual*/ Canvas::~Canvas()
{
glDeleteBuffers(1, &m_vbo);
glDeleteVertexArrays(1, &m_vao);
}
void Canvas::use() const
{
m_shaders->use();
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
}
void Canvas::draw(glm::mat4 const& transform) const
{
use();
m_shaders->set_matrix("proj", transform);
glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
}
void Canvas::clear()
{
m_vertices.clear();
update();
}
void Canvas::rect(glm::vec2 const& pos,
glm::vec2 const& size,
glm::vec4 const& color)
{
// A -- B
// | |
// D -- C
Vertex a {pos.x - size.x/2.0f, pos.y - size.y/2.0f,
color.x, color.y, color.z, color.w
};
Vertex b {pos.x + size.x/2.0f, pos.y - size.y/2.0f,
color.x, color.y, color.z, color.w};
Vertex c {pos.x + size.x/2.0f, pos.y + size.y/2.0f,
color.x, color.y, color.z, color.w};
Vertex d {pos.x - size.x/2.0f, pos.y + size.y/2.0f,
color.x, color.y, color.z, color.w};
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::update()
{
use();
glBufferData(GL_ARRAY_BUFFER,
m_vertices.size() * sizeof(Vertex),
m_vertices.data(),
GL_STATIC_DRAW);
GLint pos = m_shaders->attrib("pos");
glEnableVertexAttribArray(pos);
glVertexAttribPointer(pos,
2,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
nullptr);
GLint color = m_shaders->attrib("color");
glEnableVertexAttribArray(color);
glVertexAttribPointer(color,
4,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, r)));
}
}

48
src/Canvas.hpp Normal file
View File

@ -0,0 +1,48 @@
#ifndef bg_CANVAS_HPP
#define bg_CANVAS_HPP
#include <glm/glm.hpp>
#include <GL/glew.h>
#include "Shaders.hpp"
#include "commons.hpp"
namespace bg
{
struct Vertex
{
float x; float y;
float r; float g; float b; float a;
};
class Canvas
{
public:
explicit Canvas();
virtual ~Canvas();
void use() const;
void draw(glm::mat4 const& transform) const;
// Draw shapes
// -----------
void clear();
void rect(glm::vec2 const& pos,
glm::vec2 const& size,
glm::vec4 const& color =
glm::vec4 {0.0f, 0.0f, 0.0f, 1.0f});
private:
std::shared_ptr<Shaders> m_shaders =
std::make_shared<Shaders>();
GLuint m_vao;
GLuint m_vbo;
std::vector<Vertex> m_vertices;
void update();
};
}
#endif

122
src/Game.cpp Normal file
View File

@ -0,0 +1,122 @@
#include "Game.hpp"
#include "BaseScene.hpp"
namespace bg
{
/*explicit*/ Game::Game()
{
}
/*virtual*/ Game::~Game()
{
free();
}
void Game::init()
{
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
throw game_error {"Cannot initialize SDL 2."};
}
auto title = "Bloody Gun v" + BG_VERSION;
m_window = SDL_CreateWindow(title.c_str(),
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
1600, 900,
SDL_WINDOW_SHOWN
| SDL_WINDOW_FULLSCREEN
| SDL_WINDOW_OPENGL);
if (!m_window)
{
throw game_error {"Cannot open SDL window."};
}
{
int w;
int h;
SDL_GetWindowSize(m_window, &w, &h);
m_renderer = std::make_unique<Renderer>(w, h);
}
m_context = SDL_GL_CreateContext(m_window);
if (!m_context)
{
throw game_error {std::string()
+ "Cannot create SDL 2 GL context ("
+ SDL_GetError()
+ ")"};
}
if (glewInit() != GLEW_OK)
{
throw game_error {"Cannot initialize glew."};
}
}
void Game::free()
{
SDL_GL_DeleteContext(m_context);
SDL_DestroyWindow(m_window);
m_window = nullptr;
SDL_Quit();
}
int Game::start()
{
SDL_Event event;
m_running = true;
float dt = 0.0f;
while (m_running)
{
auto loop_start = std::chrono::steady_clock::now();
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
m_running = false;
}
}
if (m_scene_stack.empty() == false)
{
m_scene_stack.back()->update(dt);
}
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
if (m_scene_stack.empty() == false)
{
m_scene_stack.back()->draw(*m_renderer);
}
SDL_GL_SwapWindow(m_window);
for (auto& scene: m_push_queue)
{
m_scene_stack.push_back(std::move(scene));
}
m_push_queue.clear();
auto elapsed = std::chrono::steady_clock::now() - loop_start;
dt = std::chrono::duration_cast<std::chrono::microseconds>(elapsed)
.count() / 1000000.0f;
}
return 0;
}
void Game::queue_push(std::unique_ptr<BaseScene> scene)
{
m_push_queue.push_back(std::move(scene));
}
}

43
src/Game.hpp Normal file
View File

@ -0,0 +1,43 @@
#ifndef bg_GAME_HPP
#define bg_GAME_HPP
#include "commons.hpp"
#include <GL/glew.h>
#include <SDL2/SDL.h>
#include "Renderer.hpp"
namespace bg
{
BG_ERROR(game_error);
class BaseScene;
class Game
{
public:
explicit Game();
virtual ~Game();
// Lifecycle management
// --------------------
void init();
void free();
int start();
// Scenes
// ------
void queue_push(std::unique_ptr<BaseScene> scene);
private:
bool m_running = false;
SDL_Window* m_window = nullptr;
std::unique_ptr<Renderer> m_renderer;
SDL_GLContext m_context = nullptr;
std::vector<std::unique_ptr<BaseScene>> m_scene_stack;
std::vector<std::unique_ptr<BaseScene>> m_push_queue;
};
}
#endif

26
src/Renderer.cpp Normal file
View File

@ -0,0 +1,26 @@
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "Renderer.hpp"
#include "Canvas.hpp"
namespace bg
{
/*explicit*/ Renderer::Renderer(int width, int height)
: m_width { width }
, m_height { height }
{
}
/*virtual*/ Renderer::~Renderer()
{
}
void Renderer::draw(Canvas const& canvas) const
{
glm::mat4 proj = glm::ortho(0.0f,
static_cast<float>(m_width),
static_cast<float>(m_height),
0.0f);
canvas.draw(proj);
}
}

22
src/Renderer.hpp Normal file
View File

@ -0,0 +1,22 @@
#ifndef bg_RENDERER_HPP
#define bg_RENDERER_HPP
namespace bg
{
class Canvas;
class Renderer
{
public:
explicit Renderer(int width, int height);
virtual ~Renderer();
void draw(Canvas const& canvas) const;
private:
int m_width;
int m_height;
};
}
#endif

124
src/Shaders.cpp Normal file
View File

@ -0,0 +1,124 @@
#include <glm/gtc/type_ptr.hpp>
#include "Shaders.hpp"
namespace bg
{
/*explicit*/ Shaders::Shaders()
{
shader(BG_DATADIR / "assets" / "shaders" / "canvas_vertex.glsl",
GL_VERTEX_SHADER);
shader(BG_DATADIR / "assets" / "shaders" / "canvas_fragment.glsl",
GL_FRAGMENT_SHADER);
glLinkProgram(m_program);
GLint status = GL_FALSE;
glGetProgramiv(m_program, GL_LINK_STATUS, &status);
if (!status)
{
size_t const SZ = 512;
char msg[SZ];
glGetProgramInfoLog(m_program, SZ, nullptr, msg);
throw shaders_error {std::string() + "Cannot link program.\n" + msg};
}
}
/*virtual*/ Shaders::~Shaders()
{
glDeleteProgram(m_program);
}
void Shaders::use() const
{
glUseProgram(m_program);
}
GLint Shaders::attrib(std::string const& name) const
{
use();
GLint loc = glGetAttribLocation(m_program, name.c_str());
if (loc < 0)
{
throw shaders_error {"Cannot find vertex attribute '"+name+"'."};
}
return loc;
}
GLint Shaders::matrix(std::string const& name) const
{
use();
GLint loc = glGetUniformLocation(m_program, name.c_str());
if (loc < 0)
{
throw shaders_error {"Cannot find uniform '"+name+"'."};
}
return loc;
}
void Shaders::set_matrix(std::string const& name, glm::mat4 mat) const
{
use();
glUniformMatrix4fv(matrix(name), 1, GL_FALSE, glm::value_ptr(mat));
}
GLuint Shaders::shader(std::filesystem::path const& path, GLenum type)
{
if (!std::filesystem::is_regular_file(path))
{
throw shaders_error {"Shader file '" + path.string() + "' not found."};
}
GLuint myshader = glCreateShader(type);
std::string source;
{
std::stringstream ss;
std::ifstream file { path };
if (!file)
{
throw shaders_error {"Cannot open file '" + path.string() + "'"};
}
ss << file.rdbuf();
source = ss.str();
}
char const* sources[] = {source.c_str()};
glShaderSource(myshader, 1, sources, nullptr);
glCompileShader(myshader);
GLint status = GL_FALSE;
glGetShaderiv(myshader, GL_COMPILE_STATUS, &status);
if (!status)
{
size_t const SZ = 512;
char msg[SZ];
glGetShaderInfoLog(myshader, SZ, nullptr, msg);
throw shaders_error {"Cannot compile shader '"
+ path.string()
+ "'\n"
+ msg
};
}
glAttachShader(m_program, myshader);
return myshader;
}
}

34
src/Shaders.hpp Normal file
View File

@ -0,0 +1,34 @@
#ifndef bg_SHADERS_HPP
#define bg_SHADERS_HPP
#include <glm/glm.hpp>
#include <GL/glew.h>
#include "commons.hpp"
namespace bg
{
BG_ERROR(shaders_error);
class Shaders
{
public:
explicit Shaders();
virtual ~Shaders();
void use() const;
// Attribute and Uniforms
// ----------------------
GLint attrib(std::string const& name) const;
GLint matrix(std::string const& name) const;
void set_matrix(std::string const& name, glm::mat4 mat) const;
private:
GLuint m_program = glCreateProgram();
GLuint shader(std::filesystem::path const& path, GLenum type);
};
}
#endif

View File

@ -1,7 +1,18 @@
#ifndef bg_COMMONS_HPP #ifndef bg_COMMONS_HPP
#define bg_COMMONS_HPP #define bg_COMMONS_HPP
#include <fstream>
#include <iostream>
#include <filesystem> #include <filesystem>
#include <chrono>
#include <vector>
#include "conf.hpp" #include "conf.hpp"
#define BG_ERROR(NAME) \
struct NAME : public std::runtime_error { \
explicit NAME (std::string const& what) \
: std::runtime_error { what } {} \
}
#endif #endif

View File

@ -1,8 +1,15 @@
#include <iostream> #include <iostream>
#include "commons.hpp" #include "commons.hpp"
#include "Game.hpp"
#include "scenes/World.hpp"
int main(int, char**) int main(int, char**)
{ {
std::cout << "Bloody Gun v" << BG_VERSION << std::endl; bg::Game game;
return 0; game.init();
auto world = std::make_unique<bg::World>(game);
game.queue_push(std::move(world));
return game.start();
} }

30
src/scenes/World.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "World.hpp"
#include "../Renderer.hpp"
namespace bg
{
/*explicit*/ World::World(Game& game)
: BaseScene(game)
{
m_canvas.rect(glm::vec2 {64.0f, 64.0f},
glm::vec2 {128.0f, 32.0f},
glm::vec4 {0.0f, 0.5f, 0.0f, 1.0f});
m_canvas.rect(glm::vec2 {512.0f, 512.0f},
glm::vec2 {96.0f, 96.0f},
glm::vec4 {0.5f, 0.0f, 0.0f, 1.0f});
}
/*virtual*/ World::~World()
{
}
void World::update(float) /*override*/
{
}
void World::draw(Renderer const& renderer) /*override*/
{
renderer.draw(m_canvas);
}
}

24
src/scenes/World.hpp Normal file
View File

@ -0,0 +1,24 @@
#ifndef bg_WORLD_HPP
#define bg_WORLD_HPP
#include "../commons.hpp"
#include "../BaseScene.hpp"
#include "../Canvas.hpp"
namespace bg
{
class World: public BaseScene
{
public:
explicit World(Game& game);
virtual ~World();
void update(float dt) override;
void draw(Renderer const& renderer) override;
private:
Canvas m_canvas;
};
}
#endif