feat: systems
Some checks reported errors
continuous-integration/drone/push Build was killed

feat: surface system

This commit puts the project in major jeopardy as it overhauls the
architecture such as removing the layer stack completely, etc.

I am filled with determination.
This commit is contained in:
light7734 2025-07-26 18:01:27 +03:30
parent d9229ad912
commit 2b96a85b62
Signed by: light7734
GPG key ID: 8C30176798F1A6BA
52 changed files with 1094 additions and 1751 deletions

View file

@ -4,22 +4,21 @@ add_subdirectory(./time)
add_subdirectory(./logger)
add_subdirectory(./debug)
add_subdirectory(./math)
#
add_subdirectory(./asset_baker)
add_subdirectory(./asset_parser)
add_subdirectory(./asset_manager)
# add_subdirectory(./asset_manager)
#
add_subdirectory(./camera)
add_subdirectory(./input)
add_subdirectory(./ui)
add_subdirectory(./window)
add_subdirectory(./renderer)
# add_subdirectory(./input)
# add_subdirectory(./ui)
#
add_subdirectory(./surface)
# add_subdirectory(./renderer)
add_subdirectory(./ecs)
#
add_subdirectory(./app)
# apps
add_subdirectory(./mirror)
add_subdirectory(test)

View file

@ -1,21 +1,2 @@
add_library_module(app
application.cpp
layer.cpp
layer_stack.cpp
)
target_link_libraries(app
PUBLIC
renderer
logger
ui
asset_parser
asset_manager
lt_debug
ecs
window
glad
time
opengl::opengl
EnTT::EnTT
)
add_library_module(app application.cpp)
target_link_libraries(app PRIVATE lt_debug)

View file

@ -1,203 +1,48 @@
#include <app/application.hpp>
#include <app/layer.hpp>
#include <app/layer_stack.hpp>
#include <asset_manager/asset_manager.hpp>
#include <input/events/event.hpp>
#include <input/events/keyboard.hpp>
#include <input/events/window.hpp>
#include <input/input.hpp>
#include <lt_debug/assertions.hpp>
#include <ranges>
#include <renderer/blender.hpp>
#include <renderer/graphics_context.hpp>
#include <renderer/render_command.hpp>
#include <renderer/renderer.hpp>
#include <ui/ui.hpp>
#include <window/window.hpp>
#include <app/system.hpp>
namespace lt {
Application *Application::s_instance = nullptr;
Application::Application(): m_window(nullptr)
{
ensure(!s_instance, "Application constructed twice");
s_instance = this;
m_window = Window::create([this](auto &&PH1) { on_event(std::forward<decltype(PH1)>(PH1)); });
// create graphics context
m_graphics_context = GraphicsContext::create(
GraphicsAPI::OpenGL,
(GLFWwindow *)m_window->get_handle()
);
AssetManager::load_shader(
"LT_ENGINE_RESOURCES_TEXTURE_SHADER",
"data/assets/shaders/texture/vs.asset",
"data/assets/shaders/texture/ps.asset"
);
AssetManager::load_shader(
"LT_ENGINE_RESOURCES_TINTED_TEXTURE_SHADER",
"data/assets/shaders/tinted_texture/vs.asset",
"data/assets/shaders/tinted_texture/ps.asset"
);
AssetManager::load_shader(
"LT_ENGINE_RESOURCES_QUAD_SHADER",
"data/assets/shaders/quads/vs.asset",
"data/assets/shaders/quads/ps.asset"
);
m_renderer = Renderer::create(
(GLFWwindow *)m_window->get_handle(),
lt::GraphicsContext::get_shared_context(),
Renderer::CreateInfo {
.quad_renderer_shader = AssetManager::get_shader("LT_ENGINE_RESOURCES_QUAD_SHADER"),
.texture_renderer_shader = AssetManager::get_shader(
"LT_ENGINE_RESOURCES_TEXTURE_SHADER"
),
.tinted_texture_renderer_shader = AssetManager::get_shader(
"LT_ENGINE_RESOURCES_TINTED_"
"TEXTURE_SHADER"
),
}
);
ensure(m_graphics_context, "lWindow::lWindow: failed to create 'GraphicsContext'");
m_user_interface = UserInterface::create(
(GLFWwindow *)m_window->get_handle(),
lt::GraphicsContext::get_shared_context()
);
m_layer_stack = create_scope<LayerStack>();
}
Application::~Application()
{
/** This is required to make forward-declarations possible:
* https://stackoverflow.com/questions/34072862/why-is-error-invalid-application-of-sizeof-to-an-incomplete-type-using-uniqu
*/
}
namespace lt::app {
void Application::game_loop()
{
m_window->set_visibility(true);
while (!m_window->is_closed())
for (auto &system : m_systems)
{
update_layers();
render_layers();
render_user_interface();
poll_events();
}
}
void Application::quit()
{
s_instance->m_window->close();
}
void Application::update_layers()
{
for (auto &it : *m_layer_stack)
{
// narrowing double -> float
it->on_update(static_cast<float>(m_timer.elapsed_time().count()));
system->init();
}
// TODO(Light): each layer should have their own "delta time"
m_timer.reset();
}
void Application::render_layers()
{
m_renderer->begin_frame();
for (auto &it : *m_layer_stack)
while (true)
{
it->on_render();
}
m_renderer->end_frame();
}
void Application::render_user_interface()
{
m_user_interface->begin();
for (auto &it : *m_layer_stack)
{
it->on_user_interface_update();
}
m_user_interface->end();
}
void Application::poll_events()
{
m_window->poll_events();
}
void Application::on_event(const Event &event)
{
// window
if (event.has_category(WindowEventCategory))
{
m_window->on_event(event);
if (event.get_event_type() == EventType::WindowResized)
for (auto &system : m_systems)
{
m_renderer->on_window_resize(dynamic_cast<const WindowResizedEvent &>(event));
if (system->tick())
{
return;
}
}
}
// input
if (event.has_category(InputEventCategory))
{
Input::instance().on_event(event);
if (!Input::instance().is_receiving_game_events())
for (auto &system : m_systems_to_be_removed)
{
return;
m_systems.erase(
std::remove(m_systems.begin(), m_systems.end(), system),
m_systems.end()
);
}
}
for (auto &it : std::ranges::reverse_view(*m_layer_stack))
{
if (it->on_event(event))
if (m_systems.empty())
{
return;
}
}
}
[[nodiscard]] auto Application::sanity_check() const -> bool
void Application::register_system(Ref<app::ISystem> system)
{
log_inf("Checking application sanity...");
ensure(s_instance, "Application not constructed!?");
ensure(m_window, "Window is not initialized");
ensure(m_user_interface, "User interface is not initialized");
ensure(m_graphics_context, "Graphics context is not initialized");
ensure(m_renderer, "Renderer is not initialized");
ensure(m_layer_stack, "Layer_stack is not initialized");
ensure(!m_layer_stack->is_empty(), "Layer_stack is empty");
log_inf("Logging application state...");
this->log_debug_data();
m_graphics_context->log_debug_data();
m_user_interface->log_debug_data();
return true;
m_systems.emplace_back(std::move(system));
}
void Application::log_debug_data() const
void Application::unregister_system(Ref<app::ISystem> system)
{
log_inf("Platform::");
log_inf(" Platform name: {}", constants::platform_name);
log_inf(" Platform identifier: {}", std::to_underlying(constants::platform));
log_inf(" CWD: {}", std::filesystem::current_path().generic_string());
m_systems_to_be_removed.emplace_back(std::move(system));
}
} // namespace lt
} // namespace lt::app

View file

