can now draw colored rect.

main
bog 2023-10-29 18:37:22 +01:00
parent 5fec2c0045
commit 21ca282619
20 changed files with 765 additions and 4 deletions

View File

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

View File

@ -0,0 +1,13 @@
#version 130
in vec3 pos;
in vec4 color;
out vec4 frag_icolor;
uniform mat4 mvp;
void main() {
gl_Position = mvp * vec4(pos, 1.0f);
frag_icolor = color;
}

View File

@ -7,8 +7,11 @@ project('duck2d',
'cpp_std=c++17',
])
assets_dir = get_option('prefix') / get_option('datadir') / 'duck2d'
conf = configuration_data()
conf.set('version', meson.project_version())
conf.set('assets_dir', assets_dir / 'assets')
configure_file(
@ -20,7 +23,23 @@ configure_file(
executable('d2d',
sources: [
'src/main.cpp',
'src/Game.cpp',
'src/Script.cpp',
# graphics
'src/Shader.cpp',
'src/Shape.cpp',
'src/Material.cpp',
'src/Object.cpp',
'src/Renderer.cpp',
],
dependencies: [
dependency('sdl2'),
dependency('glew'),
dependency('glm'),
dependency('guile-3.0'),
],
install: true)
install_subdir('assets', install_dir: assets_dir)

78
src/Game.cpp Normal file
View File

@ -0,0 +1,78 @@
#include <GL/glew.h>
#include "Game.hpp"
#include <glm/glm.hpp>
namespace d2
{
/*explicit*/ Game::Game(std::string const& title,
unsigned width,
unsigned height)
{
SDL_Init(SDL_INIT_VIDEO);
m_window = SDL_CreateWindow(title.c_str(),
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
width, height,
SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
if (!m_window)
{
throw game_error {"Cannot open a new window."};
}
m_context = SDL_GL_CreateContext(m_window);
if (!m_context)
{
throw game_error {"Cannot initialize opengl context."};
}
if (glewInit() != GLEW_OK)
{
throw game_error {"Cannot initialize GLEW."};
}
m_renderer = std::make_unique<Renderer>(width, height);
}
/*virtual*/ Game::~Game()
{
SDL_GL_DeleteContext(m_context); m_context = nullptr;
SDL_DestroyWindow(m_window); m_window = nullptr;
SDL_Quit();
}
void Game::run()
{
SDL_Event event;
bool m_running = true;
while (m_running)
{
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
m_running = false;
}
}
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
for (auto& obj: m_draw_queue)
{
m_renderer->draw(*obj);
}
//m_draw_queue.clear();
SDL_GL_SwapWindow(m_window);
}
}
void Game::queue_draw(std::unique_ptr<Object> obj)
{
m_draw_queue.push_back(std::move(obj));
}
}

31
src/Game.hpp Normal file
View File

@ -0,0 +1,31 @@
#ifndef d2_GAME_HPP
#define d2_GAME_HPP
#include <SDL2/SDL.h>
#include "commons.hpp"
#include "Renderer.hpp"
namespace d2
{
D2_ERROR(game_error);
class Game
{
public:
explicit Game(std::string const& title,
unsigned width,
unsigned height);
virtual ~Game();
void run();
void queue_draw(std::unique_ptr<Object> obj);
private:
SDL_Window* m_window = nullptr;
SDL_GLContext m_context;
std::unique_ptr<Renderer> m_renderer;
std::vector<std::unique_ptr<Object>> m_draw_queue;
};
}
#endif

20
src/Material.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "Material.hpp"
namespace d2
{
/*explicit*/ Material::Material()
{
}
/*virtual*/ Material::~Material()
{
}
void Material::set_color(unsigned r, unsigned g, unsigned b, unsigned a)
{
m_r = r;
m_g = g;
m_b = b;
m_a = a;
}
}

29
src/Material.hpp Normal file
View File

@ -0,0 +1,29 @@
#ifndef d2_MATERIAL_HPP
#define d2_MATERIAL_HPP
#include "commons.hpp"
namespace d2
{
class Material
{
public:
explicit Material();
virtual ~Material();
void set_color(unsigned r, unsigned g, unsigned b, unsigned a);
unsigned r() const { return m_r; }
unsigned g() const { return m_g; }
unsigned b() const { return m_b; }
unsigned a() const { return m_a; }
private:
unsigned m_r = 0;
unsigned m_g = 0;
unsigned m_b = 0;
unsigned m_a = 0;
};
}
#endif

108
src/Object.cpp Normal file
View File

@ -0,0 +1,108 @@
#include "Object.hpp"
namespace d2
{
/*explicit*/ Object::Object(std::shared_ptr<Shape> shape,
std::shared_ptr<Material> material,
std::shared_ptr<Shader> shader)
: m_shape { shape }
, m_material { material }
, m_shader { shader }
{
glGenVertexArrays(1, &m_vao);
use();
glGenBuffers(BUFFER_COUNT, m_vbos);
update();
}
/*virtual*/ Object::~Object()
{
glDeleteBuffers(BUFFER_COUNT, m_vbos);
glDeleteVertexArrays(1, &m_vao);
}
void Object::set_position(glm::vec3 position)
{
m_position = position;
}
void Object::set_size(glm::vec3 size)
{
m_size = size;
}
void Object::use() const
{
m_shader->use();
glBindVertexArray(m_vao);
}
void Object::update() const
{
update_pos();
update_color();
}
size_t Object::point_count() const
{
return m_shape->points().size();
}
void Object::update_pos() const
{
use();
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[POS_BUFFER]);
auto const& vertices = m_shape->points();
glBufferData(GL_ARRAY_BUFFER,
vertices.size() * sizeof(glm::vec3),
vertices.data(),
GL_STATIC_DRAW);
GLint location = m_shader->attr("pos");
assert(location >= 0);
glEnableVertexAttribArray(location);
glVertexAttribPointer(location,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
nullptr);
}
void Object::update_color() const
{
use();
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[COLOR_BUFFER]);
std::vector<glm::vec4> colors;
for (size_t i=0; i<m_shape->points().size(); i++)
{
colors.push_back(glm::vec4
{static_cast<float>(m_material->r())/255.0f,
static_cast<float>(m_material->g()/255.0f),
static_cast<float>(m_material->b()/255.0f),
static_cast<float>(m_material->a())/255.0f});
}
glBufferData(GL_ARRAY_BUFFER,
colors.size() * sizeof(glm::vec4),
colors.data(),
GL_STATIC_DRAW);
GLint location = m_shader->attr("color");
assert(location >= 0);
glEnableVertexAttribArray(location);
glVertexAttribPointer(location,
4,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec4),
nullptr);
}
}

