diff --git a/assets/images/demo.png b/assets/images/demo.png
new file mode 100644
index 0000000..c84cfbc
Binary files /dev/null and b/assets/images/demo.png differ
diff --git a/assets/manifest.xml b/assets/manifest.xml
new file mode 100644
index 0000000..d7360d3
--- /dev/null
+++ b/assets/manifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/shaders/default_fragment.glsl b/assets/shaders/default_fragment.glsl
new file mode 100644
index 0000000..a5d871b
--- /dev/null
+++ b/assets/shaders/default_fragment.glsl
@@ -0,0 +1,21 @@
+#version 130
+
+in vec4 f_color;
+in vec2 f_tex;
+
+out vec4 out_color;
+uniform sampler2D tex_sampler;
+uniform int has_texture;
+
+void main() {
+
+ if (has_texture != 0)
+ {
+ vec4 texcolor = f_color * texture(tex_sampler, f_tex);
+ out_color = texcolor;
+ }
+ else
+ {
+ out_color = f_color;
+ }
+}
diff --git a/assets/shaders/default_vertex.glsl b/assets/shaders/default_vertex.glsl
new file mode 100644
index 0000000..59226f6
--- /dev/null
+++ b/assets/shaders/default_vertex.glsl
@@ -0,0 +1,15 @@
+#version 130
+
+in vec3 pos;
+in vec4 color;
+in vec2 tex;
+out vec2 f_tex;
+out vec4 f_color;
+
+uniform mat4 mvp;
+
+void main() {
+ gl_Position = mvp * vec4(pos, 1.0);
+ f_color = color;
+ f_tex = tex;
+}
diff --git a/meson.build b/meson.build
index d26cd4f..8cf4790 100644
--- a/meson.build
+++ b/meson.build
@@ -7,8 +7,11 @@ project('mornelune',
'cpp_stdd=c++17'
])
+share_dir = get_option('prefix') / get_option('datadir') / 'mornelune'
+
conf = configuration_data()
conf.set('version', meson.project_version())
+conf.set('share_dir', share_dir)
configure_file(
input: 'src/conf.in.hpp',
@@ -24,6 +27,7 @@ executable(
'src/Game.cpp',
'src/Services.cpp',
'src/Service.cpp',
+ 'src/Sprite.cpp',
# Logs
'src/logs/Logs.cpp',
@@ -32,8 +36,30 @@ executable(
# Events
'src/events/Events.cpp',
'src/events/Event.cpp',
+
+ # Graphics
+ 'src/gfx/Graphics.cpp',
+ 'src/gfx/Shader.cpp',
+ 'src/gfx/Mesh.cpp',
+ 'src/gfx/Material.cpp',
+ 'src/gfx/Texture.cpp',
+ 'src/gfx/Shape.cpp',
+
+ # Resources
+ 'src/res/Resources.cpp',
+ 'src/res/BaseRes.cpp',
+ 'src/res/TextRes.cpp',
+ 'src/res/ImageRes.cpp',
+
],
dependencies: [
+ dependency('sdl2'),
+ dependency('SDL2_image'),
+ dependency('glew'),
+ dependency('glm'),
+ dependency('tinyxml2')
],
install: true
)
+
+install_subdir('assets', install_dir: share_dir)
diff --git a/src/Game.cpp b/src/Game.cpp
index 9d35b09..6247135 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -1,7 +1,9 @@
#include "Game.hpp"
#include "logs/FileLogs.hpp"
-#include "src/events/Events.hpp"
-#include "src/logs/Logs.hpp"
+#include "events/Events.hpp"
+#include "logs/Logs.hpp"
+#include "gfx/Graphics.hpp"
+#include "Sprite.hpp"
namespace ml
{
@@ -16,6 +18,28 @@ namespace ml
void Game::start()
{
ML_SYS(FileLogs)->log(LEVEL_INFO, "game started");
+
+ m_running = true;
+
+ SDL_Event event;
+
+ Sprite sprite {256.0f, 256.0f, 128.0f, 128.0f};
+ sprite.set_texture("demo");
+
+ while (m_running)
+ {
+ while (SDL_PollEvent(&event))
+ {
+ if (event.type == SDL_QUIT)
+ {
+ m_running = false;
+ }
+ }
+
+ ML_SYS(Graphics)->clear(4, 139, 154);
+ ML_SYS(Graphics)->draw(sprite);
+ ML_SYS(Graphics)->update();
+ }
}
void Game::stop()
diff --git a/src/Game.hpp b/src/Game.hpp
index 644d10a..4155215 100644
--- a/src/Game.hpp
+++ b/src/Game.hpp
@@ -19,6 +19,7 @@ namespace ml
void stop();
private:
+ bool m_running = false;
};
}
diff --git a/src/Services.cpp b/src/Services.cpp
index 242bc2b..562ecea 100644
--- a/src/Services.cpp
+++ b/src/Services.cpp
@@ -21,4 +21,20 @@ namespace ml
/*virtual*/ Services::~Services()
{
}
+
+ void Services::init()
+ {
+ for (auto& entry: m_services)
+ {
+ entry.second->init();
+ }
+ }
+
+ void Services::free()
+ {
+ for (auto& entry: m_services)
+ {
+ entry.second->free();
+ }
+ }
}
diff --git a/src/Services.hpp b/src/Services.hpp
index 5a64692..c62b193 100644
--- a/src/Services.hpp
+++ b/src/Services.hpp
@@ -16,6 +16,9 @@ namespace ml
virtual ~Services();
+ void init();
+ void free();
+
template
size_t id();
diff --git a/src/Sprite.cpp b/src/Sprite.cpp
new file mode 100644
index 0000000..6717cd9
--- /dev/null
+++ b/src/Sprite.cpp
@@ -0,0 +1,45 @@
+#include "Sprite.hpp"
+
+namespace ml
+{
+ /*explicit*/ Sprite::Sprite(float x, float y, float w, float h)
+ : Shape(std::make_unique(glm::vec3{x, y, 0.0f},
+ glm::vec3{w, h, 0.0f}),
+ std::make_unique(),
+ std::make_unique())
+ , m_pos { glm::vec3 {x, y, 0.0f} }
+ , m_size { glm::vec3 {w, h, 0.0f} }
+
+ {
+ }
+
+ /*virtual*/ Sprite::~Sprite()
+ {
+ }
+
+ void Sprite::set_texture(std::string const& name)
+ {
+ m_material = std::make_unique(name);
+ float const tw = m_material->texture().width();
+ float const th = m_material->texture().height();
+
+ set_sub_texture(name, 0, 0, tw, th);
+ }
+
+ void Sprite::set_sub_texture(std::string const& name,
+ float x, float y, float w, float h)
+ {
+ m_material = std::make_unique(name);
+
+ float const tw = m_material->texture().width();
+ float const th = m_material->texture().height();
+
+ m_mesh = std::make_unique(m_pos, m_size,
+ glm::vec2 {x / tw,
+ y / th},
+ glm::vec2 {w / tw,
+ h / th});
+
+ update();
+ }
+}
diff --git a/src/Sprite.hpp b/src/Sprite.hpp
new file mode 100644
index 0000000..30af581
--- /dev/null
+++ b/src/Sprite.hpp
@@ -0,0 +1,34 @@
+#ifndef ml_SPRITE_HPP
+#define ml_SPRITE_HPP
+
+#include
+#include "commons.hpp"
+#include "gfx/Mesh.hpp"
+#include "gfx/Shader.hpp"
+#include "gfx/Material.hpp"
+#include "gfx/Shape.hpp"
+
+namespace ml
+{
+ /**
+ * Textured AABB.
+ * @see Shape
+ **/
+ class Sprite: public Shape
+ {
+ public:
+ explicit Sprite(float x, float y, float w, float h);
+ virtual ~Sprite();
+
+ void set_texture(std::string const& name);
+
+ void set_sub_texture(std::string const& name,
+ float x, float y, float w, float h);
+
+ private:
+ glm::vec3 m_pos;
+ glm::vec3 m_size;
+ };
+}
+
+#endif
diff --git a/src/commons.hpp b/src/commons.hpp
index 30c3433..4404084 100644
--- a/src/commons.hpp
+++ b/src/commons.hpp
@@ -1,6 +1,8 @@
#ifndef ml_COMMONS_HPP
#define ml_COMMONS_HPP
+#include "conf.hpp"
+
#include
#include
#include
@@ -12,6 +14,9 @@
#include
#include
+#define ML_ASSET(NAME) \
+ (std::filesystem::path(ML_SHARE_DIR) / "assets" / NAME)
+
#define ML ml::Services::get()
#define ML_SYS(SYSTEM) ML->get()
diff --git a/src/conf.in.hpp b/src/conf.in.hpp
index 66ee0d9..deea662 100644
--- a/src/conf.in.hpp
+++ b/src/conf.in.hpp
@@ -2,5 +2,6 @@
#define ml_CONF_HPP
#define ML_VERSION std::string("@version@")
+#define ML_SHARE_DIR std::string("@share_dir@")
#endif
diff --git a/src/gfx/Graphics.cpp b/src/gfx/Graphics.cpp
new file mode 100644
index 0000000..3c31463
--- /dev/null
+++ b/src/gfx/Graphics.cpp
@@ -0,0 +1,89 @@
+#include
+#include
+#include "Graphics.hpp"
+
+namespace ml
+{
+ /*explicit*/ Graphics::Graphics(std::string const& title,
+ unsigned width,
+ unsigned height)
+ : m_title { title }
+ , m_width { width }
+ , m_height { height }
+ {
+ m_projection = glm::ortho(0.0f,
+ static_cast(m_width),
+ static_cast(m_height),
+ -1.0f,
+ 0.0f,
+ 1.0f);
+ }
+
+ /*virtual*/ Graphics::~Graphics()
+ {
+ }
+
+ void Graphics::init() /*override*/
+ {
+ SDL_Init(SDL_INIT_VIDEO);
+ int init_flags = IMG_INIT_PNG;
+
+ if ((IMG_Init(init_flags) & init_flags) != init_flags)
+ {
+ throw gfx_error {"cannot initialize SDL2 image"};
+ }
+
+ m_window = SDL_CreateWindow(m_title.c_str(),
+ SDL_WINDOWPOS_CENTERED,
+ SDL_WINDOWPOS_CENTERED,
+ m_width, m_height,
+ SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
+
+ if (!m_window)
+ {
+ throw gfx_error {"cannot create window"};
+ }
+
+ m_context = SDL_GL_CreateContext(m_window);
+
+ if (!m_context)
+ {
+ throw gfx_error {"cannot create GL context"};
+ }
+
+ int status = glewInit();
+
+ if (status != GLEW_OK)
+ {
+ throw gfx_error {"cannot create GLEW context"};
+ }
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ void Graphics::free() /*override*/
+ {
+ SDL_GL_DeleteContext(m_context);
+ SDL_DestroyWindow(m_window);
+ IMG_Quit();
+ SDL_Quit();
+ }
+
+ void Graphics::clear(unsigned r, unsigned g, unsigned b)
+ {
+ glClearColor(r/255.0f, g/255.0f, b/255.0f, 255.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ void Graphics::draw(Shape const& shape)
+ {
+ shape.shader().set_matrix("mvp", m_projection);
+ shape.draw();
+ }
+
+ void Graphics::update()
+ {
+ SDL_GL_SwapWindow(m_window);
+ }
+}
diff --git a/src/gfx/Graphics.hpp b/src/gfx/Graphics.hpp
new file mode 100644
index 0000000..da5e5c0
--- /dev/null
+++ b/src/gfx/Graphics.hpp
@@ -0,0 +1,43 @@
+#ifndef ml_GRAPHICS_HPP
+#define ml_GRAPHICS_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include "../commons.hpp"
+#include "../Service.hpp"
+#include "Shape.hpp"
+
+namespace ml
+{
+ ML_ERROR(gfx_error);
+
+ class Graphics: public Service
+ {
+ public:
+ explicit Graphics(std::string const& title,
+ unsigned width,
+ unsigned height);
+ virtual ~Graphics();
+
+ void init() override;
+ void free() override;
+
+ void clear(unsigned r, unsigned g, unsigned b);
+ void draw(Shape const& shape);
+ void update();
+
+ private:
+ std::string m_title;
+ unsigned m_width;
+ unsigned m_height;
+ glm::mat4 m_projection;
+
+ SDL_Window* m_window = nullptr;
+ SDL_GLContext m_context;
+ };
+}
+
+#endif
diff --git a/src/gfx/Material.cpp b/src/gfx/Material.cpp
new file mode 100644
index 0000000..f77262f
--- /dev/null
+++ b/src/gfx/Material.cpp
@@ -0,0 +1,27 @@
+#include "Material.hpp"
+
+namespace ml
+{
+ /*explicit*/ Material::Material(glm::vec4 const& color)
+ : m_color { color }
+ {
+ }
+
+ /*explicit*/ Material::Material(std::string const& texture_name)
+ : m_texture { std::make_unique() }
+ {
+ m_texture->load(texture_name);
+ }
+
+ /*virtual*/ Material::~Material()
+ {
+ }
+
+ void Material::use() const
+ {
+ if (m_texture)
+ {
+ m_texture->use();
+ }
+ }
+}
diff --git a/src/gfx/Material.hpp b/src/gfx/Material.hpp
new file mode 100644
index 0000000..778cc6f
--- /dev/null
+++ b/src/gfx/Material.hpp
@@ -0,0 +1,30 @@
+#ifndef ml_MATERIAL_HPP
+#define ml_MATERIAL_HPP
+
+#include
+#include "../commons.hpp"
+#include "Texture.hpp"
+
+namespace ml
+{
+ class Material
+ {
+ public:
+ explicit Material(glm::vec4 const& color=glm::vec4{1.0f});
+ explicit Material(std::string const& texture_name);
+
+ virtual ~Material();
+
+ glm::vec4 color() const { return m_color; }
+ bool has_texture() const { return m_texture != nullptr; }
+ Texture const& texture() const { return *m_texture; }
+
+ void use() const;
+
+ private:
+ glm::vec4 m_color = {1.0f, 1.0f, 1.0f, 1.0f};
+ std::unique_ptr m_texture;
+ };
+}
+
+#endif
diff --git a/src/gfx/Mesh.cpp b/src/gfx/Mesh.cpp
new file mode 100644
index 0000000..7d9f55b
--- /dev/null
+++ b/src/gfx/Mesh.cpp
@@ -0,0 +1,78 @@
+#include "Mesh.hpp"
+
+namespace ml
+{
+ /*explicit*/ Mesh::Mesh()
+ {
+ }
+
+ /*explicit*/ Mesh::Mesh(glm::vec3 pos,
+ glm::vec3 size,
+ glm::vec2 tpos,
+ glm::vec2 tsize)
+ {
+ quad(pos, size, tpos, tsize);
+ }
+
+ /*virtual*/ Mesh::~Mesh()
+ {
+ }
+
+ glm::vec3 Mesh::get(size_t index) const
+ {
+ if (index >= count())
+ {
+ throw mesh_error {"cannot access to vertex '"
+ + std::to_string(index)
+ + "'"};
+ }
+
+ return m_vertices[index];
+ }
+
+ Mesh& Mesh::vertex(glm::vec3 v)
+ {
+ m_vertices.push_back(v);
+ return *this;
+ }
+
+ Mesh& Mesh::texture(glm::vec2 v)
+ {
+ m_textures.push_back(v);
+ return *this;
+ }
+
+ Mesh& Mesh::quad(glm::vec3 pos,
+ glm::vec3 size,
+ glm::vec2 tpos,
+ glm::vec2 tsize)
+ {
+ // a -- b
+ // | |
+ // d -- c
+
+ float hw = size.x / 2.0f;
+ float hh = size.y / 2.0f;
+
+ glm::vec3 a {pos.x - hw, pos.y - hh, pos.z};
+ glm::vec3 b {pos.x + hw, pos.y - hh, pos.z};
+ glm::vec3 c {pos.x + hw, pos.y + hh, pos.z};
+ glm::vec3 d {pos.x - hw, pos.y + hh, pos.z};
+
+ glm::vec2 ta {tpos.x, tpos.y};
+ glm::vec2 tb {tpos.x + tsize.x, tpos.y};
+ glm::vec2 tc {tpos.x + tsize.x, tpos.y + tsize.y};
+ glm::vec2 td {tpos.x, tpos.y + tsize.y};
+
+ vertex(a); texture(ta);
+ vertex(b); texture(tb);
+ vertex(c); texture(tc);
+
+ vertex(a); texture(ta);
+ vertex(c); texture(tc);
+ vertex(d); texture(td);
+
+ return *this;
+ }
+
+}
diff --git a/src/gfx/Mesh.hpp b/src/gfx/Mesh.hpp
new file mode 100644
index 0000000..85e78de
--- /dev/null
+++ b/src/gfx/Mesh.hpp
@@ -0,0 +1,44 @@
+#ifndef ml_MESH_HPP
+#define ml_MESH_HPP
+
+#include
+
+#include "../commons.hpp"
+
+namespace ml
+{
+ ML_ERROR(mesh_error);
+
+ class Mesh
+ {
+ public:
+ explicit Mesh();
+ explicit Mesh(glm::vec3 pos,
+ glm::vec3 size,
+ glm::vec2 tpos = glm::vec2 {0.0f, 0.0f},
+ glm::vec2 tsize = glm::vec2 {1.0f, 1.0f});
+
+ virtual ~Mesh();
+
+ Mesh& vertex(glm::vec3 v);
+ Mesh& texture(glm::vec2 v);
+
+ Mesh& quad(glm::vec3 pos,
+ glm::vec3 size,
+ glm::vec2 tpos = glm::vec2 {0.0f, 0.0f},
+ glm::vec2 tsize = glm::vec2 {1.0f, 1.0f});
+
+ std::vector data() const { return m_vertices; }
+ std::vector tex_data() const { return m_textures; }
+
+ size_t count() const { return m_vertices.size(); }
+
+ glm::vec3 get(size_t index) const;
+
+ private:
+ std::vector m_vertices;
+ std::vector m_textures;
+ };
+}
+
+#endif
diff --git a/src/gfx/Shader.cpp b/src/gfx/Shader.cpp
new file mode 100644
index 0000000..9f69820
--- /dev/null
+++ b/src/gfx/Shader.cpp
@@ -0,0 +1,116 @@
+#include
+#include "Shader.hpp"
+#include "../Services.hpp"
+#include "../res/Resources.hpp"
+#include "../res/TextRes.hpp"
+
+namespace ml
+{
+ /*explicit*/ Shader::Shader()
+ : m_program { glCreateProgram() }
+ {
+ load_shader(GL_VERTEX_SHADER, "vertex-shader");
+ load_shader(GL_FRAGMENT_SHADER, "fragment-shader");
+
+ for (auto shader: m_shaders)
+ {
+ glAttachShader(m_program, shader);
+ }
+
+ 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, NULL, msg);
+ throw shader_error {std::string() + "link error: " + msg};
+ }
+ }
+
+ /*virtual*/ Shader::~Shader()
+ {
+ for (auto shader: m_shaders)
+ {
+ glDeleteShader(shader);
+ }
+
+ glDeleteProgram(m_program);
+ }
+
+ int Shader::attrib(std::string const& name) const
+ {
+ int res = glGetAttribLocation(m_program, name.c_str());
+
+ if (res < 0)
+ {
+ throw shader_error {"cannot find attribute '" + name + "'"};
+ }
+
+ return res;
+ }
+
+ int Shader::uniform(std::string const& name) const
+ {
+ use();
+ int res = glGetUniformLocation(m_program, name.c_str());
+
+ if (res < 0)
+ {
+ throw shader_error {"cannot find uniform '" + name + "'"};
+ }
+
+ return res;
+ }
+
+ void Shader::set_matrix(std::string const& name, glm::mat4 mat) const
+ {
+ use();
+ glUniformMatrix4fv(uniform(name), 1, GL_FALSE, glm::value_ptr(mat));
+ }
+
+ void Shader::set_int(std::string const& name, int value) const
+ {
+ use();
+ glUniform1i(uniform(name), value);
+ }
+
+ void Shader::use() const
+ {
+ glUseProgram(m_program);
+ }
+
+ GLuint Shader::load_shader(GLenum shader_type, std::string const& name)
+ {
+ GLuint shader = glCreateShader(shader_type);
+
+ auto res = ML_SYS(Resources)->get(name);
+ std::string source = res->data();
+ char const* src [] = { source.c_str() };
+
+ glShaderSource(shader, 1, src, nullptr);
+
+ glCompileShader(shader);
+
+ 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, NULL, msg);
+ throw shader_error {std::string() + "shader '"
+ + name
+ + "' error: " + msg};
+ }
+
+ m_shaders.push_back(shader);
+ return shader;
+ }
+}
diff --git a/src/gfx/Shader.hpp b/src/gfx/Shader.hpp
new file mode 100644
index 0000000..6a26a02
--- /dev/null
+++ b/src/gfx/Shader.hpp
@@ -0,0 +1,34 @@
+#ifndef ml_SHADER_HPP
+#define ml_SHADER_HPP
+
+#include
+#include
+#include "../commons.hpp"
+
+namespace ml
+{
+ ML_ERROR(shader_error);
+
+ class Shader
+ {
+ public:
+ explicit Shader();
+ virtual ~Shader();
+
+ int attrib(std::string const& name) const;
+ int uniform(std::string const& name) const;
+
+ void set_matrix(std::string const& name, glm::mat4 mat) const;
+ void set_int(std::string const& name, int value) const;
+
+ void use() const;
+
+ private:
+ GLuint m_program;
+ std::vector m_shaders;
+
+ GLuint load_shader(GLenum shader_type, std::string const& name);
+ };
+}
+
+#endif
diff --git a/src/gfx/Shape.cpp b/src/gfx/Shape.cpp
new file mode 100644
index 0000000..348c0d5
--- /dev/null
+++ b/src/gfx/Shape.cpp
@@ -0,0 +1,97 @@
+#include "Shape.hpp"
+
+namespace ml
+{
+ /*explicit*/ Shape::Shape(std::unique_ptr mesh,
+ std::unique_ptr shader,
+ std::unique_ptr material)
+ : m_mesh { std::move(mesh) }
+ , m_shader { std::move(shader) }
+ , m_material { std::move(material) }
+ {
+ update();
+ }
+
+ /*virtual*/ Shape::~Shape()
+ {
+ glDeleteBuffers(ML_GL_BUFFER_COUNT, m_vbo);
+ glDeleteVertexArrays(1, &m_vao);
+ }
+
+ void Shape::draw() const
+ {
+ glBindVertexArray(m_vao);
+ m_shader->use();
+ m_material->use();
+
+ glDrawArrays(GL_TRIANGLES, 0, m_mesh->count());
+ }
+
+ void Shape::update()
+ {
+ m_shader->use();
+ m_shader->set_int("has_texture", m_material->has_texture() ? 1 : 0);
+
+ glGenVertexArrays(1, &m_vao);
+ glBindVertexArray(m_vao);
+
+ glGenBuffers(ML_GL_BUFFER_COUNT, m_vbo);
+
+ auto vecs = m_mesh->data();
+
+ // POS
+ // ---
+ glBindBuffer(GL_ARRAY_BUFFER, m_vbo[ML_GL_POS_BUFFER]);
+
+ glBufferData(GL_ARRAY_BUFFER,
+ m_mesh->count() * sizeof(glm::vec3),
+ vecs.data(),
+ GL_STATIC_DRAW);
+
+ glEnableVertexAttribArray(m_shader->attrib("pos"));
+ glVertexAttribPointer(m_shader->attrib("pos"),
+ 3,
+ GL_FLOAT,
+ GL_FALSE,
+ sizeof(glm::vec3),
+ nullptr);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vbo[ML_GL_POS_BUFFER]);
+
+ // COLOR
+ // -----
+ std::vector colors(vecs.size(),
+ m_material->color());
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vbo[ML_GL_COLOR_BUFFER]);
+ glBufferData(GL_ARRAY_BUFFER,
+ colors.size() * sizeof(glm::vec4),
+ colors.data(),
+ GL_STATIC_DRAW);
+
+ glEnableVertexAttribArray(m_shader->attrib("color"));
+ glVertexAttribPointer(m_shader->attrib("color"),
+ 4,
+ GL_FLOAT,
+ GL_FALSE,
+ sizeof(glm::vec4),
+ nullptr);
+ // TEX
+ // ---
+ std::vector texpos = m_mesh->tex_data();
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vbo[ML_GL_TEX_BUFFER]);
+ glBufferData(GL_ARRAY_BUFFER,
+ texpos.size() * sizeof(glm::vec2),
+ texpos.data(),
+ GL_STATIC_DRAW);
+
+ glEnableVertexAttribArray(m_shader->attrib("tex"));
+ glVertexAttribPointer(m_shader->attrib("tex"),
+ 2,
+ GL_FLOAT,
+ GL_FALSE,
+ sizeof(glm::vec2),
+ nullptr);
+ }
+}
diff --git a/src/gfx/Shape.hpp b/src/gfx/Shape.hpp
new file mode 100644
index 0000000..8953e51
--- /dev/null
+++ b/src/gfx/Shape.hpp
@@ -0,0 +1,49 @@
+#ifndef ml_SHAPE_HPP
+#define ml_SHAPE_HPP
+
+#include
+
+#include "../commons.hpp"
+#include "Mesh.hpp"
+#include "Shader.hpp"
+#include "Material.hpp"
+
+namespace ml
+{
+ enum ML_GL_BUFFER {
+ ML_GL_POS_BUFFER = 0,
+ ML_GL_COLOR_BUFFER,
+ ML_GL_TEX_BUFFER,
+ ML_GL_BUFFER_COUNT
+ };
+
+ /**
+ * Anything drawable should inherit from this class.
+ * @see Sprite
+ * @see Graphics
+ **/
+ class Shape
+ {
+ public:
+ explicit Shape(std::unique_ptr mesh,
+ std::unique_ptr shader,
+ std::unique_ptr material);
+ virtual ~Shape();
+
+ Shader const& shader() const { return *m_shader; }
+ void draw() const;
+
+ void update();
+
+ protected:
+ std::unique_ptr m_mesh;
+ std::unique_ptr m_shader;
+ std::unique_ptr m_material;
+
+ private:
+ GLuint m_vao;
+ GLuint m_vbo[ML_GL_BUFFER_COUNT];
+ };
+}
+
+#endif
diff --git a/src/gfx/Texture.cpp b/src/gfx/Texture.cpp
new file mode 100644
index 0000000..7a36646
--- /dev/null
+++ b/src/gfx/Texture.cpp
@@ -0,0 +1,48 @@
+#include "Texture.hpp"
+#include "../Services.hpp"
+#include "../res/Resources.hpp"
+#include "../res/ImageRes.hpp"
+
+namespace ml
+{
+ /*explicit*/ Texture::Texture()
+ {
+ glGenTextures(1, &m_tex);
+ }
+
+ /*virtual*/ Texture::~Texture()
+ {
+ glDeleteTextures(1, &m_tex);
+ }
+
+ void Texture::use() const
+ {
+ glBindTexture(GL_TEXTURE_2D, m_tex);
+ }
+
+ void Texture::load(std::string const& name)
+ {
+ auto res = ML_SYS(Resources)->get(name);
+ auto surface = res->data();
+ m_width = surface->w;
+ m_height = surface->h;
+
+ use();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+
+ glTexImage2D(GL_TEXTURE_2D,
+ 0,
+ GL_RGBA,
+ m_width,
+ m_height,
+ 0,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ surface->pixels);
+ }
+}
diff --git a/src/gfx/Texture.hpp b/src/gfx/Texture.hpp
new file mode 100644
index 0000000..de6b7b9
--- /dev/null
+++ b/src/gfx/Texture.hpp
@@ -0,0 +1,29 @@
+#ifndef ml_TEXTURE_HPP
+#define ml_TEXTURE_HPP
+
+#include
+#include
+#include "../commons.hpp"
+
+namespace ml
+{
+ class Texture
+ {
+ public:
+ explicit Texture();
+ virtual ~Texture();
+
+ unsigned width() const { return m_width; }
+ unsigned height() const { return m_height; }
+
+ void use() const;
+ void load(std::string const& name);
+
+ private:
+ GLuint m_tex;
+ unsigned m_width = 0;
+ unsigned m_height = 0;
+ };
+}
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
index 0699326..8b1f7d4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -3,9 +3,13 @@
#include "conf.hpp"
#include "commons.hpp"
+// Services
+#include "Services.hpp"
#include "logs/FileLogs.hpp"
#include "events/Events.hpp"
-#include "Services.hpp"
+#include "gfx/Graphics.hpp"
+#include "res/Resources.hpp"
+
#include "Game.hpp"
@@ -13,12 +17,18 @@ int main(int, char**)
{
ML->add
(std::filesystem::temp_directory_path() / "mornelune.log");
+
+ ML->add();
+
ML->add();
+ ML->add("Mornelune", 1024, 768);
+ ML->init();
ml::Game game;
-
game.start();
game.stop();
+ ML->free();
+
return 0;
}
diff --git a/src/res/BaseRes.cpp b/src/res/BaseRes.cpp
new file mode 100644
index 0000000..09a6b96
--- /dev/null
+++ b/src/res/BaseRes.cpp
@@ -0,0 +1,12 @@
+#include "BaseRes.hpp"
+
+namespace ml
+{
+ /*explicit*/ BaseRes::BaseRes()
+ {
+ }
+
+ /*virtual*/ BaseRes::~BaseRes()
+ {
+ }
+}
diff --git a/src/res/BaseRes.hpp b/src/res/BaseRes.hpp
new file mode 100644
index 0000000..fa51643
--- /dev/null
+++ b/src/res/BaseRes.hpp
@@ -0,0 +1,22 @@
+#ifndef ml_BASERES_HPP
+#define ml_BASERES_HPP
+
+#include "../commons.hpp"
+
+namespace ml
+{
+ ML_ERROR(resource_error);
+
+ class BaseRes
+ {
+ public:
+ explicit BaseRes();
+ virtual ~BaseRes();
+
+ virtual void load(std::filesystem::path const& path) = 0;
+
+ private:
+ };
+}
+
+#endif
diff --git a/src/res/ImageRes.cpp b/src/res/ImageRes.cpp
new file mode 100644
index 0000000..7ce1f99
--- /dev/null
+++ b/src/res/ImageRes.cpp
@@ -0,0 +1,28 @@
+#include "ImageRes.hpp"
+#include "src/res/BaseRes.hpp"
+
+namespace ml
+{
+ /*explicit*/ ImageRes::ImageRes()
+ {
+ }
+
+ /*virtual*/ ImageRes::~ImageRes()
+ {
+ if (m_surface)
+ {
+ SDL_FreeSurface(m_surface);
+ m_surface = nullptr;
+ }
+ }
+
+ void ImageRes::load(std::filesystem::path const& path) /*override*/
+ {
+ m_surface = IMG_Load(path.c_str());
+
+ if (!m_surface)
+ {
+ throw resource_error {"cannot load image '" + path.string() + "'"};
+ }
+ }
+}
diff --git a/src/res/ImageRes.hpp b/src/res/ImageRes.hpp
new file mode 100644
index 0000000..a36aeee
--- /dev/null
+++ b/src/res/ImageRes.hpp
@@ -0,0 +1,25 @@
+#ifndef ml_IMAGERES_HPP
+#define ml_IMAGERES_HPP
+
+#include
+#include "../commons.hpp"
+#include "BaseRes.hpp"
+
+namespace ml
+{
+ class ImageRes: public BaseRes
+ {
+ public:
+ explicit ImageRes();
+ virtual ~ImageRes();
+
+ SDL_Surface* data() const { return m_surface; }
+
+ void load(std::filesystem::path const& path) override;
+
+ private:
+ SDL_Surface* m_surface = nullptr;
+ };
+}
+
+#endif
diff --git a/src/res/Resources.cpp b/src/res/Resources.cpp
new file mode 100644
index 0000000..417fd96
--- /dev/null
+++ b/src/res/Resources.cpp
@@ -0,0 +1,53 @@
+#include
+namespace xml = tinyxml2;
+
+#include "../Services.hpp"
+#include "Resources.hpp"
+#include "TextRes.hpp"
+#include "ImageRes.hpp"
+#include "BaseRes.hpp"
+
+namespace ml
+{
+ /*explicit*/ Resources::Resources()
+ {
+ }
+
+ /*virtual*/ Resources::~Resources()
+ {
+ }
+
+ void Resources::init() /*override*/
+ {
+ xml::XMLDocument doc;
+ doc.LoadFile(ML_ASSET("manifest.xml").string().c_str());
+
+ auto* root = doc.RootElement();
+
+ xml::XMLElement* itr = root->FirstChildElement();
+
+ while (itr)
+ {
+ std::string type = itr->Attribute("type");
+ std::filesystem::path path = itr->Attribute("path");
+ std::string name = itr->Attribute("name");
+
+ if (type == "text")
+ {
+ ML_SYS(Resources)->load(name, path);
+ }
+ else if (type == "image")
+ {
+ ML_SYS(Resources)->load(name, path);
+ }
+ else
+ {
+ throw resource_error {"unknown resource '"
+ + name
+ + "' of type '" + type + "'"};
+ }
+
+ itr = itr->NextSiblingElement();
+ }
+ }
+}
diff --git a/src/res/Resources.hpp b/src/res/Resources.hpp
new file mode 100644
index 0000000..dff9e3d
--- /dev/null
+++ b/src/res/Resources.hpp
@@ -0,0 +1,64 @@
+#ifndef ml_RESOURCES_HPP
+#define ml_RESOURCES_HPP
+
+#include "../commons.hpp"
+#include "../Service.hpp"
+#include "BaseRes.hpp"
+#include
+
+namespace ml
+{
+ class Resources: public Service
+ {
+ public:
+ explicit Resources();
+ virtual ~Resources();
+
+ void init() override;
+
+ template
+ void load(std::string const& name,
+ std::filesystem::path const& path);
+
+
+ template
+ std::shared_ptr get(std::string const& name) const;
+
+ private:
+ std::unordered_map>
+ m_resources;
+ };
+
+ template
+ void Resources::load(std::string const& name,
+ std::filesystem::path const& path)
+ {
+ if (auto itr = m_resources.find(name);
+ itr == std::end(m_resources))
+ {
+ auto res = std::make_shared();
+ res->load(ML_ASSET(path));
+
+ m_resources.insert({name, res});
+
+ return;
+ }
+
+ throw resource_error {"resource '" + name + "' already exists"};
+ }
+
+ template
+ std::shared_ptr Resources::get(std::string const& name) const
+ {
+ if (auto itr = m_resources.find(name);
+ itr != std::end(m_resources))
+ {
+ return std::static_pointer_cast(itr->second);
+ }
+
+ throw resource_error {"cannot find resource '" + name + "'"};
+ }
+
+}
+
+#endif
diff --git a/src/res/TextRes.cpp b/src/res/TextRes.cpp
new file mode 100644
index 0000000..cd508a1
--- /dev/null
+++ b/src/res/TextRes.cpp
@@ -0,0 +1,29 @@
+#include "TextRes.hpp"
+
+namespace ml
+{
+ /*explicit*/ TextRes::TextRes()
+ {
+ }
+
+ /*virtual*/ TextRes::~TextRes()
+ {
+ }
+
+ void TextRes::load(std::filesystem::path const& path) /*override*/
+ {
+ std::stringstream ss;
+
+ std::ifstream file { path };
+
+ if (!file)
+ {
+ throw resource_error
+ {"cannot find resource '" + path.string() + "'"};
+ }
+
+ ss << file.rdbuf();
+
+ m_data = ss.str();
+ }
+}
diff --git a/src/res/TextRes.hpp b/src/res/TextRes.hpp
new file mode 100644
index 0000000..895735a
--- /dev/null
+++ b/src/res/TextRes.hpp
@@ -0,0 +1,23 @@
+#ifndef ml_TEXTRES_HPP
+#define ml_TEXTRES_HPP
+
+#include "BaseRes.hpp"
+
+namespace ml
+{
+ class TextRes: public BaseRes
+ {
+ public:
+ explicit TextRes();
+ virtual ~TextRes();
+
+ std::string data() const { return m_data; }
+
+ void load(std::filesystem::path const& path) override;
+
+ private:
+ std::string m_data;
+ };
+}
+
+#endif