@ -1,47 +0,0 @@
#include <app/layer.hpp>
#include <input/events/char.hpp>
#include <input/events/event.hpp>
#include <input/events/keyboard.hpp>
#include <input/events/mouse.hpp>
#include <input/events/window.hpp>
namespace lt {
Layer::Layer(std::string name): m_layer_name(std::move(name))
{
}
auto Layer::on_event(const Event &event) -> bool
{
switch (event.get_event_type())
{
case EventType::MouseMoved: return on_mouse_moved(dynamic_cast<const MouseMovedEvent &>(event));
case EventType::ButtonPressed:
return on_button_pressed(dynamic_cast<const ButtonPressedEvent &>(event));
case EventType::ButtonReleased:
return on_button_released(dynamic_cast<const ButtonReleasedEvent &>(event));
case EventType::WheelScrolled:
return on_wheel_scrolled(dynamic_cast<const WheelScrolledEvent &>(event));
case EventType::KeyPressed: return on_key_pressed(dynamic_cast<const KeyPressedEvent &>(event));
case EventType::KeyRepeated: return on_key_repeat(dynamic_cast<const KeyRepeatEvent &>(event));
case EventType::KeyReleased:
return on_key_released(dynamic_cast<const KeyReleasedEvent &>(event));
case EventType::SetChar: return on_set_char(dynamic_cast<const SetCharEvent &>(event));
case EventType::WindowClosed:
return on_window_closed(dynamic_cast<const WindowClosedEvent &>(event));
case EventType::WindowResized:
return on_window_resized(dynamic_cast<const WindowResizedEvent &>(event));
case EventType::WindowMoved:
return on_window_moved(dynamic_cast<const WindowMovedEvent &>(event));
case EventType::WindowLostFocus:
return on_window_lost_focus(dynamic_cast<const WindowLostFocusEvent &>(event));
case EventType::WindowGainFocus:
return on_window_gain_focus(dynamic_cast<const WindowGainFocusEvent &>(event));
default: ensure(false, "Invalid event: {}", event.get_info_lt_log());
}
}
} // namespace lt

View file

@ -1,18 +0,0 @@
#include <app/layer.hpp>
#include <app/layer_stack.hpp>
namespace lt {
void LayerStack::attach_layer(Ref<Layer> layer)
{
log_trc("Attaching layer [{}]", layer->get_name());
m_layers.emplace_back(std::move(layer));
}
void LayerStack::detach_layer(const Ref<Layer> &layer)
{
log_trc("Detaching layer [{}]", layer->get_name());
m_layers.erase(std::find(m_layers.begin(), m_layers.end(), layer));
}
} // namespace lt

View file

@ -1,18 +1,15 @@
#pragma once
#include <time/timer.hpp>
namespace lt::app {
namespace lt {
class Renderer;
class Window;
class Event;
class GraphicsContext;
class UserInterface;
class LayerStack;
class ISystem;
extern Scope<class Application> create_application();
/** The main application class.
* Think of this like an aggregate of systems, you register systems through this interface.
* Then they'll tick every "application frame".
*/
class Application
{
public:
@ -24,54 +21,22 @@ public:
auto operator=(Application &&) -> Application & = delete;
virtual ~Application();
[[nodiscard]] auto sanity_check() const -> bool;
virtual ~Application() = default;
void game_loop();
[[nodiscard]] auto get_window() -> Window &
{
return *m_window;
}
void register_system(Ref<app::ISystem> system);
[[nodiscard]] auto get_layer_stack() -> LayerStack &
{
return *m_layer_stack;
}
static void quit();
void unregister_system(Ref<app::ISystem> system);
protected:
Application();
Application() = default;
private:
void update_layers();
std::vector<Ref<app::ISystem>> m_systems;
void render_layers();
void render_user_interface();
void poll_events();
void on_event(const Event &event);
void log_debug_data() const;
Timer m_timer;
Scope<Window> m_window;
Scope<UserInterface> m_user_interface;
Scope<GraphicsContext> m_graphics_context;
Scope<Renderer> m_renderer;
Scope<LayerStack> m_layer_stack;
static Application *s_instance;
std::vector<Ref<app::ISystem>> m_systems_to_be_removed;
};
} // namespace lt
} // namespace lt::app

View file

@ -2,21 +2,21 @@
#include <app/application.hpp>
int main(int argc, char *argv[]) // NOLINT
auto main(int argc, char *argv[]) -> int32_t
try
{
std::ignore = argc;
std::ignore = argv;
auto application = lt::Scope<lt::Application> {};
auto application = lt::Scope<lt::app::Application> {};
application = lt::create_application();
lt::ensure(application, "Failed to create application");
lt::ensure(application->sanity_check(), "Failed to verify the sanity of the application");
application = lt::app::create_application();
if (!application)
{
throw std::runtime_error { "Failed to create application\n" };
}
application->game_loop();
return EXIT_SUCCESS;
}
catch (const std::exception &exp)

View file