59
src/Object.hpp Normal file
View File

@ -0,0 +1,59 @@
#ifndef d2_OBJECT_HPP
#define d2_OBJECT_HPP
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <GL/glew.h>
#include "commons.hpp"
#include "Shader.hpp"
#include "Material.hpp"
#include "Shape.hpp"
namespace d2
{
enum BufferType {
POS_BUFFER,
COLOR_BUFFER,
BUFFER_COUNT
};
class Object
{
public:
explicit Object(std::shared_ptr<Shape> shape,
std::shared_ptr<Material> material,
std::shared_ptr<Shader> shader);
virtual ~Object();
glm::mat4 transform() const { return m_transform; }
std::shared_ptr<Shader> shader() const { return m_shader; }
glm::vec3 position() const { return m_position; }
glm::vec3 size() const { return m_size; }
void set_position(glm::vec3 position);
void set_size(glm::vec3 size);
void use() const;
void update() const;
size_t point_count() const;
private:
GLuint m_vao;
GLuint m_vbos[BUFFER_COUNT];
glm::vec3 m_position;
glm::vec3 m_size;
glm::mat4 m_transform;
std::shared_ptr<Shape> m_shape;
std::shared_ptr<Material> m_material;
std::shared_ptr<Shader> m_shader;
void update_pos() const;
void update_color() const;
};
}
#endif

34
src/Renderer.cpp Normal file
View File

