draw sprites on the game window.

main
bog 2023-10-26 21:03:41 +02:00
parent 85b5b4e820
commit bb78d60407
34 changed files with 1159 additions and 4 deletions

BIN
assets/images/demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

14
assets/manifest.xml Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1" encoding="utf-8"?>
<resources>
<!-- SHADERS -->
<resource type="text" name="vertex-shader"
path="shaders/default_vertex.glsl"/>
<resource type="text" name="fragment-shader"
path="shaders/default_fragment.glsl"/>
<!-- IMAGES -->
<resource type="image" name="demo" path="images/demo.png"/>
</resources>

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -7,8 +7,11 @@ project('mornelune',
'cpp_stdd=c++17' 'cpp_stdd=c++17'
]) ])
share_dir = get_option('prefix') / get_option('datadir') / 'mornelune'
conf = configuration_data() conf = configuration_data()
conf.set('version', meson.project_version()) conf.set('version', meson.project_version())
conf.set('share_dir', share_dir)
configure_file( configure_file(
input: 'src/conf.in.hpp', input: 'src/conf.in.hpp',
@ -24,6 +27,7 @@ executable(
'src/Game.cpp', 'src/Game.cpp',
'src/Services.cpp', 'src/Services.cpp',
'src/Service.cpp', 'src/Service.cpp',
'src/Sprite.cpp',
# Logs # Logs
'src/logs/Logs.cpp', 'src/logs/Logs.cpp',
@ -32,8 +36,30 @@ executable(
# Events # Events
'src/events/Events.cpp', 'src/events/Events.cpp',
'src/events/Event.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: [ dependencies: [
dependency('sdl2'),
dependency('SDL2_image'),
dependency('glew'),
dependency('glm'),
dependency('tinyxml2')
], ],
install: true install: true
) )
install_subdir('assets', install_dir: share_dir)

View File

@ -1,7 +1,9 @@
#include "Game.hpp" #include "Game.hpp"
#include "logs/FileLogs.hpp" #include "logs/FileLogs.hpp"
#include "src/events/Events.hpp" #include "events/Events.hpp"
#include "src/logs/Logs.hpp" #include "logs/Logs.hpp"
#include "gfx/Graphics.hpp"
#include "Sprite.hpp"
namespace ml namespace ml
{ {
@ -16,6 +18,28 @@ namespace ml
void Game::start() void Game::start()
{ {
ML_SYS(FileLogs)->log(LEVEL_INFO, "game started"); 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() void Game::stop()

View File

@ -19,6 +19,7 @@ namespace ml
void stop(); void stop();
private: private:
bool m_running = false;
}; };
} }

View File

@ -21,4 +21,20 @@ namespace ml
/*virtual*/ Services::~Services() /*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();
}
}
} }

View File

@ -16,6 +16,9 @@ namespace ml
virtual ~Services(); virtual ~Services();
void init();
void free();
template<typename T> template<typename T>
size_t id(); size_t id();

45
src/Sprite.cpp Normal file
View File

@ -0,0 +1,45 @@
#include "Sprite.hpp"
namespace ml
{
/*explicit*/ Sprite::Sprite(float x, float y, float w, float h)
: Shape(std::make_unique<Mesh>(glm::vec3{x, y, 0.0f},
glm::vec3{w, h, 0.0f}),
std::make_unique<Shader>(),
std::make_unique<Material>())
, 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<Material>(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<Material>(name);
float const tw = m_material->texture().width();
float const th = m_material->texture().height();
m_mesh = std::make_unique<Mesh>(m_pos, m_size,
glm::vec2 {x / tw,
y / th},
glm::vec2 {w / tw,
h / th});
update();
}
}

34
src/Sprite.hpp Normal file
View File

@ -0,0 +1,34 @@
#ifndef ml_SPRITE_HPP
#define ml_SPRITE_HPP
#include <glm/glm.hpp>
#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

View File

