Compare commits

...

47 Commits

Author SHA1 Message Date
7fe522ffbe new minor change to test new https subdomain 2026-04-19 20:30:15 +02:00
4cd1528343 fix: getmouse pos uses the member vars correctly and removed const 2026-04-17 11:54:42 +02:00
31f57eddf7 fix: window remove flags function used | to remove flag instead of & 2026-04-17 11:40:56 +02:00
e9808fb47f fix: sdl render alpha was set 0 changed to 255 to be transparent 2026-04-17 11:39:48 +02:00
fa05feceb8 fixed: uninitalized m_Flags in window made the application window not appear 2026-04-16 15:42:08 +02:00
4b89548102 changed the type of the windowflags in windowdata struct and window create function argument 2026-04-16 13:48:34 +02:00
99d508c5ed added windowFlag to WindowData so client can specify the first window's flags when application is created 2026-04-15 10:35:22 +02:00
fb9e93d9cf changed m_Flags from pointer to stack var 2026-04-15 10:28:11 +02:00
e64fc16d6a removeflags function for window class 2026-04-15 10:19:24 +02:00
73db13dcc2 addflags function for windows 2026-04-15 10:16:05 +02:00
ec5d5af43e added member var for the window flags to the window class 2026-04-15 10:15:08 +02:00
ccbc79d496 fixed: the application crashes when the user used the x button or key combination to close the window
the crash occured because running state was set false but the
application loop was still running during that frame and rendered to the
now closed window, because the check only happened at the start of the
next frame
2026-04-14 18:03:36 +02:00
7ff93e58ea fixed: handling window creation failure correctly 2026-04-10 16:00:41 +02:00
e57149e9db removed not needed includes and added the include for the event 2026-04-10 15:49:09 +02:00
796769c6a4 window flag add and remove function definition 2026-04-10 15:48:43 +02:00
daefc73de7 window create now accepts flags as parameter 2026-04-10 15:48:11 +02:00
d28dbc1083 updated all submodules to newest version 2026-04-10 11:26:49 +02:00
7e5317fdc0 Revert "deleted getlayers function"
This reverts commit 0dbb8bc642.
2026-04-10 10:56:48 +02:00
0dbb8bc642 deleted getlayers function 2026-04-10 10:54:23 +02:00
96c19efaea changed default constructor and initialized vectors with size to avoid early copying 2026-04-10 10:54:14 +02:00
b964c2257b deleted commented out old code and todo comment 2026-04-10 10:53:22 +02:00
e46225eda9 added getlayerstack function to application 2026-04-10 10:52:26 +02:00
5dd3cc1b73 feat: queuing layer commands such as pop, push and transition at the end of each frame 2026-04-09 22:07:10 +02:00
b98f46f425 removed todo comment from window class 2026-04-09 22:04:31 +02:00
60f6c9281b using sdl main 2026-04-06 15:37:56 +02:00
fd319405e5 added onrender function to use sdl render in client, enabled multiviewports in imgui 2026-04-06 15:06:14 +02:00
5b40377d04 bug fixed: imgui rendered inside the event loop
the closing bracket for the event loop was misplaced and the imgui
render got inside the event loop
2026-04-06 10:24:56 +02:00
9974471b66 new event and window abstraction, preparing for multiple windows 2026-04-05 17:41:58 +02:00
3925ccc520 layer transition with temporary testing code until the event system is implemented 2026-04-04 11:38:31 +02:00
8b91c7ab76 sdl high dpi flag 2026-04-03 16:26:46 +02:00
d58ddf6c35 refactored imgui layer into nonlayer version
it returned to being a member of application with new init and shutdown
function which is ran by the con- and destructors, and init is called at
the end of the app init, with this it inits before any layer and layers
can use the onimguirender to draw with imgui
2026-04-03 15:44:43 +02:00
da3ad2ed61 get layer and filter with name, reworked layer system with template 2026-04-02 15:27:13 +02:00
53ef776d4c reworked application push layer and push overlay for a template system part of the new layer system 2026-04-02 13:01:25 +02:00
abe57222a8 new layer function definitions for the new layer system 2026-04-02 13:00:30 +02:00
5f2dc0d66e new library versions 2026-04-02 11:50:57 +02:00
5613e3824a new imgui version 2026-04-02 11:35:25 +02:00
5aa1e39cdd changed layer stack vector to contain uniqe_ptr of layers 2026-04-02 11:23:36 +02:00
fe265c6994 is active toggle on layer
this will be used to turn off a layer but we do not want to delete them
from the layer stack
2026-04-01 18:24:46 +02:00
fb06a38c83 reworked app init before major layer rework 2026-04-01 18:23:51 +02:00
cc3f592c67 removed temporary imgui window for checking if delta time was calculated properly 2026-04-01 18:22:52 +02:00
a9d3910ab3 removed comments for self explaining code 2026-04-01 18:21:57 +02:00
ea1d6252ba deleted not needed include 2026-04-01 18:19:16 +02:00
51cfab26f2 calculating delta time 2026-03-17 16:38:14 +01:00
a6ccb37b10 break!: switched to imgui docking branch 2026-03-17 16:37:15 +01:00
b05badcf90 implemented new layer function imguirender 2026-03-17 11:21:25 +01:00
9b64e0b065 finished imguilayer end function 2026-03-17 11:20:30 +01:00
fed67c7baa removed onstart onend since they are redundant with attach and detach 2026-03-17 11:19:40 +01:00
20 changed files with 779 additions and 152 deletions

View File