@ -0,0 +1,34 @@
#include "Renderer.hpp"
namespace d2
{
/*explicit*/ Renderer::Renderer(unsigned width, unsigned height)
: m_width { width }
, m_height { height }
{
m_projection = glm::ortho(0.0f,
static_cast<float>(m_width),
static_cast<float>(height),
0.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
/*virtual*/ Renderer::~Renderer()
{
}
void Renderer::draw(Object const& object) const
{
glm::mat4 mvp {1.0f};
mvp = glm::translate(mvp, object.position());
mvp = glm::scale(mvp, object.size());
mvp = m_projection * mvp;
object.use();
object.shader()->set_matrix("mvp", mvp);
glDrawArrays(GL_TRIANGLES, 0, object.point_count());
}
}

26
src/Renderer.hpp Normal file
View File

@ -0,0 +1,26 @@
#ifndef d2_RENDERER_HPP
#define d2_RENDERER_HPP
#include <GL/glew.h>
#include <glm/gtc/matrix_transform.hpp>
#include "commons.hpp"
#include "Object.hpp"
namespace d2
{
class Renderer
{
public:
explicit Renderer(unsigned width, unsigned height);
virtual ~Renderer();
void draw(Object const& object) const;
private:
unsigned m_width;
unsigned m_height;
glm::mat4 m_projection;
};
}
#endif

85
src/Script.cpp Normal file
View File

@ -0,0 +1,85 @@
#include "Script.hpp"
namespace d2
{
/*static*/ std::unique_ptr<Game> Script::game = nullptr;
/*static*/ glm::vec4 Script::color = {0.0f, 0.0f, 0.0f, 0.0f};
/*static*/ void Script::init()
{
scm_init_guile();
scm_c_define_gsubr("winconf", 3, 0, 0, (void*) &fn_winconf);
// Drawing
// -------
scm_c_define_gsubr("rect", 4, 0, 0, (void*) &fn_rect);
scm_c_define_gsubr("color!", 4, 0, 0, (void*) &fn_set_color);
}
/*static*/ void Script::run(std::filesystem::path conf_file)
{
scm_c_primitive_load(conf_file.c_str());
if (Script::game == nullptr)
{
Script::game = std::make_unique<Game>("Duck2D", 640, 480);
}
Script::game->run();
}
/*static*/ SCM Script::fn_winconf(SCM scm_title,
SCM scm_width,
SCM scm_height)
{
std::string title = scm_to_locale_string(scm_title);
unsigned width = scm_to_uint32(scm_width);
unsigned height = scm_to_uint32(scm_height);
Script::game = std::make_unique<Game>(title, width, height);
return SCM_BOOL_T;
}
/*static*/ SCM Script::fn_rect(SCM scm_x, SCM scm_y, SCM scm_w, SCM scm_h)
{
float x = scm_to_double(scm_x);
float y = scm_to_double(scm_y);
float w = scm_to_double(scm_w);
float h = scm_to_double(scm_h);
auto shape = std::make_shared<Shape>();
shape->set_rect();
auto material = std::make_shared<Material>();
material->set_color(Script::color.x,
Script::color.y,
Script::color.z,
Script::color.w);
auto shader = std::make_shared<Shader>();
auto obj = std::make_unique<Object>(shape, material, shader);
obj->set_position(glm::vec3 {x, y, 0.0f});
obj->set_size(glm::vec3 {w, h, 0.0f});
Script::game->queue_draw(std::move(obj));
return SCM_BOOL_T;
}
/*static*/
SCM Script::fn_set_color(SCM scm_r, SCM scm_g, SCM scm_b, SCM scm_a)
{
float r = scm_to_double(scm_r);
float g = scm_to_double(scm_g);
float b = scm_to_double(scm_b);
float a = scm_to_double(scm_a);
Script::color = glm::vec4 {r, g, b, a};
return SCM_BOOL_T;
}
}

25
src/Script.hpp Normal file
View File

@ -0,0 +1,25 @@
#ifndef d2_SCRIPT_HPP
#define d2_SCRIPT_HPP
#include <guile/3.0/libguile.h>
#include "commons.hpp"
#include "Game.hpp"
namespace d2
{
struct Script
{
static void init();
static void run(std::filesystem::path conf_file);
static SCM fn_winconf(SCM scm_title, SCM scm_width, SCM scm_height);
static SCM fn_rect(SCM scm_x, SCM scm_y, SCM scm_w, SCM scm_h);
static SCM fn_set_color(SCM scm_r, SCM scm_g, SCM scm_b, SCM scm_a);
private:
static std::unique_ptr<Game> game;
static glm::vec4 color;
};
}
#endif

114
src/Shader.cpp Normal file
View File

@ -0,0 +1,114 @@
#include "Shader.hpp"
namespace d2
{
/*explicit*/ Shader::Shader()
{
m_program = glCreateProgram();
load_shader(GL_VERTEX_SHADER,
DUCK_ASSETS / "shaders" / "vertex.glsl");
load_shader(GL_FRAGMENT_SHADER,
DUCK_ASSETS / "shaders" / "fragment.glsl");
glLinkProgram(m_program);
GLint status = GL_FALSE;
glGetProgramiv(m_program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
size_t const SZ = 1024;
char msg[SZ];
glGetProgramInfoLog(m_program, SZ, nullptr, msg);
throw shader_error {"Cannot link program."};
}
}
/*virtual*/ Shader::~Shader()
{
glDeleteProgram(m_program);
}
void Shader::use() const
{
glUseProgram(m_program);
}
GLint Shader::attr(std::string const& name) const
{
use();
return glGetAttribLocation(m_program, name.c_str());
}
GLint Shader::uniform(std::string const& name) const
{
use();
return glGetUniformLocation(m_program, name.c_str());
}
void Shader::set_matrix(std::string const& name, glm::mat4 matrix) const
{
use();
glUniformMatrix4fv(uniform(name), 1, false, glm::value_ptr(matrix));
}
GLuint Shader::load_shader(GLenum type,
std::filesystem::path source_path)
{
// Load source
// -----------
if (!std::filesystem::is_regular_file(source_path))
{
throw shader_error {"Cannot find shader '"
+ source_path.string() + "'."};
}
std::stringstream ss;
std::ifstream file { source_path };
if (!file)
{
throw shader_error {"Cannot open shader '"
+ source_path.string()
+ "'"};
}
ss << file.rdbuf();
// Compile source
// --------------
std::string source = ss.str();
char const* c_source[] = {source.c_str()};
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, c_source, nullptr);
glCompileShader(shader);
// check compilation status
// ------------------------
GLint status = GL_FALSE;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
size_t const SZ = 1024;
char msg[SZ];
glGetShaderInfoLog(shader, SZ, nullptr, msg);
std::stringstream ss;
ss << "Cannot compile shader '"
<< source_path.string() << "'" << std::endl;
ss << msg << std::endl;
throw shader_error {ss.str()};
}
glAttachShader(m_program, shader);
return shader;
}
}