@ -1,6 +1,8 @@
#ifndef ml_COMMONS_HPP #ifndef ml_COMMONS_HPP
#define ml_COMMONS_HPP #define ml_COMMONS_HPP
#include "conf.hpp"
#include <functional> #include <functional>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@ -12,6 +14,9 @@
#include <fstream> #include <fstream>
#include <optional> #include <optional>
#define ML_ASSET(NAME) \
(std::filesystem::path(ML_SHARE_DIR) / "assets" / NAME)
#define ML ml::Services::get() #define ML ml::Services::get()
#define ML_SYS(SYSTEM) ML->get<SYSTEM>() #define ML_SYS(SYSTEM) ML->get<SYSTEM>()

View File

@ -2,5 +2,6 @@
#define ml_CONF_HPP #define ml_CONF_HPP
#define ML_VERSION std::string("@version@") #define ML_VERSION std::string("@version@")
#define ML_SHARE_DIR std::string("@share_dir@")
#endif #endif

89
src/gfx/Graphics.cpp Normal file
View File

@ -0,0 +1,89 @@
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#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<float>(m_width),
static_cast<float>(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);
}
}

43
src/gfx/Graphics.hpp Normal file
View File

@ -0,0 +1,43 @@
#ifndef ml_GRAPHICS_HPP
#define ml_GRAPHICS_HPP
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <GL/glew.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#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

27
src/gfx/Material.cpp Normal file
View File

@ -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<Texture>() }
{
m_texture->load(texture_name);
}
/*virtual*/ Material::~Material()
{
}
void Material::use() const
{
if (m_texture)
{
m_texture->use();
}
}
}

30
src/gfx/Material.hpp Normal file
View File

@ -0,0 +1,30 @@
#ifndef ml_MATERIAL_HPP
#define ml_MATERIAL_HPP
#include <glm/glm.hpp>
#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<Texture> m_texture;
};
}
#endif

78
src/gfx/Mesh.cpp Normal file
View File

@ -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;
}
}

44
src/gfx/Mesh.hpp Normal file
View File

@ -0,0 +1,44 @@
#ifndef ml_MESH_HPP
#define ml_MESH_HPP
#include <glm/glm.hpp>
#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<glm::vec3> data() const { return m_vertices; }
std::vector<glm::vec2> tex_data() const { return m_textures; }
size_t count() const { return m_vertices.size(); }
glm::vec3 get(size_t index) const;
private:
std::vector<glm::vec3> m_vertices;
std::vector<glm::vec2> m_textures;
};
}
#endif

116
src/gfx/Shader.cpp Normal file
View File

@ -0,0 +1,116 @@
#include <glm/gtc/type_ptr.hpp>
#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<TextRes>(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;
}
}

34
src/gfx/Shader.hpp Normal file
View File

@ -0,0 +1,34 @@
#ifndef ml_SHADER_HPP
#define ml_SHADER_HPP
#include <glm/glm.hpp>
#include <GL/glew.h>
#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<GLuint> m_shaders;
GLuint load_shader(GLenum shader_type, std::string const& name);
};
}
#endif

97
src/gfx/Shape.cpp Normal file
View File

@ -0,0 +1,97 @@
#include "Shape.hpp"
namespace ml
{
/*explicit*/ Shape::Shape(std::unique_ptr<Mesh> mesh,
std::unique_ptr<Shader> shader,
std::unique_ptr<Material> 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<glm::vec4> 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<glm::vec2> 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);
}
}

49
src/gfx/Shape.hpp Normal file
View File

@ -0,0 +1,49 @@
#ifndef ml_SHAPE_HPP
#define ml_SHAPE_HPP
#include <GL/glew.h>
#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> mesh,
std::unique_ptr<Shader> shader,
std::unique_ptr<Material> material);
virtual ~Shape();
Shader const& shader() const { return *m_shader; }
void draw() const;
void update();
protected:
std::unique_ptr<Mesh> m_mesh;
std::unique_ptr<Shader> m_shader;
std::unique_ptr<Material> m_material;
private:
GLuint m_vao;
GLuint m_vbo[ML_GL_BUFFER_COUNT];
};
}
#endif

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