@ -1,116 +0,0 @@
#pragma once
namespace lt {
class Event;
class MouseMovedEvent;
class ButtonPressedEvent;
class ButtonReleasedEvent;
class WheelScrolledEvent;
class KeyPressedEvent;
class KeyRepeatEvent;
class KeyReleasedEvent;
class SetCharEvent;
class WindowClosedEvent;
class WindowResizedEvent;
class WindowMovedEvent;
class WindowLostFocusEvent;
class WindowGainFocusEvent;
class Layer
{
public:
Layer(std::string name);
virtual ~Layer() = default;
[[nodiscard]] auto get_name() const -> const std::string &
{
return m_layer_name;
}
virtual void on_update(float deltaTime)
{
}
virtual void on_user_interface_update()
{
}
virtual void on_render()
{
}
auto on_event(const Event &event) -> bool;
protected:
std::string m_layer_name;
virtual auto on_mouse_moved(const MouseMovedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_button_pressed(const ButtonPressedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_button_released(const ButtonReleasedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_wheel_scrolled(const WheelScrolledEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_key_pressed(const KeyPressedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_key_repeat(const KeyRepeatEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_key_released(const KeyReleasedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_set_char(const SetCharEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_window_closed(const WindowClosedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_window_resized(const WindowResizedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_window_moved(const WindowMovedEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_window_lost_focus(const WindowLostFocusEvent & /*event*/) -> bool
{
return false;
}
virtual auto on_window_gain_focus(const WindowGainFocusEvent & /*event*/) -> bool
{
return false;
}
};
} // namespace lt

View file

@ -1,50 +0,0 @@
#pragma once
namespace lt {
class Layer;
class Event;
class LayerStack
{
public:
template<typename Layer_T, typename... Args>
void emplace_layer(Args &&...args)
{
attach_layer(create_ref<Layer_T>(std::forward<Args>(args)...));
}
void attach_layer(Ref<Layer> layer);
void detach_layer(const Ref<Layer> &layer);
[[nodiscard]] auto is_empty() const -> bool
{
return m_layers.empty();
}
[[nodiscard]] auto begin() -> std::vector<Ref<Layer>>::iterator
{
return m_layers.begin();
}
[[nodiscard]] auto end() -> std::vector<Ref<Layer>>::iterator
{
return m_layers.end();
}
[[nodiscard]] auto rbegin() -> std::vector<Ref<Layer>>::reverse_iterator
{
return m_layers.rbegin();
}
[[nodiscard]] auto rend() -> std::vector<Ref<Layer>>::reverse_iterator
{
return m_layers.rend();
}
private:
std::vector<Ref<Layer>> m_layers;
};
} // namespace lt

View file

@ -0,0 +1,25 @@
#pragma once
namespace lt::app {
class ISystem
{
public:
ISystem() = default;
virtual ~ISystem() = default;
ISystem(ISystem &&) = default;
ISystem(const ISystem &) = delete;
auto operator=(ISystem &&) -> ISystem & = default;
auto operator=(const ISystem &) -> ISystem & = delete;
virtual void init() = 0;
virtual auto tick() -> bool = 0;
};
} // namespace lt::app

View file

@ -5,5 +5,5 @@ add_library_module(asset_manager
target_link_libraries(
asset_manager
PUBLIC asset_parser
PRIVATE renderer
PRIVATE logger
)

View file

@ -1,5 +1,4 @@
add_library_module(ecs entity.cpp scene.cpp uuid.cpp serializer.cpp)
add_library_module(ecs entity.cpp scene.cpp uuid.cpp )
target_link_libraries(ecs
PUBLIC logger lt_debug EnTT::EnTT renderer input camera
PRIVATE yaml-cpp::yaml-cpp asset_manager
PUBLIC logger lt_debug EnTT::EnTT input camera math
)

View file

@ -1,73 +1,9 @@
#include <camera/component.hpp>
#include <ecs/components.hpp>
#include <ecs/entity.hpp>
#include <ecs/scene.hpp>
#include <renderer/renderer.hpp>
namespace lt {
void Scene::on_create()
{
/* native scripts */
{
m_registry.view<NativeScriptComponent>().each([](NativeScriptComponent &nsc) {
if (nsc.instance == nullptr)
{
nsc.instance = nsc.CreateInstance();
nsc.instance->on_create();
}
});
}
}
void Scene::on_update(float deltaTime)
{
/* native scripts */
{
m_registry.view<NativeScriptComponent>().each([=](NativeScriptComponent &nsc) {
nsc.instance->on_update(deltaTime);
});
}
}
void Scene::on_render(const Ref<Framebuffer> &targetFrameBuffer /* = nullptr */)
{
auto *sceneCamera = (Camera *)nullptr;
auto *sceneCameraTransform = (TransformComponent *)nullptr;
/* scene camera */
{
m_registry.group(entt::get<TransformComponent, CameraComponent>)
.each([&](TransformComponent &transformComp, CameraComponent &cameraComp) {
if (cameraComp.isPrimary)
{
sceneCamera = &cameraComp.camera;
sceneCameraTransform = &transformComp;
}
});
}
/* draw quads */
{
if (sceneCamera)
{
Renderer::begin_scene(sceneCamera, *sceneCameraTransform, targetFrameBuffer);
m_registry.group(entt::get<TransformComponent, SpriteRendererComponent>)
.each([](TransformComponent &transformComp,
SpriteRendererComponent &spriteRendererComp) {
Renderer::draw_quad(
transformComp,
spriteRendererComp.tint,
spriteRendererComp.texture
);
});
Renderer::end_scene();
}
}
}
auto Scene::create_entity(const std::string &name, const TransformComponent &transform) -> Entity
{
return create_entity_with_uuid(name, UUID(), transform);

View file

@ -3,6 +3,7 @@
#include <ecs/components/transform.hpp>
#include <ecs/uuid.hpp>
#include <entt/entt.hpp>
#include <functional>
namespace lt {
@ -12,11 +13,11 @@ class Framebuffer;
class Scene
{
public:
void on_create();
void on_update(float deltaTime);
void on_render(const Ref<Framebuffer> &targetFrameBuffer = nullptr);
template<typename... T>
auto group()
{
return m_registry.group(entt::get<T...>);
}
auto create_entity(
const std::string &name,
@ -25,6 +26,11 @@ public:
auto get_entity_by_tag(const std::string &tag) -> Entity;
auto get_entt_registry() -> entt::registry &
{
return m_registry;
}
private:
friend class Entity;
@ -41,4 +47,12 @@ private:
) -> Entity;
};
namespace ecs {
using Registry = Scene;
using Entity = ::lt::Entity;
} // namespace ecs
} // namespace lt

View file

@ -1,2 +1,2 @@
add_library_module(input input.cpp)
target_link_libraries(input PUBLIC math imgui::imgui logger)
target_link_libraries(input PUBLIC surface math imgui::imgui logger)

View file

@ -1,10 +1,5 @@
#include <imgui.h>
#include <input/events/char.hpp>
#include <input/events/event.hpp>
#include <input/events/keyboard.hpp>
#include <input/events/mouse.hpp>
#include <input/input.hpp>
#include <input/key_codes.hpp>
#include <logger/logger.hpp>
namespace lt {

View file

@ -0,0 +1,5 @@
#include <input/system.hpp>
namespace lt::input {
} // namespace lt::input

View file

@ -1,41 +0,0 @@
#pragma once
#include <input/events/event.hpp>
#include <sstream>
namespace lt {
class SetCharEvent: public Event
{
public:
SetCharEvent(unsigned int character): m_character(character)
{
}
[[nodiscard]] auto get_character() const -> int
{
return m_character;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "CharSet: " << m_character;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::SetChar;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | KeyboardEventCategory) & category;
}
private:
const unsigned int m_character;
};
} // namespace lt

View file

@ -1,56 +0,0 @@
#pragma once
namespace lt {
enum class EventType : uint8_t
{
None = 0,
// input
MouseMoved,
WheelScrolled,
ButtonPressed,
ButtonReleased,
KeyPressed,
KeyRepeated,
KeyReleased,
SetChar,
// window
WindowMoved,
WindowResized,
WindowClosed,
WindowLostFocus,
WindowGainFocus,
};
enum EventCategory : uint8_t
{
None = 0,
WindowEventCategory = bit(0),
InputEventCategory = bit(1),
KeyboardEventCategory = bit(2),
MouseEventCategory = bit(3),
};
class Event
{
public:
Event() = default;
virtual ~Event() = default;
[[nodiscard]] virtual auto get_event_type() const -> EventType = 0;
[[nodiscard]] virtual auto get_info_lt_log() const -> std::string = 0;
[[nodiscard]] virtual auto has_category(EventCategory category) const -> bool = 0;
friend auto operator<<(std::ostream &os, const Event &e) -> std::ostream &
{
return os << e.get_info_lt_log();
}
};
} // namespace lt

View file

@ -1,107 +0,0 @@
#pragma once
#include <input/events/event.hpp>
#include <sstream>
namespace lt {
class KeyPressedEvent: public Event
{
public:
KeyPressedEvent(int key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> int
{
return m_key;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "KeyPressed: " << m_key;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::KeyPressed;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | KeyboardEventCategory) & category;
}
private:
const int m_key;
};
class KeyRepeatEvent: public Event
{
public:
KeyRepeatEvent(int key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> int
{
return m_key;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "KeyRepeated: " << m_key;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::KeyRepeated;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | KeyboardEventCategory) & category;
}
private:
const int m_key;
};
class KeyReleasedEvent: public Event
{
public:
KeyReleasedEvent(int key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> int
{
return m_key;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "KeyReleased: " << m_key;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::KeyReleased;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | KeyboardEventCategory) & category;
}
private:
const int m_key;
};
} // namespace lt

View file

@ -1,151 +0,0 @@
#pragma once
#include <input/events/event.hpp>
#include <math/vec2.hpp>
#include <sstream>
namespace lt {
class MouseMovedEvent: public Event
{
public:
MouseMovedEvent(float x, float y): m_position(x, y)
{
}
[[nodiscard]] auto get_position() const -> const math::vec2 &
{
return m_position;
}
[[nodiscard]] auto get_x() const -> float
{
return m_position.x;
}
[[nodiscard]] auto get_y() const -> float
{
return m_position.y;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "MouseMoved: " << m_position.x << ", " << m_position.y;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::MouseMoved;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | MouseEventCategory) & category;
}
private:
const math::vec2 m_position;
};
class WheelScrolledEvent: public Event
{
public:
WheelScrolledEvent(float offset): m_offset(offset)
{
}
[[nodiscard]] auto get_offset() const -> float
{
return m_offset;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "WheelScrolled: " << m_offset;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::WheelScrolled;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | MouseEventCategory) & category;
}
private:
const float m_offset;
};
class ButtonPressedEvent: public Event
{
public:
ButtonPressedEvent(int button): m_button(button)
{
}
[[nodiscard]] auto get_button() const -> int
{
return m_button;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "ButtonPressed: " << m_button;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::ButtonPressed;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | MouseEventCategory) & category;
}
private:
const int m_button;
};
class ButtonReleasedEvent: public Event
{
public:
ButtonReleasedEvent(int button): m_button(button)
{
}
[[nodiscard]] auto get_button() const -> int
{
return m_button;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "ButtonReleased: " << m_button;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::ButtonReleased;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(InputEventCategory | MouseEventCategory) & category;
}
private:
const int m_button;
};
} // namespace lt

View file

@ -1,133 +0,0 @@
#pragma once
#include <input/events/event.hpp>
#include <math/vec2.hpp>
#include <sstream>
namespace lt {
class WindowClosedEvent: public Event
{
public:
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
return "WindowClosedEvent";
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::WindowClosed;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(WindowEventCategory) & category;
}
};
class WindowMovedEvent: public Event
{
public:
WindowMovedEvent(int x, int y): m_position(x, y)
{
}
[[nodiscard]] auto get_position() const -> const math::ivec2 &
{
return m_position;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "WindwoMoved: " << m_position.x << ", " << m_position.y;
return ss.str();
;
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::WindowMoved;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(WindowEventCategory) & category;
}
private:
const math::ivec2 m_position;
};
class WindowResizedEvent: public Event
{
public:
WindowResizedEvent(unsigned int width, unsigned int height): m_size(width, height)
{
}
[[nodiscard]] auto get_size() const -> const math::uvec2 &
{
return m_size;
}
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
std::stringstream ss;
ss << "WindowResized: " << m_size.x << ", " << m_size.y;
return ss.str();
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::WindowResized;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(WindowEventCategory) & category;
}
private:
const math::uvec2 m_size;
};
class WindowLostFocusEvent: public Event
{
public:
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
return "WindowLostFocus";
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::WindowLostFocus;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(WindowEventCategory) & category;
}
};
class WindowGainFocusEvent: public Event
{
public:
[[nodiscard]] auto get_info_lt_log() const -> std::string override
{
return "WindowGainFocus";
}
[[nodiscard]] auto get_event_type() const -> EventType override
{
return ::lt::EventType::WindowGainFocus;
}
[[nodiscard]] auto has_category(EventCategory category) const -> bool override
{
return static_cast<uint8_t>(WindowEventCategory) & category;
}
};
} // namespace lt

View file

@ -0,0 +1,64 @@
#pragma once
#include <surface/system.hpp>
namespace lt::input {
template<class... Ts>
struct overloads: Ts...
{
using Ts::operator()...;
};
/**
*
* @note If this system is attached, it will always consume the input events f rom surface.
* Therefore if you want any input detection mechanism, callbacks should be setup with this
* system and not directly with surface.
*/
class System
{
public:
System(lt::surface::System &surface_system)
{
surface_system.add_event_listener([this](auto &&event) {
return handle_event(std::forward<decltype(event)>(event));
});
};
private:
auto handle_event(const lt::surface::System::Event &event) -> bool
{
const auto visitor = overloads {
[this](const lt::surface::KeyPressedEvent &event) {
m_keys[event.get_key()] = true;
return true;
},
[](const lt::surface::KeyRepeatEvent &) { return false; },
[](const lt::surface::KeyReleasedEvent &) { return false; },
[](const lt::surface::KeySetCharEvent &) { return false; },
[](const lt::surface::MouseMovedEvent &) { return false; },
[](const lt::surface::WheelScrolledEvent &) { return false; },
[](const lt::surface::ButtonPressedEvent &) { return false; },
[](const lt::surface::ButtonReleasedEvent &) { return false; },
[](const auto &) { return false; },
};
return std::visit(visitor, event);
}
void setup_callbacks(GLFWwindow *handle);
std::array<bool, 512> m_keys {};
};
} // namespace lt::input

View file

@ -1,16 +1,11 @@
add_library_module(libmirror
layers/editor_layer.cpp
panels/asset_browser.cpp
panels/properties.cpp
panels/scene_hierarchy.cpp
)
target_link_libraries(
libmirror
PUBLIC app
PUBLIC opengl::opengl
PUBLIC ui
PUBLIC imgui
PUBLIC input
INTERFACE
app
opengl::opengl
surface
)
add_test_module(libmirror

View file

@ -1,30 +1,74 @@
#include <app/application.hpp>
#include <app/entrypoint.hpp>
#include <app/layer_stack.hpp>
#include <app/system.hpp>
#include <math/vec2.hpp>
#include <mirror/layers/editor_layer.hpp>
#include <window/window.hpp>
#include <surface/system.hpp>
namespace lt {
class Mirror: public Application
template<class... Ts>
struct overloads: Ts...
{
using Ts::operator()...;
};
class Mirror: public app::Application
{
public:
Mirror()
{
get_window().set_properties(
Window::Properties {
.title = "Mirror",
.size = math::uvec2(1280u, 720u),
.vsync = true,
}
);
m_editor_registry = create_ref<ecs::Registry>();
get_layer_stack().emplace_layer<EditorLayer>("MirrorLayer");
setup_window_system();
register_systems();
m_window_system->add_event_listener([&](const surface::System::Event &event) {
const auto visitor = overloads {
[&](const lt::surface::KeyPressedEvent &event) {
std::cout << "key pressed: " << event.to_string() << std::endl;
if (event.get_key() == 81)
{
unregister_system(m_window_system);
log_inf("Quitting...");
}
return true;
},
[](const auto &) { return false; },
};
return std::visit(visitor, event);
});
}
void setup_window_system()
{
using lt::surface::SurfaceComponent;
m_window_system = create_ref<lt::surface::System>(m_editor_registry);
m_window = m_editor_registry->create_entity("Editor Window");
m_window.add_component<SurfaceComponent>(SurfaceComponent::CreateInfo {
.title = "Editor Window",
.size = { 800u, 600u },
.vsync = true,
.visible = true,
});
}
void register_systems()
{
register_system(m_window_system);
}
private:
Ref<ecs::Registry> m_editor_registry;
Ref<lt::surface::System> m_window_system;
lt::ecs::Entity m_window;
};
auto create_application() -> Scope<Application>
auto app::create_application() -> Scope<app::Application>
{
return create_scope<Mirror>();
}

View file

@ -1,4 +1,5 @@
add_library_module(renderer
system.cpp
blender.cpp
buffers.cpp
framebuffer.cpp
@ -19,6 +20,7 @@ add_library_module(renderer
gl/shader.cpp
gl/texture.cpp
gl/vertex_layout.cpp
vk/instance.cpp
)
target_link_libraries(
@ -34,4 +36,15 @@ target_link_libraries(
PUBLIC yaml-cpp::yaml-cpp
PUBLIC EnTT::EnTT
PRIVATE lt_debug
PRIVATE window
PUBLIC vulkan
)
add_test_module(renderer
system.test.cpp
)
target_link_libraries(
renderer_tests
PRIVATE lt_debug
PRIVATE window
)

View file

@ -0,0 +1,18 @@
#include <lt_debug/assertions.hpp>
#include <renderer/system.hpp>
namespace lt::renderer {
System::System(InitRequirements requirements): m_registry(std::move(requirements.registry))
{
ensure(m_registry, "null registry");
ensure(requirements.glfw_window_handle, "null glfw handle");
}
System::~System() = default;
void System::tick(TickRequirements requirements)
{
}
} // namespace lt::renderer

View file

@ -0,0 +1,50 @@
#include <ranges>
#include <renderer/system.hpp>
#include <test/test.hpp>
#include <window/window.hpp>
using namespace lt;
using lt::test::Case;
using lt::test::Suite;
Suite raii = [] {
using lt::test::expect_true;
using lt::test::expect_throw;
using renderer::System;
auto *window = static_cast<GLFWwindow *>(lt::Window::create([](auto &&PH1) {})->get_handle());
Case { "happy" } = [=] {
std::ignore = System { {
.glfw_window_handle = window,
.registry = create_ref<ecs::Registry>(),
} };
};
Case { "unhappy" } = [=] {
expect_throw([=] {
std::ignore = System { {
.glfw_window_handle = window,
.registry = {},
} };
});
expect_throw([=] {
std::ignore = System { {
.glfw_window_handle = {},
.registry = create_ref<ecs::Registry>(),
} };
});
};
Case { "plenty" } = [=] {
for (auto idx : std::views::iota(0, 100'001))
{
std::ignore = System { {
.glfw_window_handle = window,
.registry = create_ref<ecs::Registry>(),
} };
}
};
};

View file

@ -0,0 +1,5 @@
#include <renderer/vk/instance.hpp>
namespace lt::vk {
}

View file

@ -0,0 +1,19 @@
#pragma once
#include <vulkan/vulkan.h>
#include <vulkan/vulkan.hpp>
namespace lt::vk {
class Instance
{
public:
Instance()
{
}
private:
::vk::Instance m_instace;
};
} // namespace lt::vk

View file

@ -0,0 +1,8 @@
#include <renderer/vk/instance.hpp>
#include <test/test.hpp>
lt::test::Suite raii = [] {
lt::test::Case { "raii" } = [] {
auto instance = lt::vk::Instance {};
};
};

View file

@ -0,0 +1,12 @@
#pragma once
namespace lt::renderer {
/** Requires a Transform Component
* @todo(Light): Figure out how to enforce a component requirement list for a component
*/
struct RendererComponent
{
};
} // namespace lt::renderer

View file

@ -0,0 +1,9 @@
#pragma once
namespace lt::renderer {
class Context
{
};
} // namespace lt::renderer

View file

@ -0,0 +1,60 @@
#pragma once
#include <base/base.hpp>
#include <ecs/scene.hpp>
struct GLFWwindow;
namespace lt::renderer {
/** The system for putting gore on your display
*
* Exclusively operates on components:
* - RendererComponent
* - PostEffectsComponent
* - UserInterfaceComponent
*
* Requires read acces on components:
* - TransformComponent
*/
class System
{
public:
/** The configurations of this system. */
struct Properties
{
};
/** The requirements for this system to initialize. */
struct InitRequirements
{
GLFWwindow *glfw_window_handle;
Ref<ecs::Registry> registry;
};
/** The requirements for this system to tick. */
struct TickRequirements
{
double delta_time;
};
[[nodiscard]] System(InitRequirements requirements);
System(System &&) = default;
System(const System &) = delete;
auto operator=(System &&) -> System & = default;
auto operator=(const System &) -> System & = delete;
~System();
void tick(TickRequirements requirements);
private:
Ref<ecs::Registry> m_registry;
};
} // namespace lt::renderer

View file

@ -0,0 +1,13 @@
if (NOT WIN32)
add_library_module(surface system.cpp linux/system.cpp)
else()
endif()
target_link_libraries(surface PUBLIC
ecs
app
PRIVATE
glfw
logger
lt_debug
)

View file

@ -0,0 +1,198 @@
#include <GLFW/glfw3.h>
#include <surface/system.hpp>
namespace lt::surface {
void handle_event(GLFWwindow *window, const System::Event &event)
{
auto &callbacks = *static_cast<std::vector<System::EventCallback> *>(
glfwGetWindowUserPointer(window)
);
for (auto &callback : callbacks)
{
if (callback(event))
{
return;
}
}
}
void bind_glfw_events(GLFWwindow *handle)
{
glfwSetWindowPosCallback(handle, [](GLFWwindow *window, int xpos, int ypos) {
handle_event(window, MovedEvent { xpos, ypos });
});
glfwSetWindowSizeCallback(handle, [](GLFWwindow *window, int width, int height) {
handle_event(
window,
ResizedEvent { static_cast<uint32_t>(width), static_cast<uint32_t>(height) }
);
});
glfwSetWindowCloseCallback(handle, [](GLFWwindow *window) {
handle_event(window, ClosedEvent {});
});
glfwSetWindowFocusCallback(handle, [](GLFWwindow *window, int focus) {
if (focus == GLFW_TRUE)
{
handle_event(window, GainFocusEvent {});
}
else
{
handle_event(window, LostFocusEvent {});
}
});
glfwSetCursorPosCallback(handle, [](GLFWwindow *window, double xpos, double ypos) {
handle_event(
window,
MouseMovedEvent { static_cast<float>(xpos), static_cast<float>(ypos) }
);
});
glfwSetMouseButtonCallback(
handle,
[](GLFWwindow *window, int button, int action, int /*mods*/) {
if (action == GLFW_PRESS)
{
handle_event(window, ButtonPressedEvent { button });
}
else if (action == GLFW_RELEASE)
{
handle_event(window, ButtonReleasedEvent { button });
}
}
);
glfwSetScrollCallback(handle, [](GLFWwindow *window, double /*xoffset*/, double yoffset) {
handle_event(window, WheelScrolledEvent { static_cast<float>(yoffset) });
});
glfwSetKeyCallback(
handle,
[](GLFWwindow *window, int key, int /*scancode*/, int action, int /*mods*/) {
if (action == GLFW_PRESS)
{
handle_event(window, KeyPressedEvent { key });
}
else if (action == GLFW_RELEASE)
{
handle_event(window, KeyReleasedEvent { key });
}
}
);
glfwSetCharCallback(handle, [](GLFWwindow *window, unsigned int character) {
handle_event(window, KeySetCharEvent { character });
});
}
void System::on_surface_construct(entt::registry &registry, entt::entity entity)
{
ensure(glfwInit(), "Failed to initialize 'glfw'");
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
auto &surface = registry.get<SurfaceComponent>(entity);
auto [width, height] = surface.get_size();
surface.m_glfw_handle = glfwCreateWindow(
static_cast<int32_t>(width),
static_cast<int32_t>(height),
surface.get_title().begin(),
nullptr,
nullptr
);
ensure(surface.m_glfw_handle, "Failed to create 'GLFWwindow'");
glfwSetWindowUserPointer(surface.m_glfw_handle, &m_event_callbacks);
bind_glfw_events(surface.m_glfw_handle);
}
void System::on_surface_destroy(entt::registry &registry, entt::entity entity)
{
auto &surface = registry.get<SurfaceComponent>(entity);
glfwDestroyWindow(surface.m_glfw_handle);
}
void System::set_title(ecs::Entity entity, std::string_view new_title)
{
auto &surface = entity.get_component<SurfaceComponent>();
surface.m_title = new_title;
glfwSetWindowTitle(surface.m_glfw_handle, surface.m_title.begin());
}
auto System::tick() -> bool
{
glfwPollEvents();
return false;
}
void System::set_size(ecs::Entity surface_entity, const math::uvec2 &new_size)
{
auto &surface = surface_entity.get_component<SurfaceComponent>();
surface.m_size = new_size;
glfwSetWindowSize(
surface.m_glfw_handle,
static_cast<int>(new_size.x),
static_cast<int>(new_size.y)
);
}
void System::set_v_sync(ecs::Entity surface_entity, bool vsync)
{
auto &surface = surface_entity.get_component<SurfaceComponent>();
surface.m_vsync = vsync;
glfwSwapInterval(vsync);
}
void System::set_visibility(ecs::Entity surface_entity, bool visible)
{
auto &surface = surface_entity.get_component<SurfaceComponent>();
surface.m_visible = visible;
if (visible)
{
glfwShowWindow(surface.m_glfw_handle);
}
else
{
glfwHideWindow(surface.m_glfw_handle);
}
}
} // namespace lt::surface
namespace lt {
// void System::on_event(const Event &event)
// {
// switch (event.get_event_type())
// {
// /* closed */
// case EventType::WindowClosed: b_Closed = true; break;
//
// /* resized */
// case EventType::WindowResized:
// on_surface_resize(dynamic_cast<const WindowResizedEvent &>(event));
// break;
//
// default: break;
// }
// }
//
// void System::on_surface_resize(const WindowResizedEvent &event)
// {
// m_properties.size = event.get_size();
// }
} // namespace lt

View file

@ -0,0 +1,27 @@
#include <surface/system.hpp>
namespace lt::surface {
System::System(Ref<ecs::Registry> registry): m_registry(std::move(registry))
{
m_registry->get_entt_registry()
.on_construct<SurfaceComponent>()
.connect<&System::on_surface_construct>(this);
m_registry->get_entt_registry()
.on_destroy<SurfaceComponent>()
.connect<&System::on_surface_destroy>(this);
}
System::~System()
{
m_registry->get_entt_registry()
.on_construct<SurfaceComponent>()
.disconnect<&System::on_surface_construct>(this);
m_registry->get_entt_registry()
.on_destroy<SurfaceComponent>()
.disconnect<&System::on_surface_destroy>(this);
}
} // namespace lt::surface

View file

View file

@ -0,0 +1,77 @@
#pragma once
#include <math/vec2.hpp>
struct GLFWwindow;
namespace lt::surface {
class SurfaceComponent
{
public:
friend class System;
struct CreateInfo
{
std::string_view title;
math::uvec2 size;
bool vsync;
bool visible;
};
SurfaceComponent(const CreateInfo &info)
: m_title(info.title)
, m_size(info.size)
, m_vsync(info.vsync)
, m_visible(info.visible)
{
}
[[nodiscard]] auto get_title() const -> const std::string_view &
{
return m_title;
}
[[nodiscard]] auto get_size() const -> const math::uvec2 &
{
return m_size;
}
[[nodiscard]] auto is_vsync() const -> bool
{
return m_vsync;
}
[[nodiscard]] auto is_visible() const -> bool
{
return m_visible;
}
[[nodiscard]] auto get_native_handle() const -> void *
{
return m_native_handle;
}
[[nodiscard]] auto get_glfw_handle() const -> GLFWwindow *
{
return m_glfw_handle;
}
private:
std::string_view m_title;
math::uvec2 m_size;
bool m_vsync;
bool m_visible;
void *m_native_handle {};
GLFWwindow *m_glfw_handle {};
};
} // namespace lt::surface

View file

@ -0,0 +1,91 @@
#pragma once
#include <format>
namespace lt::surface {
class KeyPressedEvent
{
public:
KeyPressedEvent(int32_t key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> int32_t
{
return m_key;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("KeyPressed: {}", m_key);
}
private:
int32_t m_key;
};
class KeyRepeatEvent
{
public:
KeyRepeatEvent(int key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> int32_t
{
return m_key;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("KeyRepeated: {}", m_key);
}
private:
int32_t m_key;
};
class KeyReleasedEvent
{
public:
KeyReleasedEvent(int key): m_key(key)
{
}
[[nodiscard]] auto get_key() const -> int32_t
{
return m_key;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("KeyReleased: {}", m_key);
}
private:
int32_t m_key;
};
class KeySetCharEvent
{
public:
KeySetCharEvent(uint32_t character): m_character(character)
{
}
[[nodiscard]] auto get_character() const -> uint32_t
{
return m_character;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("KeyCharSet: {}", m_character);
}
private:
uint32_t m_character;
};
} // namespace lt::surface

View file

@ -0,0 +1,103 @@
#pragma once
#include <math/vec2.hpp>
namespace lt::surface {
class MouseMovedEvent
{
public:
MouseMovedEvent(float x, float y): m_position(x, y)
{
}
[[nodiscard]] auto get_position() const -> const math::vec2 &
{
return m_position;
}
[[nodiscard]] auto get_x() const -> float
{
return m_position.x;
}
[[nodiscard]] auto get_y() const -> float
{
return m_position.y;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("MouseMoved: {}, {}", m_position.x, m_position.y);
}
private:
math::vec2 m_position;
};
class WheelScrolledEvent
{
public:
WheelScrolledEvent(float offset): m_offset(offset)
{
}
[[nodiscard]] auto get_offset() const -> float
{
return m_offset;
}
[[nodiscard]] auto to_string() const -> std::string
{
std::stringstream ss;
ss << "WheelScrolled: " << m_offset;
return ss.str();
}
private:
float m_offset;
};
class ButtonPressedEvent
{
public:
ButtonPressedEvent(int button): m_button(button)
{
}
[[nodiscard]] auto get_button() const -> int
{
return m_button;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("ButtonPressed: {}", m_button);
}
private:
int m_button;
};
class ButtonReleasedEvent
{
public:
ButtonReleasedEvent(int button): m_button(button)
{
}
[[nodiscard]] auto get_button() const -> int
{
return m_button;
}
[[nodiscard]] auto to_string() const -> std::string
{
return std::format("ButtonReleased: {}", m_button);
}
private:
int m_button;
};
} // namespace lt::surface

View file

@ -0,0 +1,81 @@
#pragma once
#include <math/vec2.hpp>
#include <sstream>
namespace lt::surface {
class ClosedEvent
{
public:
[[nodiscard]] auto to_string() const -> std::string_view
{
return "SurfaceClosedEvent";
}
};
class MovedEvent
{
public:
MovedEvent(int x, int y): m_position(x, y)
{
}
[[nodiscard]] auto get_position() const -> const math::ivec2 &
{
return m_position;
}
[[nodiscard]] auto to_string() const -> std::string
{
auto stream = std::stringstream {};
stream << "WindwoMoved: " << m_position.x << ", " << m_position.y;
return stream.str();
}
private:
math::ivec2 m_position;
};
class ResizedEvent
{
public:
ResizedEvent(unsigned int width, unsigned int height): m_size(width, height)
{
}
[[nodiscard]] auto get_size() const -> const math::uvec2 &
{
return m_size;
}
[[nodiscard]] auto to_string() const -> std::string
{
auto stream = std::stringstream {};
stream << "SurfaceResized: " << m_size.x << ", " << m_size.y;
return stream.str();
}
private:
math::uvec2 m_size;
};
class LostFocusEvent
{
public:
[[nodiscard]] auto to_string() const -> std::string_view
{
return "SurfaceLostFocus";
}
};
class GainFocusEvent
{
public:
[[nodiscard]] auto to_string() const -> std::string_view
{
return "SurfaceGainFocus";
}
};
} // namespace lt::surface

View file

@ -0,0 +1,80 @@
#pragma once
#include <app/system.hpp>
#include <ecs/entity.hpp>
#include <ecs/scene.hpp>
#include <surface/components.hpp>
#include <surface/events/keyboard.hpp>
#include <surface/events/mouse.hpp>
#include <surface/events/surface.hpp>
namespace lt::surface {
class System: public app::ISystem
{
public:
using Event = std::variant<
// surface events
ClosedEvent,
MovedEvent,
ResizedEvent,
LostFocusEvent,
GainFocusEvent,
// keyboard events
KeyPressedEvent,
KeyRepeatEvent,
KeyReleasedEvent,
KeySetCharEvent,
// mouse events
MouseMovedEvent,
WheelScrolledEvent,
ButtonPressedEvent,
ButtonReleasedEvent>;
using EventCallback = std::function<bool(const Event &)>;
System(Ref<ecs::Registry> registry);
~System() override;
System(System &&) = default;
System(const System &) = delete;
auto operator=(System &&) -> System & = default;
auto operator=(const System &) -> System & = delete;
void init() override
{
}
auto tick() -> bool override;
void set_title(ecs::Entity surface_entity, std::string_view new_title);
void set_size(ecs::Entity surface_entity, const math::uvec2 &new_size);
void set_v_sync(ecs::Entity surface_entity, bool vsync);
void set_visibility(ecs::Entity surface_entity, bool visible);
void add_event_listener(EventCallback callback)
{
m_event_callbacks.emplace_back(std::move(callback));
}
private:
void on_surface_construct(entt::registry &registry, entt::entity entity);
void on_surface_destroy(entt::registry &registry, entt::entity entity);
Ref<ecs::Registry> m_registry;
std::vector<EventCallback> m_event_callbacks;
};
} // namespace lt::surface

View file

@ -1,13 +0,0 @@
if (NOT WIN32)
add_library_module(window linux/window.cpp)
else()
add_library_module(window windows/window.cpp)
endif()
target_link_libraries(window PUBLIC
PRIVATE
glfw
logger
lt_debug
input
)

View file

@ -1,219 +0,0 @@
#include <GLFW/glfw3.h>
#include <input/events/char.hpp>
#include <input/events/event.hpp>
#include <input/events/keyboard.hpp>
#include <input/events/mouse.hpp>
#include <input/events/window.hpp>
#include <window/linux/window.hpp>
namespace lt {
Window::~Window() = default;
auto Window::create(const std::function<void(Event &)> &callback) -> Scope<Window>
{
return create_scope<lWindow>(callback);
}
lWindow::lWindow(std::function<void(Event &)> callback)
: m_event_callback(std::move(std::move(callback)))
{
ensure(glfwInit(), "Failed to initialize 'glfw'");
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
m_handle = glfwCreateWindow(1u, 1u, "", nullptr, nullptr);
ensure(m_handle, "Failed to create 'GLFWwindow'");
glfwSetWindowUserPointer(m_handle, &m_event_callback);
bind_glfw_events();
}
lWindow::~lWindow()
{
glfwDestroyWindow(m_handle);
}
void lWindow::poll_events()
{
glfwPollEvents();
}
void lWindow::on_event(const Event &event)
{
switch (event.get_event_type())
{
/* closed */
case EventType::WindowClosed: b_Closed = true; break;
/* resized */
case EventType::WindowResized:
on_window_resize(dynamic_cast<const WindowResizedEvent &>(event));
break;
default: break;
}
}
void lWindow::on_window_resize(const WindowResizedEvent &event)
{
m_properties.size = event.get_size();
}
void lWindow::set_properties(const Properties &properties, bool overrideVisibility /* = false */)
{
// save the visibility status and re-assign if 'overrideVisibility' is false
auto visible = overrideVisibility ? properties.visible : m_properties.visible;
m_properties = properties;
m_properties.visible = visible;
// set properties
set_title(properties.title);
set_size(properties.size);
set_v_sync(properties.vsync);
set_visibility(visible);
}
void lWindow::set_title(const std::string &title)
{
m_properties.title = title;
glfwSetWindowTitle(m_handle, title.c_str());
}
void lWindow::set_size(const math::uvec2 &size)
{
m_properties.size = size;
glfwSetWindowSize(m_handle, static_cast<int>(size.x), static_cast<int>(size.y));
}
void lWindow::set_v_sync(bool vsync, bool toggle /* = false */)
{
m_properties.vsync = toggle ? !m_properties.vsync : vsync;
glfwSwapInterval(m_properties.vsync);
}
void lWindow::set_visibility(bool visible, bool toggle)
{
m_properties.visible = toggle ? !m_properties.visible : visible;
if (m_properties.visible)
{
glfwShowWindow(m_handle);
}
else
{
glfwHideWindow(m_handle);
}
}
void lWindow::bind_glfw_events()
{
glfwSetCursorPosCallback(m_handle, [](GLFWwindow *window, double xpos, double ypos) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
auto event = MouseMovedEvent {
static_cast<float>(xpos),
static_cast<float>(ypos),
};
callback(event);
});
glfwSetMouseButtonCallback(
m_handle,
[](GLFWwindow *window, int button, int action, int /*mods*/) {
std::function<void(Event &)> const callback = *(
std::function<void(Event &)> *
)glfwGetWindowUserPointer(window);
if (action == GLFW_PRESS)
{
auto event = ButtonPressedEvent { button };
callback(event);
}
else if (action == GLFW_RELEASE)
{
auto event = ButtonReleasedEvent { button };
callback(event);
}
}
);
glfwSetScrollCallback(m_handle, [](GLFWwindow *window, double /*xoffset*/, double yoffset) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
auto event = WheelScrolledEvent { static_cast<float>(yoffset) };
callback(event);
});
glfwSetKeyCallback(
m_handle,
[](GLFWwindow *window, int key, int /*scancode*/, int action, int /*mods*/) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
if (action == GLFW_PRESS)
{
auto event = KeyPressedEvent { key };
callback(event);
}
else if (action == GLFW_RELEASE)
{
auto event = KeyReleasedEvent { key };
callback(event);
}
}
);
glfwSetCharCallback(m_handle, [](GLFWwindow *window, unsigned int character) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
auto event = SetCharEvent { character };
callback(event);
});
glfwSetWindowPosCallback(m_handle, [](GLFWwindow *window, int xpos, int ypos) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
auto event = WindowMovedEvent { xpos, ypos };
callback(event);
});
glfwSetWindowSizeCallback(m_handle, [](GLFWwindow *window, int width, int height) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
auto event = WindowResizedEvent {
static_cast<unsigned int>(width),
static_cast<unsigned int>(height),
};
callback(event);
});
glfwSetWindowCloseCallback(m_handle, [](GLFWwindow *window) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
auto event = WindowClosedEvent {};
callback(event);
});
glfwSetWindowFocusCallback(m_handle, [](GLFWwindow *window, int focus) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
if (focus == GLFW_TRUE)
{
auto event = WindowGainFocusEvent {};
callback(event);
}
else
{
auto event = WindowLostFocusEvent {};
callback(event);
}
});
}
} // namespace lt

View file

@ -1,60 +0,0 @@
#pragma once
#include <window/window.hpp>
struct GLFWwindow;
namespace lt {
class Event;
class WindowResizedEvent;
class lWindow: public Window
{
public:
lWindow(std::function<void(Event &)> callback);
~lWindow() override;
lWindow(lWindow &&) = delete;
lWindow(const lWindow &) = delete;
auto operator=(lWindow &&) -> lWindow & = delete;
auto operator=(const lWindow &) -> lWindow & = delete;
void poll_events() override;
void on_event(const Event &event) override;
void set_properties(const Properties &properties, bool overrideVisibility = false) override;
void set_title(const std::string &title) override;
void set_size(const math::uvec2 &size) override;
void set_v_sync(bool vsync, bool toggle = false) override;
void set_visibility(bool visible, bool toggle = false) override;
[[nodiscard]] auto get_handle() -> void * override
{
return m_handle;
}
private:
void on_window_resize(const WindowResizedEvent &event);
void bind_glfw_events();
GLFWwindow *m_handle { nullptr };
std::function<void(Event &)> m_event_callback;
Properties m_properties {};
bool b_Closed {};
};
} // namespace lt

View file

@ -1,219 +0,0 @@
#include <GLFW/glfw3.h>
#include <input/events/char.hpp>
#include <input/events/event.hpp>
#include <input/events/keyboard.hpp>
#include <input/events/mouse.hpp>
#include <input/events/window.hpp>
#include <window/windows/window.hpp>
namespace lt {
Window::~Window() = default;
auto Window::create(const std::function<void(Event &)> &callback) -> Scope<Window>
{
return create_scope<wWindow>(callback);
}
wWindow::wWindow(std::function<void(Event &)> callback)
: m_event_callback(std::move(std::move(callback)))
{
ensure(glfwInit(), "Failed to initialize 'glfw'");
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
m_handle = glfwCreateWindow(1u, 1u, "", nullptr, nullptr);
ensure(m_handle, "Failed to create 'GLFWwindow'");
glfwSetWindowUserPointer(m_handle, &m_event_callback);
bind_glfw_events();
}
wWindow::~wWindow()
{
glfwDestroyWindow(m_handle);
}
void wWindow::poll_events()
{
glfwPollEvents();
}
void wWindow::on_event(const Event &event)
{
switch (event.get_event_type())
{
/* closed */
case EventType::WindowClosed: b_Closed = true; break;
/* resized */
case EventType::WindowResized:
on_window_resize(dynamic_cast<const WindowResizedEvent &>(event));
break;
default: break;
}
}
void wWindow::on_window_resize(const WindowResizedEvent &event)
{
m_properties.size = event.get_size();
}
void wWindow::set_properties(const Properties &properties, bool overrideVisibility /* = false */)
{
// save the visibility status and re-assign if 'overrideVisibility' is false
auto visible = overrideVisibility ? properties.visible : m_properties.visible;
m_properties = properties;
m_properties.visible = visible;
// set properties
set_title(properties.title);
set_size(properties.size);
set_v_sync(properties.vsync);
set_visibility(visible);
}
void wWindow::set_title(const std::string &title)
{
m_properties.title = title;
glfwSetWindowTitle(m_handle, title.c_str());
}
void wWindow::set_size(const math::uvec2 &size)
{
m_properties.size = size;
glfwSetWindowSize(m_handle, static_cast<int>(size.x), static_cast<int>(size.y));
}
void wWindow::set_v_sync(bool vsync, bool toggle /* = false */)
{
m_properties.vsync = toggle ? !m_properties.vsync : vsync;
glfwSwapInterval(m_properties.vsync);
}
void wWindow::set_visibility(bool visible, bool toggle)
{
m_properties.visible = toggle ? !m_properties.visible : visible;
if (m_properties.visible)
{
glfwShowWindow(m_handle);
}
else
{
glfwHideWindow(m_handle);
}
}
void wWindow::bind_glfw_events()
{
glfwSetCursorPosCallback(m_handle, [](GLFWwindow *window, double xpos, double ypos) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
auto event = MouseMovedEvent {
static_cast<float>(xpos),
static_cast<float>(ypos),
};
callback(event);
});
glfwSetMouseButtonCallback(
m_handle,
[](GLFWwindow *window, int button, int action, int /*mods*/) {
std::function<void(Event &)> const callback = *(
std::function<void(Event &)> *
)glfwGetWindowUserPointer(window);
if (action == GLFW_PRESS)
{
auto event = ButtonPressedEvent { button };
callback(event);
}
else if (action == GLFW_RELEASE)
{
auto event = ButtonReleasedEvent { button };
callback(event);
}
}
);
glfwSetScrollCallback(m_handle, [](GLFWwindow *window, double /*xoffset*/, double yoffset) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
auto event = WheelScrolledEvent { static_cast<float>(yoffset) };
callback(event);
});
glfwSetKeyCallback(
m_handle,
[](GLFWwindow *window, int key, int /*scancode*/, int action, int /*mods*/) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
if (action == GLFW_PRESS)
{
auto event = KeyPressedEvent { key };
callback(event);
}
else if (action == GLFW_RELEASE)
{
auto event = KeyReleasedEvent { key };
callback(event);
}
}
);
glfwSetCharCallback(m_handle, [](GLFWwindow *window, unsigned int character) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
auto event = SetCharEvent { character };
callback(event);
});
glfwSetWindowPosCallback(m_handle, [](GLFWwindow *window, int xpos, int ypos) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
auto event = WindowMovedEvent { xpos, ypos };
callback(event);
});
glfwSetWindowSizeCallback(m_handle, [](GLFWwindow *window, int width, int height) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
auto event = WindowResizedEvent {
static_cast<unsigned int>(width),
static_cast<unsigned int>(height),
};
callback(event);
});
glfwSetWindowCloseCallback(m_handle, [](GLFWwindow *window) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
auto event = WindowClosedEvent {};
callback(event);
});
glfwSetWindowFocusCallback(m_handle, [](GLFWwindow *window, int focus) {
auto callback = *(std::function<void(Event &)> *)glfwGetWindowUserPointer(window);
if (focus == GLFW_TRUE)
{
auto event = WindowGainFocusEvent {};
callback(event);
}
else
{
auto event = WindowLostFocusEvent {};
callback(event);
}
});
}
} // namespace lt

View file

@ -1,60 +0,0 @@
#pragma once
#include <window/window.hpp>
struct GLFWwindow;
namespace lt {
class Event;
class WindowResizedEvent;
class wWindow: public Window
{
public:
wWindow(std::function<void(Event &)> callback);
~wWindow() override;
wWindow(wWindow &&) = delete;
wWindow(const wWindow &) = delete;
auto operator=(wWindow &&) -> wWindow & = delete;
auto operator=(const wWindow &) -> wWindow & = delete;
void poll_events() override;
void on_event(const Event &event) override;
void set_properties(const Properties &properties, bool overrideVisibility = false) override;
void set_title(const std::string &title) override;
void set_size(const math::uvec2 &size) override;
void set_v_sync(bool vsync, bool toggle = false) override;
void set_visibility(bool visible, bool toggle = false) override;
[[nodiscard]] auto get_handle() -> void * override
{
return m_handle;
}
private:
void on_window_resize(const WindowResizedEvent &event);
void bind_glfw_events();
GLFWwindow *m_handle { nullptr };
std::function<void(Event &)> m_event_callback;
Properties m_properties {};
bool b_Closed {};
};
} // namespace lt

View file

@ -1,98 +0,0 @@
#pragma once
#include <math/vec2.hpp>
namespace lt {
class Event;
class Window
{
public:
struct Properties
{
std::string title;
math::uvec2 size;
bool vsync, visible;
};
static Scope<Window> create(const std::function<void(Event &)> &callback);
Window() = default;
virtual ~Window();
Window(Window &&) = delete;
Window(const Window &) = delete;
auto operator=(Window &&) -> Window & = delete;
auto operator=(const Window &) -> Window & = delete;
virtual void poll_events() = 0;
virtual void on_event(const Event &event) = 0;
virtual void set_properties(const Properties &properties, bool affectVisibility = false) = 0;
virtual void set_title(const std::string &title) = 0;
virtual void set_size(const math::uvec2 &size) = 0;
void close()
{
b_Closed = true;
}
virtual void set_v_sync(bool vsync, bool toggle = false) = 0;
virtual void set_visibility(bool visible, bool toggle = false) = 0;
[[nodiscard]] auto get_properties() const -> const Properties &
{
return m_properties;
}
[[nodiscard]] auto get_title() const -> const std::string &
{
return m_properties.title;
}
[[nodiscard]] auto get_size() const -> const math::uvec2 &
{
return m_properties.size;
}
[[nodiscard]] auto is_closed() const -> bool
{
return b_Closed;
}
[[nodiscard]] auto is_v_sync() const -> bool
{
return m_properties.vsync;
}
[[nodiscard]] auto is_visible() const -> bool
{
return m_properties.visible;
}
virtual auto get_handle() -> void * = 0;
protected:
auto get_properties_handle() -> Properties &
{
return m_properties;
}
private:
Properties m_properties {};
bool b_Closed {};
};
} // namespace lt