31
src/Shader.hpp Normal file
View File

@ -0,0 +1,31 @@
#ifndef d2_SHADER_HPP
#define d2_SHADER_HPP
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <GL/glew.h>
#include "commons.hpp"
namespace d2
{
D2_ERROR(shader_error);
class Shader
{
public:
explicit Shader();
virtual ~Shader();
void use() const;
GLint attr(std::string const& name) const;
GLint uniform(std::string const& name) const;
void set_matrix(std::string const& name, glm::mat4 matrix) const;
private:
GLuint m_program;
GLuint load_shader(GLenum type, std::filesystem::path source_path);
};
}
#endif

24
src/Shape.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "Shape.hpp"
namespace d2
{
/*explicit*/ Shape::Shape()
{
}
/*virtual*/ Shape::~Shape()
{
}
void Shape::set_rect()
{
auto const a = glm::vec3 {-0.5f, -0.5f, +0.0f};
auto const b = glm::vec3 {+0.5f, -0.5f, +0.0f};
auto const c = glm::vec3 {+0.5f, +0.5f, +0.0f};
auto const d = glm::vec3 {-0.5f, +0.5f, +0.0f};
// a -- b
// | |
// d -- c
m_points = {a, b, c, a, c, d};
}
}

24
src/Shape.hpp Normal file
View File

@ -0,0 +1,24 @@
#ifndef d2_SHAPE_HPP
#define d2_SHAPE_HPP
#include <glm/glm.hpp>
#include "commons.hpp"
namespace d2
{
class Shape
{
public:
explicit Shape();
virtual ~Shape();
std::vector<glm::vec3> const& points() const { return m_points; }
void set_rect();
private:
std::vector<glm::vec3> m_points;
};
}
#endif

21
src/commons.hpp Normal file
View File

@ -0,0 +1,21 @@
#ifndef d2_COMMONS_HPP
#define d2_COMMONS_HPP
#include <cassert>
#include <iostream>
#include <filesystem>
#include <sstream>
#include <fstream>
#include <vector>
#include "conf.hpp"
#define D2_ERROR(NAME) \
struct NAME : public std::runtime_error { \
explicit NAME(std::string const& what): \
std::runtime_error { what } \
{ \
} \
}
#endif

View File

@ -2,5 +2,5 @@
#define d2_CONF_HPP
#define DUCK_VERSION std::string("@version@")
#define DUCK_ASSETS std::filesystem::path("@assets_dir@")
#endif

View File

@ -1,8 +1,20 @@
#include <iostream>
#include "conf.hpp"
#include "commons.hpp"
#include "Game.hpp"
#include "Script.hpp"
int main(int, char**)
int main(int argc, char** argv)
{
std::cout << "Duck2D " << DUCK_VERSION << std::endl;
d2::Script::init();
if (argc > 1)
{
d2::Script::run(argv[1]);
}
else if (std::filesystem::exists(std::filesystem::path("main.scm")))
{
d2::Script::run("main.scm");
}
return 0;
}