@ -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<ImageRes>(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);
}
}

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

@ -0,0 +1,29 @@
#ifndef ml_TEXTURE_HPP
#define ml_TEXTURE_HPP
#include <GL/glew.h>
#include <SDL2/SDL.h>
#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

View File

@ -3,9 +3,13 @@
#include "conf.hpp" #include "conf.hpp"
#include "commons.hpp" #include "commons.hpp"
// Services
#include "Services.hpp"
#include "logs/FileLogs.hpp" #include "logs/FileLogs.hpp"
#include "events/Events.hpp" #include "events/Events.hpp"
#include "Services.hpp" #include "gfx/Graphics.hpp"
#include "res/Resources.hpp"
#include "Game.hpp" #include "Game.hpp"
@ -13,12 +17,18 @@ int main(int, char**)
{ {
ML->add<ml::FileLogs> ML->add<ml::FileLogs>
(std::filesystem::temp_directory_path() / "mornelune.log"); (std::filesystem::temp_directory_path() / "mornelune.log");
ML->add<ml::Resources>();
ML->add<ml::Events>(); ML->add<ml::Events>();
ML->add<ml::Graphics>("Mornelune", 1024, 768);
ML->init();
ml::Game game; ml::Game game;
game.start(); game.start();
game.stop(); game.stop();
ML->free();
return 0; return 0;
} }

12
src/res/BaseRes.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "BaseRes.hpp"
namespace ml
{
/*explicit*/ BaseRes::BaseRes()
{
}
/*virtual*/ BaseRes::~BaseRes()
{
}
}

22
src/res/BaseRes.hpp Normal file
View File

@ -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

28
src/res/ImageRes.cpp Normal file
View File

@ -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() + "'"};
}
}
}

25
src/res/ImageRes.hpp Normal file
View File

@ -0,0 +1,25 @@
#ifndef ml_IMAGERES_HPP
#define ml_IMAGERES_HPP
#include <SDL2/SDL_image.h>
#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

53
src/res/Resources.cpp Normal file
View File

@ -0,0 +1,53 @@
#include <tinyxml2.h>
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<TextRes>(name, path);
}
else if (type == "image")
{
ML_SYS(Resources)->load<ImageRes>(name, path);
}
else
{
throw resource_error {"unknown resource '"
+ name
+ "' of type '" + type + "'"};
}
itr = itr->NextSiblingElement();
}
}
}

64
src/res/Resources.hpp Normal file
View File

@ -0,0 +1,64 @@
#ifndef ml_RESOURCES_HPP
#define ml_RESOURCES_HPP
#include "../commons.hpp"
#include "../Service.hpp"
#include "BaseRes.hpp"
#include <memory>
namespace ml
{
class Resources: public Service
{
public:
explicit Resources();
virtual ~Resources();
void init() override;
template <typename T>
void load(std::string const& name,
std::filesystem::path const& path);
template <typename T>
std::shared_ptr<T> get(std::string const& name) const;
private:
std::unordered_map<std::string, std::shared_ptr<BaseRes>>
m_resources;
};
template <typename T>
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<T>();
res->load(ML_ASSET(path));
m_resources.insert({name, res});
return;
}
throw resource_error {"resource '" + name + "' already exists"};
}
template <typename T>
std::shared_ptr<T> Resources::get(std::string const& name) const
{
if (auto itr = m_resources.find(name);
itr != std::end(m_resources))
{
return std::static_pointer_cast<T>(itr->second);
}
throw resource_error {"cannot find resource '" + name + "'"};
}
}
#endif

29
src/res/TextRes.cpp Normal file
View File

@ -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();
}
}

23
src/res/TextRes.hpp Normal file
View File

@ -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