@@ -2,17 +2,23 @@
#include "Event.h"
#include "Layer.h"
#include "Log.h"
#include "SDL3/SDL_main.h"
#include "SDL3/SDL_events.h"
#include "SDL3/SDL_hints.h"
#include "SDL3/SDL_init.h"
#include "SDL3/SDL_render.h"
#include "SDL3/SDL_stdinc.h"
#include "SDL3/SDL_timer.h"
#include "SDL3/SDL_video.h"
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_sdlrenderer3.h"
#define SDL_MAIN_HANDLED 1
#include "imguiinit.h"
#include <memory>
#include <ranges>
namespace SakuraVNE {
Application *Application::s_Instance = nullptr;
Application::Application() : m_Window(nullptr), m_Renderer(nullptr), m_Surface(nullptr), m_isRunning(false) { SDL_SetMainReady(); }
Application::Application(const AppData &appdata) : m_Window(), m_Renderer(nullptr), m_Surface(nullptr), m_isRunning(false), m_AppData(appdata) { m_initResult = Init(); }
Application::~Application() { Shutdown(); }
bool Application::Init() {
@@ -23,7 +29,7 @@ bool Application::Init() {
SakuraVNE::Log::Init();
LOG_INFO("Initialized logger library");
LOG_INFO("window width: {0}, height: {1}", GetWindowData().width, GetWindowData().height);
LOG_INFO("window width: {0}, height: {1}", GetAppData().windowdata.width, GetAppData().windowdata.height);
// Init sdl
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) == 0) {
@@ -32,34 +38,25 @@ bool Application::Init() {
return false;
}
SDL_WindowFlags windowFlags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE);
m_Window = SDL_CreateWindow(GetWindowData().title, GetWindowData().width, GetWindowData().height, windowFlags);
if (!m_Window) {
LOG_ERROR("SDL window could not be created! {0}", SDL_GetError());
Shutdown();
return false;
} else {
LOG_INFO("SDl window created");
}
if (GetWindowData().pos_x != -1 && GetWindowData().pos_y != -1) {
if (!SDL_SetWindowPosition(m_Window, GetWindowData().pos_x, GetWindowData().pos_y)) {
LOG_ERROR("Failed to set SDL_Window position {0}", SDL_GetError());
} else {
LOG_INFO("SDL window position set to the initial value: x {0}, y {1}", GetWindowData().pos_x, GetWindowData().pos_y);
}
} else {
LOG_WARN("SDL window position not set. Will not attempt to set window position.");
}
LOG_INFO("Available renderer drivers:");
for (int i = 0; i < SDL_GetNumRenderDrivers(); i++) {
LOG_INFO("{0}. {1}", i + 1, SDL_GetRenderDriver(i));
}
m_Renderer = SDL_CreateRenderer(m_Window, nullptr);
// TODO: check here if m_Window is not null otherwise shutdown procedure
if (m_AppData.windowdata.title.empty()) {
m_AppData.windowdata.title = m_AppData.name;
}
m_AppData.windowdata.eventCallback = [this](Event &event) { RaiseEvent(event); };
m_Window.push_back(std::make_shared<Window>(m_AppData.windowdata));
bool isSuccessful = m_Window[0]->Create({m_AppData.windowdata.windowFlags});
if (!isSuccessful) {
Shutdown();
return false;
}
m_Renderer = SDL_CreateRenderer(GetSDLWindow(), nullptr);
if (!m_Renderer) {
LOG_ERROR("Renderer could not be created! {0}", SDL_GetError());
} else {
@@ -67,25 +64,34 @@ bool Application::Init() {
LOG_INFO("Renderer: {0}", SDL_GetRendererName(m_Renderer));
}
SDL_SetRenderVSync(m_Renderer, 1);
SDL_SetRenderVSync(m_Renderer, m_AppData.windowdata.isVsync);
m_ImGuiLayer = new SakuraVNE::ImGuiLayer();
PushOverlay(m_ImGuiLayer);
m_ImGui = new SakuraVNE::ImGuiInit();
return true;
}
void Application::Run() {
if (!m_initResult) {
return;
}
ImGuiIO &io = ImGui::GetIO();
Uint64 oldTime = SDL_GetTicks();
while (GetRunningState()) {
float time = 0;
Uint64 currentTime = SDL_GetTicks();
Uint64 time = currentTime - oldTime;
oldTime = currentTime;
for (auto layer : m_LayerStack) {
// Update functions before rendereing
for (auto &layer : m_LayerStack) {
layer->OnFrame(time);
}
// Events
SDL_Event event;
while (SDL_PollEvent(&event)) {
@@ -96,24 +102,69 @@ void Application::Run() {
LOG_INFO("Running state: {0}", GetRunningState());
}
if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(GetSDLWindow())) {
SetRunningState(false);
SDL_WindowID targetWindowID = 0;
if (event.type >= SDL_EVENT_WINDOW_FIRST && event.type <= SDL_EVENT_WINDOW_LAST) {
targetWindowID = event.window.windowID;
} else if (event.type >= SDL_EVENT_KEY_DOWN && event.type <= SDL_EVENT_KEY_UP) {
targetWindowID = event.key.windowID;
} else if (event.type >= SDL_EVENT_MOUSE_MOTION && event.type <= SDL_EVENT_MOUSE_WHEEL) {
targetWindowID = event.motion.windowID;
}
if (event.type == SDL_EVENT_WINDOW_RESIZED) {
SDL_GetWindowSize(GetSDLWindow(), &GetWindowData().width, &GetWindowData().height);
if (targetWindowID != 0) {
for (auto it = m_Window.begin(); it != m_Window.end();) {
SetSDLWindowSurface(SDL_GetWindowSurface(GetSDLWindow()));
if (SDL_GetWindowID((*it)->GetHandle()) == targetWindowID) {
(*it)->ProcessEvent(event);
if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
it = m_Window.erase(it);
if (m_Window.empty()) {
SetRunningState(false);
}
} else {
++it;
}
break;
} else {
++it;
}
}
}
}
//
if (!m_isRunning) {
break;
}
// Rendering
ImGui::Render();
for (auto &layer : m_LayerStack) {
layer->OnRender();
}
m_ImGui->Begin();
SDL_SetRenderScale(m_Renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
SDL_SetRenderDrawColor(m_Renderer, (Uint8)255, (Uint8)255, (Uint8)255, (Uint8)255);
SDL_SetRenderDrawColor(m_Renderer, (Uint8)111, (Uint8)232, (Uint8)168, (Uint8)255);
SDL_RenderClear(m_Renderer);
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), m_Renderer);
for (auto &layer : m_LayerStack) {
layer->OnImGuiRender();
}
m_ImGui->End();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
SDL_RenderPresent(m_Renderer);
// handle layercommands here after each frame
m_LayerStack.ProcessCommands();
}
}
@@ -122,18 +173,19 @@ void Application::Shutdown() {
SDL_DestroyRenderer(m_Renderer);
// Destroy window
SDL_DestroyWindow(m_Window);
m_Window.clear();
// Quit SDL subsystems
SDL_Quit();
s_Instance = nullptr;
}
void Application::PushLayer(SakuraVNE::Layer *layer) {
m_LayerStack.PushLayer(layer);
layer->OnAttach();
}
void Application::PushOverlay(SakuraVNE::Layer *layer) {
m_LayerStack.PushOverLay(layer);
layer->OnAttach();
void Application::RaiseEvent(Event &event) {
for (auto &layer : std::views::reverse(m_LayerStack)) {
layer->OnEvent(event);
if (event.Handled) {
break;
}
}
}
} // namespace SakuraVNE

View File

@@ -1,54 +1,106 @@
#pragma once
#include "Event.h"
#include "Layer.h"
#include "LayerStack.h"
#include "SDL3/SDL.h"
#include "imguilayer.h"
#include "SDL3/SDL_render.h"
#include "SDL3/SDL_video.h"
#include "Window.h"
#include "imguiinit.h"
#include <memory>
#include <type_traits>
#include <vector>
// TODO: Move this out to its own class(?) so it can be defined by the application
// maybe this could be used as a fallback if the app did not define one
struct WindowData {
int width = 1280;
int height = 720;
int pos_x = -1;
int pos_y = -1;
const char *title = "Sakura Visual Novel Engine";
namespace SakuraVNE {
struct AppData {
std::string name = "Application";
WindowData windowdata;
};
class Application {
public:
Application();
Application(const AppData &appdata = AppData());
~Application();
bool Init();
void Run();
void Shutdown();
void RaiseEvent(Event &e);
void PushLayer(SakuraVNE::Layer *);
void PushOverlay(SakuraVNE::Layer *);
template <typename TLayer>
requires(std::is_base_of_v<Layer, TLayer>)
void PushLayer() {
auto newLayer = std::make_unique<TLayer>();
Layer *layer = newLayer.get();
inline WindowData &GetWindowData() { return m_WindowData; }
inline SDL_Window *GetSDLWindow() { return m_Window; }
m_LayerStack.PushLayer(std::move(newLayer));
layer->OnAttach();
}
template <typename TLayer>
requires(std::is_base_of_v<Layer, TLayer>)
TLayer *GetLayer() {
for (const auto &layer : m_LayerStack) {
if (auto casted = dynamic_cast<TLayer *>(layer.get())) {
return casted;
}
}
return nullptr;
}
template <typename TLayer>
requires(std::is_base_of_v<Layer, TLayer>)
TLayer *GetLayer(const std::string &layerName) {
for (const auto &layer : m_LayerStack) {
if (auto casted = dynamic_cast<TLayer *>(layer.get()) && layer->GetName() == layerName) {
return casted;
}
}
return nullptr;
}
template <typename TLayer>
requires(std::is_base_of_v<Layer, TLayer>)
void PushOverlay() {
auto newOverlay = std::make_unique<TLayer>();
Layer *layer = newOverlay.get();
m_LayerStack.PushOverLay(std::move(newOverlay));
layer->OnAttach();
}
inline AppData &GetAppData() { return m_AppData; }
inline SDL_Window *GetSDLWindow(const int index = 0) { return m_Window[index]->GetHandle(); }
inline SDL_Renderer *GetSDLRenderer() { return m_Renderer; }
inline SDL_Surface *GetSDLWindowSurface() { return m_Surface; }
inline void SetSDLWindowSurface(SDL_Surface *newSurface) { m_Surface = newSurface; }
std::shared_ptr<Window> GetWindow(const int index = 0) const { return m_Window[index]; }
bool &GetRunningState() { return m_isRunning; }
void SetRunningState(bool isRunning) { m_isRunning = isRunning; }
static Application &Get() { return *s_Instance; }
inline LayerStack &GetLayerStack() { return m_LayerStack; }
private:
SDL_Window *m_Window;
bool m_isRunning;
bool m_initResult;
SDL_Surface *m_Surface;
SDL_Renderer *m_Renderer;
WindowData m_WindowData;
AppData m_AppData;
std::vector<std::shared_ptr<Window>> m_Window;
bool m_isRunning;
SakuraVNE::LayerStack m_LayerStack;
LayerStack m_LayerStack;
ImGuiInit *m_ImGui;
static Application *s_Instance;
SakuraVNE::ImGuiLayer *m_ImGuiLayer;
friend class Layer;
};
} // namespace SakuraVNE

View File

@@ -1,6 +1 @@
#include "Event.h"
#include "Application.h"
#include "Log.h"
#include "SDL3/SDL.h"
void SakuraVNE::ProcessEvents() {}

View File

@@ -1,3 +1,51 @@
namespace SakuraVNE{
void ProcessEvents();
}
#pragma once
#include <functional>
#include <string>
namespace SakuraVNE {
enum class EventType {
None = 0,
WindowClose,
WindowResize,
KeyPressed,
KeyReleased,
MouseButtonPressed,
MouseButtonReleased,
MouseMoved,
MouseScrolled,
};
#define EVENT_CLASS_TYPE(type) \
static EventType GetStaticType() { return EventType::type; } \
virtual EventType GetEventType() const override { return GetStaticType(); } \
virtual const char *GetName() const override { return #type; }
class Event {
public:
bool Handled = false;
virtual ~Event() {}
virtual EventType GetEventType() const = 0;
virtual const char *GetName() const = 0;
virtual std::string ToString() const { return GetName(); }
};
class EventDispatcher {
template <typename T> using EventFn = std::function<bool(T &)>;
public:
EventDispatcher(Event &event) : m_Event(event) {}
template <typename T> bool Dispatch(EventFn<T> func) {
if (m_Event.GetEventType() == T::GetStaticType() && !m_Event.Handled) {
m_Event.Handled = func(*(T *)&m_Event);
return true;
}
return false;
}
private:
Event &m_Event;
};
} // namespace SakuraVNE

View File

@@ -0,0 +1,94 @@
#pragma once
#include "Event.h"
#include <format>
#include <string>
namespace SakuraVNE {
// Key events
class KeyEvent : public Event {
public:
inline int GetKeyCode() const { return m_KeyCode; }
protected:
KeyEvent(int keycode) : m_KeyCode(keycode) {}
int m_KeyCode;
};
class KeyPressedEvent : public KeyEvent {
public:
KeyPressedEvent(int keycode, bool isRepeat) : KeyEvent(keycode), m_IsRepeat(isRepeat) {}
inline bool IsRepeat() const { return m_IsRepeat; }
std::string ToString() const override { return std::format("KeyPressedEvent: {} (repeat={})", m_KeyCode, m_IsRepeat); }
EVENT_CLASS_TYPE(KeyPressed)
private:
bool m_IsRepeat;
};
class KeyReleasedEvent : public KeyEvent {
public:
KeyReleasedEvent(int keycode) : KeyEvent(keycode) {}
std::string ToString() const override { return std::format("KeyReleasedEvent: {}", m_KeyCode); }
EVENT_CLASS_TYPE(KeyReleased)
};
// Mouse events
class MouseMovedEvent : public Event {
public:
MouseMovedEvent(double x, double y) : m_MouseX(x), m_MouseY(y) {}
inline double GetX() const { return m_MouseX; }
inline double GetY() const { return m_MouseY; }
std::string ToString() { return std::format("MouseMovedEvent: {}, {}", m_MouseX, m_MouseY); }
EVENT_CLASS_TYPE(MouseMoved)
private:
double m_MouseX, m_MouseY;
};
class MouseScrolledEvent : public Event {
public:
MouseScrolledEvent(double xOffset, double yOffset) : m_XOffset(xOffset), m_YOffset(yOffset) {}
inline double GetXOffset() const { return m_XOffset; }
inline double GetYOffset() const { return m_YOffset; }
std::string ToString() const override { return std::format("MouseScrolledEvent: {}, {}", m_XOffset, m_YOffset); }
EVENT_CLASS_TYPE(MouseScrolled)
private:
double m_XOffset, m_YOffset;
};
class MouseButtonEvent : public Event {
public:
inline int GetMouseButton() const { return m_Button; }
protected:
MouseButtonEvent(int button) : m_Button(button) {}
int m_Button;
};
class MouseButtonPressedEvent : public MouseButtonEvent {
public:
MouseButtonPressedEvent(int button) : MouseButtonEvent(button) {}
std::string ToString() const override { return std::format("MouseButtonPressedEvent: {}", m_Button); }
EVENT_CLASS_TYPE(MouseButtonPressed)
};
class MouseButtonReleasedEvent : public MouseButtonEvent {
public:
MouseButtonReleasedEvent(int button) : MouseButtonEvent(button) {}
std::string ToString() const override { return std::format("MouseButtonReleasedEvent: {}", m_Button); }
EVENT_CLASS_TYPE(MouseButtonReleased)
};
} // namespace SakuraVNE

View File

@@ -1,6 +1,20 @@
#include "Layer.h"
#include "Application.h"
#include "LayerStack.h"
#include <memory>
#include <string>
namespace SakuraVNE {
Layer::Layer(const std::string &name) : m_LayerName(name) {}
Layer::Layer(const std::string &name, bool isActive) : m_LayerName(name), m_isActive(isActive) {}
void Layer::QueueTransition(std::unique_ptr<Layer> toLayer) {
auto &layerStack = Application::Get().GetLayerStack();
LayerCommand command;
command.action = LayerAction::Transition;
command.targetLayer = this;
command.newLayer = std::move(toLayer);
layerStack.SubmitCommand(std::move(command));
}
} // namespace SakuraVNE

View File

@@ -1,23 +1,35 @@
#pragma once
#include "Event.h"
#include "SDL3/SDL_stdinc.h"
#include <concepts>
#include <memory>
#include <string>
namespace SakuraVNE {
class Layer {
public:
Layer(const std::string &name = "Layer");
Layer(const std::string &name = "Layer", bool isActive = true);
virtual ~Layer() = default;
virtual void OnStart() {}
virtual void OnFrame(float timestamp) {}
virtual void OnEnd() {}
virtual void OnEvent() {}
virtual void OnFrame(Uint64 timestamp) {}
virtual void OnEvent(Event &e) {}
virtual void OnAttach() {}
virtual void OnDetach() {}
virtual void OnImGuiRender() {}
virtual void OnRender() {}
virtual void Suspend() { m_isActive = false; }
virtual void Activate() { m_isActive = true; }
template <std::derived_from<Layer> T, typename... Args> void TransitionTo(Args &&...args) { QueueTransition(std::move(std::make_unique<T>(std::forward<Args>(args)...))); }
const std::string &GetName() const { return m_LayerName; }
protected:
std::string m_LayerName;
bool m_isActive = true;
private:
void QueueTransition(std::unique_ptr<Layer> layer);
};
} // namespace SakuraVNE

View File

@@ -1,22 +1,59 @@
#include "LayerStack.h"
#include <algorithm>
#include <memory>
#include <vector>
namespace SakuraVNE {
LayerStack::LayerStack() {
m_LayerStack.reserve(3);
m_CommandQueue.reserve(10);
}
LayerStack::~LayerStack() {
for (Layer *layer : m_LayerStack) {
for (auto &layer : m_LayerStack) {
layer->OnDetach();
delete layer;
}
}
void LayerStack::PushLayer(Layer *layer) {
m_LayerStack.emplace(m_LayerStack.begin() + m_LayerIndex, layer);
void LayerStack::SubmitCommand(LayerCommand command) { m_CommandQueue.push_back(std::move(command)); }
void LayerStack::ProcessCommands() {
if (m_CommandQueue.empty()) {
return;
}
for (auto &command : m_CommandQueue) {
switch (command.action) {
case LayerAction::Pop:
PopLayer(command.targetLayer);
break;
case LayerAction::Push:
PushLayer(std::move(command.newLayer));
break;
case LayerAction::Transition:
auto it = std::find_if(m_LayerStack.begin(), m_LayerStack.end(), [&](const std::unique_ptr<Layer> &layer) { return layer.get() == command.targetLayer; });
if (it != m_LayerStack.end()) {
(*it)->OnDetach();
command.newLayer->OnAttach();
*it = std::move(command.newLayer);
}
break;
}
}
m_CommandQueue.clear();
}
void LayerStack::PushLayer(std::unique_ptr<Layer> layer) {
m_LayerStack.emplace(m_LayerStack.begin() + m_LayerIndex, std::move(layer));
m_LayerIndex++;
}
void LayerStack::PopLayer(Layer *layer) {
auto match = std::find(m_LayerStack.begin(), m_LayerStack.begin() + m_LayerIndex, layer);
auto match = std::find_if(m_LayerStack.begin(), m_LayerStack.begin() + m_LayerIndex, [layer](const std::unique_ptr<Layer> &ptr) { return ptr.get() == layer; });
if (match != m_LayerStack.begin() + m_LayerIndex) {
layer->OnDetach();
m_LayerStack.erase(match);
@@ -24,10 +61,10 @@ void LayerStack::PopLayer(Layer *layer) {
}
}
void LayerStack::PushOverLay(Layer *layer) { m_LayerStack.emplace_back(layer); }
void LayerStack::PushOverLay(std::unique_ptr<Layer> layer) { m_LayerStack.emplace_back(std::move(layer)); }
void LayerStack::PopOverlay(Layer *layer) {
auto match = std::find(m_LayerStack.begin() + m_LayerIndex, m_LayerStack.end(), layer);
auto match = std::find_if(m_LayerStack.begin() + m_LayerIndex, m_LayerStack.end(), [layer](const std::unique_ptr<Layer> &ptr) { return ptr.get() == layer; });
if (match != m_LayerStack.end()) {
layer->OnDetach();

View File

@@ -2,35 +2,48 @@
#include "Layer.h"
#include <iterator>
#include <memory>
#include <vector>
namespace SakuraVNE {
enum class LayerAction { Push, Pop, Transition };
struct LayerCommand {
LayerAction action;
Layer *targetLayer;
std::unique_ptr<Layer> newLayer;
};
class LayerStack {
public:
LayerStack() = default;
LayerStack();
~LayerStack();
void PushLayer(Layer *layer);
void PushLayer(std::unique_ptr<Layer> layer);
void PopLayer(Layer *layer);
void PushOverLay(Layer *layer);
void PushOverLay(std::unique_ptr<Layer> layer);
void PopOverlay(Layer *layer);
std::vector<Layer *>::iterator begin() { return m_LayerStack.begin(); }
std::vector<Layer *>::iterator end() { return m_LayerStack.end(); }
std::vector<Layer *>::reverse_iterator rbegin() { return m_LayerStack.rbegin(); }
std::vector<Layer *>::reverse_iterator rend() { return m_LayerStack.rend(); }
std::vector<std::unique_ptr<Layer>>::iterator begin() { return m_LayerStack.begin(); }
std::vector<std::unique_ptr<Layer>>::iterator end() { return m_LayerStack.end(); }
std::vector<std::unique_ptr<Layer>>::reverse_iterator rbegin() { return m_LayerStack.rbegin(); }
std::vector<std::unique_ptr<Layer>>::reverse_iterator rend() { return m_LayerStack.rend(); }
std::vector<Layer *>::const_iterator begin() const { return m_LayerStack.begin(); }
std::vector<Layer *>::const_iterator end() const { return m_LayerStack.end(); }
std::vector<Layer *>::const_reverse_iterator rbegin() const { return m_LayerStack.rbegin(); }
std::vector<Layer *>::const_reverse_iterator rend() const { return m_LayerStack.rend(); }
std::vector<std::unique_ptr<Layer>>::const_iterator begin() const { return m_LayerStack.begin(); }
std::vector<std::unique_ptr<Layer>>::const_iterator end() const { return m_LayerStack.end(); }
std::vector<std::unique_ptr<Layer>>::const_reverse_iterator rbegin() const { return m_LayerStack.rbegin(); }
std::vector<std::unique_ptr<Layer>>::const_reverse_iterator rend() const { return m_LayerStack.rend(); }
void SubmitCommand(LayerCommand command);
void ProcessCommands();
#ifdef DEBUG
// this is only used for the tests for now, so it will be taken out of the release build
inline const std::vector<Layer *> &GetLayers() const { return m_LayerStack; }
inline const std::vector<std::unique_ptr<Layer>> &GetLayers() const { return m_LayerStack; }
#endif
private:
std::vector<Layer *> m_LayerStack;
std::vector<LayerCommand> m_CommandQueue;
std::vector<std::unique_ptr<Layer>> m_LayerStack;
unsigned int m_LayerIndex = 0;
};
} // namespace SakuraVNE

222
SakuraCore/src/Window.cpp Normal file
View File

@@ -0,0 +1,222 @@
#include "Window.h"
#include "InputEvents.h"
#include "Log.h"
#include "SDL3/SDL_events.h"
#include "SDL3/SDL_mouse.h"
#include "SDL3/SDL_video.h"
#include "WindowEvents.h"
#include <span>
namespace SakuraVNE {
Window::Window(const WindowData &data) : m_Data(data), m_Flags(0) {}
Window::~Window() { Destroy(); }
bool Window::Create(const SDL_WindowFlags flags) {
m_Flags |= flags;
m_Handle = SDL_CreateWindow(m_Data.title.c_str(), m_Data.width, m_Data.height, m_Flags);
if (!m_Handle) {
LOG_ERROR("SDL window could not be created! {0}", SDL_GetError());
return false;
} else {
LOG_INFO("SDl window created");
}
if (m_Data.pos_x != -1 && m_Data.pos_y != -1) {
if (!SDL_SetWindowPosition(m_Handle, m_Data.pos_x, m_Data.pos_y)) {
LOG_ERROR("Failed to set SDL_Window position {0}", SDL_GetError());
} else {
LOG_INFO("SDL window position set to the initial value: x {0}, y {1}", m_Data.pos_x, m_Data.pos_y);
}
} else {
LOG_WARN("SDL window position not set. Will not attempt to set window position.");
}
return true;
}
void Window::ProcessEvent(const SDL_Event &event) {
switch (event.type) {
case SDL_EVENT_WINDOW_CLOSE_REQUESTED: {
WindowClosedEvent e;
RaiseEvent(e);
break;
}
case SDL_EVENT_WINDOW_RESIZED: {
WindowResizeEvent e((uint32_t)event.window.data1, (uint32_t)event.window.data2);
RaiseEvent(e);
break;
}
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP: {
bool isRepeat = event.key.repeat != 0;
if (event.type == SDL_EVENT_KEY_DOWN) {
KeyPressedEvent e(event.key.key, isRepeat);
RaiseEvent(e);
} else {
KeyReleasedEvent e(event.key.key);
RaiseEvent(e);
}
break;
}
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP: {
if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
MouseButtonPressedEvent e(event.button.button);
RaiseEvent(e);
} else {
MouseButtonReleasedEvent e(event.button.button);
RaiseEvent(e);
}
break;
}
case SDL_EVENT_MOUSE_MOTION: {
MouseMovedEvent e(event.motion.x, event.motion.y);
RaiseEvent(e);
break;
}
case SDL_EVENT_MOUSE_WHEEL: {
MouseScrolledEvent e(event.wheel.x, event.wheel.y);
RaiseEvent(e);
break;
}
}
}
void Window::AddFlags(std::span<const SDL_WindowFlags> flags) {
for (auto &flag : flags) {
switch (flag) {
case SDL_WINDOW_FULLSCREEN:
SDL_SetWindowFullscreen(m_Handle, true);
break;
case SDL_WINDOW_HIDDEN:
SDL_ShowWindow(m_Handle);
break;
case SDL_WINDOW_BORDERLESS:
SDL_SetWindowBordered(m_Handle, false);
break;
case SDL_WINDOW_RESIZABLE:
SDL_SetWindowResizable(m_Handle, true);
break;
case SDL_WINDOW_MINIMIZED:
SDL_MinimizeWindow(m_Handle);
break;
case SDL_WINDOW_MAXIMIZED:
SDL_MaximizeWindow(m_Handle);
break;
case SDL_WINDOW_MOUSE_GRABBED:
SDL_SetWindowMouseGrab(m_Handle, true);
break;
case SDL_WINDOW_MOUSE_CAPTURE:
SDL_SetWindowMouseGrab(m_Handle, true);
break;
case SDL_WINDOW_MOUSE_RELATIVE_MODE:
SDL_SetWindowRelativeMouseMode(m_Handle, true);
break;
case SDL_WINDOW_MODAL:
SDL_SetWindowModal(m_Handle, true);
break;
case SDL_WINDOW_ALWAYS_ON_TOP:
SDL_SetWindowAlwaysOnTop(m_Handle, true);
break;
case SDL_WINDOW_KEYBOARD_GRABBED:
SDL_SetWindowKeyboardGrab(m_Handle, true);
break;
case SDL_WINDOW_NOT_FOCUSABLE:
SDL_SetWindowFocusable(m_Handle, false);
break;
default:
LOG_WARN("You can only use this when creating a window or the flag is readonly");
break;
}
m_Flags |= flag;
}
}
void Window::RemoveFlags(std::span<const SDL_WindowFlags> flags) {
for (auto &flag : flags) {
switch (flag) {
case SDL_WINDOW_FULLSCREEN:
SDL_SetWindowFullscreen(m_Handle, false);
break;
case SDL_WINDOW_HIDDEN:
SDL_HideWindow(m_Handle);
break;
case SDL_WINDOW_BORDERLESS:
SDL_SetWindowBordered(m_Handle, true);
break;
case SDL_WINDOW_RESIZABLE:
SDL_SetWindowResizable(m_Handle, false);
break;
case SDL_WINDOW_MINIMIZED:
case SDL_WINDOW_MAXIMIZED:
SDL_RestoreWindow(m_Handle);
break;
case SDL_WINDOW_MOUSE_GRABBED:
SDL_SetWindowMouseGrab(m_Handle, false);
break;
case SDL_WINDOW_MOUSE_CAPTURE:
SDL_SetWindowMouseGrab(m_Handle, false);
break;
case SDL_WINDOW_MOUSE_RELATIVE_MODE:
SDL_SetWindowRelativeMouseMode(m_Handle, false);
break;
case SDL_WINDOW_MODAL:
SDL_SetWindowModal(m_Handle, false);
break;
case SDL_WINDOW_ALWAYS_ON_TOP:
SDL_SetWindowAlwaysOnTop(m_Handle, false);
break;
case SDL_WINDOW_KEYBOARD_GRABBED:
SDL_SetWindowKeyboardGrab(m_Handle, false);
break;
case SDL_WINDOW_NOT_FOCUSABLE:
SDL_SetWindowFocusable(m_Handle, true);
break;
default:
LOG_WARN("You can only use this when creating a window or the flag is readonly");
break;
}
m_Flags &= flag;
}
}
void Window::Resize() {
SDL_GetWindowSize(m_Handle, &m_Data.width, &m_Data.height);
SDL_SetWindowSize(m_Handle, m_Data.width, m_Data.height);
}
void Window::Destroy() {
if (m_Handle) {
SDL_DestroyWindow(m_Handle);
}
m_Handle = nullptr;
}
void Window::RaiseEvent(Event &event) {
if (m_Data.eventCallback) {
m_Data.eventCallback(event);
}
}
// TODO: this need testing because i am really not sure this is correct
// should this be static or go somewhere else | probably in application?
auto Window::GetMousePos() {
struct result {
float x;
float y;
};
SDL_GetMouseState(&m_MouseXPos, &m_MouseYPos);
return result{m_MouseXPos, m_MouseYPos};
}
} // namespace SakuraVNE

54
SakuraCore/src/Window.h Normal file
View File

@@ -0,0 +1,54 @@
#pragma once
#include "Event.h"
#include "SDL3/SDL_events.h"
#include "SDL3/SDL_video.h"
#include <functional>
#include <span>
#include <string>
namespace SakuraVNE {
struct WindowData {
SDL_WindowFlags windowFlags = 0;
std::string title = "Window Title";
int width = 1280;
int height = 720;
int pos_x = -1;
int pos_y = -1;
bool isResizable = true;
bool isVsync = true;
using EventCallbackFn = std::function<void(Event &)>;
EventCallbackFn eventCallback;
};
class Window {
public:
Window(const WindowData &data = WindowData());
~Window();
bool Create(const SDL_WindowFlags flags = 0);
void Destroy();
void Update();
void AddFlags(std::span<const SDL_WindowFlags> flags = {});
void RemoveFlags(std::span<const SDL_WindowFlags> flags = {});
void Resize();
void ProcessEvent(const SDL_Event &event);
void RaiseEvent(Event &event);
bool ShouldClose() const;
auto GetMousePos();
SDL_Window *GetHandle() const { return m_Handle; }
private:
WindowData m_Data;
SDL_Window *m_Handle = nullptr;
SDL_WindowFlags m_Flags;
float m_MouseXPos;
float m_MouseYPos;
};
} // namespace SakuraVNE

View File

@@ -0,0 +1,29 @@
#pragma once
#include "Event.h"
#include <cstdint>
#include <format>
#include <string>
namespace SakuraVNE {
class WindowClosedEvent : public Event {
public:
WindowClosedEvent() {}
EVENT_CLASS_TYPE(WindowClose)
};
class WindowResizeEvent : public Event {
public:
WindowResizeEvent(uint32_t width, uint32_t height) : m_Width(width), m_Height(height) {}
// TODO: chech if uint32_t works with sdl3 windows
inline uint32_t GetWidth() const { return m_Width; }
inline uint32_t GetHeight() const { return m_Height; }
std::string ToString() const override { return std::format("WindowResizeEvent: {}, {}", m_Width, m_Height); }
EVENT_CLASS_TYPE(WindowResize)
private:
uint32_t m_Width, m_Height;
};
} // namespace SakuraVNE

View File

@@ -1,4 +1,4 @@
#include "imguilayer.h"
#include "imguiinit.h"
#include "Application.h"
#include "Log.h"
#include "imgui.h"
@@ -6,15 +6,18 @@
#include "imgui_impl_sdlrenderer3.h"
namespace SakuraVNE {
ImGuiLayer::ImGuiLayer() : Layer("ImGuiLayer") {}
ImGuiInit::ImGuiInit() { Init(); }
ImGuiInit::~ImGuiInit() { Shutdown(); }
void ImGuiLayer::OnAttach() {
void ImGuiInit::Init() {
// Imgui init
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
ImGui::StyleColorsDark();
@@ -24,23 +27,29 @@ void ImGuiLayer::OnAttach() {
LOG_INFO("Imgui layer OnAttach ran");
}
void ImGuiLayer::OnDetach() {
void ImGuiInit::Shutdown() {
ImGui_ImplSDLRenderer3_Shutdown();
ImGui_ImplSDL3_Shutdown();
ImGui::DestroyContext();
LOG_WARN("Imgui on detach ran");
}
void ImGuiLayer::Begin() {
void ImGuiInit::Begin() {
ImGui_ImplSDLRenderer3_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
// for docking the imgui windows to the edge of the sdl window
ImGui::DockSpaceOverViewport();
}
void ImGuiLayer::End() {
void ImGuiInit::End() {
ImGuiIO &io = ImGui::GetIO();
Application &app = Application::Get();
io.DisplaySize = ImVec2((float)app.GetWindowData().width, (float)app.GetWindowData().height);
io.DisplaySize = ImVec2((float)app.GetAppData().windowdata.width, (float)app.GetAppData().windowdata.height);
ImGui::Render();
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), Application::Get().GetSDLRenderer());
ImGui::EndFrame();
}
} // namespace SakuraVNE

View File

@@ -0,0 +1,14 @@
#pragma once
namespace SakuraVNE {
class ImGuiInit {
public:
ImGuiInit();
~ImGuiInit();
void Init();
void Shutdown();
void Begin();
void End();
};
} // namespace SakuraVNE

View File

@@ -1,21 +0,0 @@
#pragma once
#include "Layer.h"
namespace SakuraVNE {
class ImGuiLayer : public Layer {
public:
ImGuiLayer();
~ImGuiLayer() = default;
// virtual void OnStart() override;
// virtual void OnFrame(float timestamp) override;
// virtual void OnEnd() override;
virtual void OnAttach() override;
virtual void OnDetach() override;
// virtual void OnEvent() override;
void Begin();
void End();
};
} // namespace SakuraVNE

View File

@@ -3,19 +3,21 @@
#include "Layer.h"
#include "LayerStack.h"
#include <iostream>
#include <memory>
#include <vector>
TEST_CASE("Layer operations", "[Layer]") {
SakuraVNE::LayerStack lstack;
const auto &layers = lstack.GetLayers();
SakuraVNE::Layer *layer1 = new SakuraVNE::Layer("layer1");
lstack.PushLayer(layer1);
// Create the unique_ptr, keep a raw pointer for testing Pop operations later, then move it.
auto u_layer1 = std::make_unique<SakuraVNE::Layer>("layer1");
SakuraVNE::Layer *layer1 = u_layer1.get();
lstack.PushLayer(std::move(u_layer1));
SECTION("PushLayer should make the size 2") {
SakuraVNE::Layer *layer2 = new SakuraVNE::Layer("layer2");
lstack.PushLayer(layer2);
auto u_layer2 = std::make_unique<SakuraVNE::Layer>("layer2");
lstack.PushLayer(std::move(u_layer2));
REQUIRE(layers.size() == 2);
REQUIRE(layers[1]->GetName() == "layer2");
}
@@ -24,25 +26,26 @@ TEST_CASE("Layer operations", "[Layer]") {
REQUIRE(layers.size() == 0);
}
SECTION("PushOverlay should always put at the end of the list and should always be after the last fence index") {
SakuraVNE::Layer *layer2 = new SakuraVNE::Layer("layer2");
lstack.PushOverLay(layer2);
auto u_layer2 = std::make_unique<SakuraVNE::Layer>("layer2");
lstack.PushOverLay(std::move(u_layer2));
REQUIRE(layers[1]->GetName() == "layer2");
SakuraVNE::Layer *layer3 = new SakuraVNE::Layer("layer3");
lstack.PushLayer(layer3);
auto u_layer3 = std::make_unique<SakuraVNE::Layer>("layer3");
lstack.PushLayer(std::move(u_layer3));
REQUIRE(layers[1]->GetName() == "layer3");
REQUIRE(layers[2]->GetName() == "layer2");
}
SECTION("Pop overlay") {
SakuraVNE::Layer *layer6 = new SakuraVNE::Layer("layer6");
lstack.PushOverLay(layer6);
auto u_layer6 = std::make_unique<SakuraVNE::Layer>("layer6");
SakuraVNE::Layer *layer6 = u_layer6.get();
lstack.PushOverLay(std::move(u_layer6));
REQUIRE(layers[1]->GetName() == "layer6");
lstack.PopOverlay(layer6);
bool hasLayer2InVec = false;
for (auto layer : layers) {
for (auto &layer : layers) {
if (layer->GetName() == "layer6") {
hasLayer2InVec = true;
}