initial commit

This commit is contained in:
2025-05-01 23:03:47 +02:00
commit a23fbff52a
200 changed files with 434542 additions and 0 deletions

39
ScopEngine/Runtime/Includes/Core/CLI.h git.filemode.normal_file
View File

@@ -0,0 +1,39 @@
#ifndef __SCOP_CLI__
#define __SCOP_CLI__
#include <string>
#include <optional>
#include <unordered_map>
#include <unordered_set>
namespace Scop
{
class CommandLineInterface
{
public:
inline CommandLineInterface();
inline std::optional<std::string> GetOption(std::string_view option) const;
inline std::optional<std::string> GetArgument(std::string_view arg) const;
inline bool HasFlag(std::string_view flag) const;
inline const std::string& GetCommand() const;
inline void Feed(int ac, char** av);
inline static CommandLineInterface& Get() noexcept;
inline ~CommandLineInterface();
private:
static inline CommandLineInterface* s_instance = nullptr;
std::unordered_map<std::string, std::string> m_options;
std::unordered_set<std::string> m_flags;
std::unordered_set<std::string> m_args;
std::string m_cmd;
};
}
#include <Core/CLI.inl>
#endif

72
ScopEngine/Runtime/Includes/Core/CLI.inl git.filemode.normal_file
View File

@@ -0,0 +1,72 @@
#pragma once
#include <Core/CLI.h>
#include <algorithm>
namespace Scop
{
CommandLineInterface::CommandLineInterface()
{
s_instance = this;
}
std::optional<std::string> CommandLineInterface::GetOption(std::string_view option) const
{
auto it = m_options.find(static_cast<std::string>(option));
if(it == m_options.end())
return std::nullopt;
return it->second;
}
std::optional<std::string> CommandLineInterface::GetArgument(std::string_view arg) const
{
auto it = std::find(m_args.begin(), m_args.end(), arg);
if(it == m_args.end())
return std::nullopt;
return *it;
}
bool CommandLineInterface::HasFlag(std::string_view flag) const
{
return std::find(m_flags.begin(), m_flags.end(), flag) != m_flags.end();
}
const std::string& CommandLineInterface::GetCommand() const
{
return m_cmd;
}
void CommandLineInterface::Feed(int ac, char** av)
{
if(ac == 0 || av == nullptr)
return;
m_cmd = av[0];
for(int i = 1; i < ac; i++)
{
std::string_view arg{ av[i] };
if(arg.front() == '-')
{
arg.remove_prefix(1);
if(arg.front() == '-') // in case of arg begenning with --
arg.remove_prefix(1);
std::size_t finder = arg.find_first_of('=');
if(finder != std::string_view::npos)
m_options.emplace(std::make_pair(arg.substr(0, finder), arg.substr(finder + 1)));
else
m_flags.emplace(arg);
}
else
m_args.emplace(arg);
}
}
CommandLineInterface& CommandLineInterface::Get() noexcept
{
return *s_instance;
}
CommandLineInterface::~CommandLineInterface()
{
s_instance = nullptr;
}
}

67
ScopEngine/Runtime/Includes/Core/Engine.h git.filemode.normal_file
View File

@@ -0,0 +1,67 @@
#ifndef __SCOP_CORE_ENGINE__
#define __SCOP_CORE_ENGINE__
#include <cstdint>
#include <filesystem>
#include <Platform/Window.h>
#include <Platform/Inputs.h>
#include <Renderer/Renderer.h>
#include <Renderer/ScenesRenderer.h>
#include <Renderer/RenderCore.h>
#include <Core/Logs.h>
#include <Graphics/Scene.h>
#include <Core/CLI.h>
#ifdef DEBUG
#include <Debug/ImGuiRenderer.h>
#endif
namespace Scop
{
class ScopEngine
{
friend class Scene;
public:
ScopEngine(int ac, char** av, const std::string& title, std::uint32_t width, std::uint32_t height, std::filesystem::path assets_path);
void Run();
[[nodiscard]] inline const Window& GetWindow() const noexcept { return m_window; }
[[nodiscard]] inline std::filesystem::path GetAssetsPath() const { return m_assets_path; }
inline void RegisterMainScene(NonOwningPtr<Scene> scene) noexcept { m_main_scene = scene; p_current_scene = m_main_scene; }
inline NonOwningPtr<Scene> GetRootScene() const noexcept { return m_main_scene; }
constexpr void Quit() noexcept { m_running = false; }
[[nodiscard]] static inline bool IsInit() noexcept { return s_instance != nullptr; }
[[nodiscard]] static ScopEngine& Get() noexcept;
~ScopEngine();
private:
inline void SwitchToScene(NonOwningPtr<Scene> current) noexcept { p_current_scene = current; m_scene_changed = true; }
private:
static ScopEngine* s_instance;
Inputs m_inputs;
Renderer m_renderer;
#ifdef DEBUG
ImGuiRenderer m_imgui;
#endif
CommandLineInterface m_cli;
Window m_window;
NonOwningPtr<Scene> m_main_scene;
SceneRenderer m_scene_renderer;
std::filesystem::path m_assets_path;
std::unique_ptr<RenderCore> p_renderer_core;
NonOwningPtr<Scene> p_current_scene;
bool m_running = true;
bool m_scene_changed = false;
};
}
#endif

35
ScopEngine/Runtime/Includes/Core/Enums.h git.filemode.normal_file
View File

@@ -0,0 +1,35 @@
#ifndef __SCOPE_CORE_ENUMS__
#define __SCOPE_CORE_ENUMS__
#include <cstddef>
namespace Scop
{
enum class LogType
{
Debug = 0,
Message,
Warning,
Error,
FatalError,
EndEnum
};
constexpr std::size_t LogTypeCount = static_cast<std::size_t>(LogType::EndEnum);
enum class Event
{
SceneHasChangedEventCode = 55,
ResizeEventCode = 56,
FrameBeginEventCode = 57,
FatalErrorEventCode = 168,
QuitEventCode = 168,
EndEnum
};
constexpr std::size_t EventCount = static_cast<std::size_t>(Event::EndEnum);
}
#endif

14
ScopEngine/Runtime/Includes/Core/EventBase.h git.filemode.normal_file
View File

@@ -0,0 +1,14 @@
#ifndef __SCOPE_CORE_BASE_EVENT__
#define __SCOPE_CORE_BASE_EVENT__
#include <Core/Enums.h>
namespace Scop
{
struct EventBase
{
virtual Event What() const = 0;
};
}
#endif

25
ScopEngine/Runtime/Includes/Core/EventBus.h git.filemode.normal_file
View File

@@ -0,0 +1,25 @@
#ifndef __SCOPE_CORE_EVENT_BUS__
#define __SCOPE_CORE_EVENT_BUS__
#include <Core/EventBase.h>
#include <Core/EventListener.h>
#include <string>
#include <vector>
namespace Scop
{
class EventBus
{
public:
EventBus() = delete;
static void Send(const std::string& listener_name, const EventBase& event);
static void SendBroadcast(const EventBase& event);
inline static void RegisterListener(const EventListener& listener) { s_listeners.push_back(listener); }
~EventBus() = delete;
private:
inline static std::vector<EventListener> s_listeners;
};
}
#endif

View File

@@ -0,0 +1,27 @@
#ifndef __SCOPE_CORE_EVENT_LISTENER__
#define __SCOPE_CORE_EVENT_LISTENER__
#include <Core/EventBase.h>
#include <functional>
#include <string>
namespace Scop
{
class EventListener
{
public:
EventListener() = delete;
EventListener(std::function<void(const EventBase&)> functor, std::string name);
[[nodiscard]] inline const std::string& GetName() const { return m_name; }
inline void Call(const EventBase& event) const noexcept { m_listen_functor(event); }
~EventListener() = default;
private:
std::function<void(const EventBase&)> m_listen_functor;
std::string m_name;
};
}
#endif

22
ScopEngine/Runtime/Includes/Core/Format.h git.filemode.normal_file
View File

@@ -0,0 +1,22 @@
#ifndef __SCOPE_CORE_FORMAT__
#define __SCOPE_CORE_FORMAT__
#include <type_traits>
#include <string_view>
#include <tuple>
namespace Scop
{
template<typename T, typename = void>
struct IsOstreamable : std::false_type {};
template<typename T>
struct IsOstreamable<T, std::void_t<decltype(std::declval<std::ostream>() << std::declval<T>())>> : std::true_type {};
template<typename... Args, std::enable_if_t<std::conjunction_v<IsOstreamable<Args>...>, int> = 0>
auto Format(std::string_view format, const Args&... args);
}
#include <Core/Format.inl>
#endif

137
ScopEngine/Runtime/Includes/Core/Format.inl git.filemode.normal_file
View File

@@ -0,0 +1,137 @@
#pragma once
#include <Core/Format.h>
#include <sstream>
#include <ostream>
// TODO : make single functions that manages stringstreams and ostreams
namespace Scop
{
namespace Internal
{
template<typename It>
void Format(std::stringstream& ss, It first, It last)
{
for(auto it = first; it != last; ++it)
{
switch(*it)
{
case '%':
throw std::invalid_argument{"too few arguments"};
case '/':
++it;
if(it == last)
throw std::invalid_argument{"stray '/'"};
[[fallthrough]];
default: ss << *it;
}
}
}
template<typename It, typename T, typename... Args>
void Format(std::stringstream& ss, It first, It last, const T& arg, const Args&... args)
{
for(auto it = first; it != last; ++it)
{
switch(*it)
{
case '%':
ss << arg;
return Format(ss, ++it, last, args...);
case '/':
++it;
if(it == last)
throw std::invalid_argument{"stray '/'"};
[[fallthrough]];
default: ss << *it;
}
}
throw std::invalid_argument{"too many arguments"};
}
template<typename It>
void Format(std::ostream& os, It first, It last)
{
for(auto it = first; it != last; ++it)
{
switch(*it)
{
case '%':
throw std::invalid_argument{"too few arguments"};
case '/':
++it;
if(it == last)
throw std::invalid_argument{"stray '/'"};
[[fallthrough]];
default: os << *it;
}
}
}
template<typename It, typename T, typename... Args>
void Format(std::ostream& os, It first, It last, const T& arg, const Args&... args)
{
for(auto it = first; it != last; ++it)
{
switch(*it)
{
case '%':
os << arg;
return Format(os, ++it, last, args...);
case '/':
++it;
if(it == last)
throw std::invalid_argument{"stray '/'"};
[[fallthrough]];
default: os << *it;
}
}
throw std::invalid_argument{"too many arguments"};
}
template<typename... Args>
struct Formatter
{
std::string_view format;
std::tuple<const Args&...> args;
};
template<typename... Args, std::size_t... Is>
void FormatHelper(std::stringstream& ss, const Formatter<Args...>& formatter, std::index_sequence<Is...>)
{
Format(ss, formatter.format.begin(), formatter.format.end(),
std::get<Is>(formatter.args)...);
}
template<typename... Args>
std::stringstream& operator<<(std::stringstream& ss, const Formatter<Args...>& printer)
{
FormatHelper(ss, printer, std::index_sequence_for<Args...>{});
return ss;
}
template<typename... Args, std::size_t... Is>
void FormatHelper(std::ostream& os, const Formatter<Args...>& formatter, std::index_sequence<Is...>)
{
Format(os, formatter.format.begin(), formatter.format.end(),
std::get<Is>(formatter.args)...);
}
template<typename... Args>
std::ostream& operator<<(std::ostream& os, const Formatter<Args...>& printer)
{
FormatHelper(os, printer, std::index_sequence_for<Args...>{});
return os;
}
}
template<typename... Args, std::enable_if_t<std::conjunction_v<IsOstreamable<Args>...>, int>>
auto Format(std::string_view format, const Args&... args)
{
return Internal::Formatter<Args...>{format, std::forward_as_tuple(args...)};
}
}

74
ScopEngine/Runtime/Includes/Core/Logs.h git.filemode.normal_file
View File

@@ -0,0 +1,74 @@
#ifndef __SCOPE_CORE_LOGS__
#define __SCOPE_CORE_LOGS__
#include <string>
#include <string_view>
#include <Core/Enums.h>
namespace Scop
{
template<typename... Args>
void DebugLog(unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args);
template<typename... Args>
void Error(unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args);
template<typename... Args>
void Warning(unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args);
template<typename... Args>
void Message(unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args);
template<typename... Args>
void FatalError(unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args);
template<typename... Args>
void Verify(bool cond, unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args);
class Logs
{
public:
Logs() = delete;
static void Report(LogType type, std::string message);
static void Report(LogType type, unsigned int line, std::string_view file, std::string_view function, std::string message);
~Logs() = delete;
};
#if defined(DEBUG)
template<typename... Args>
void Assert(bool cond, unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args);
#else
template<typename... Args>
void Assert(bool cond, unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args) {}
#endif
}
#include <Core/Logs.inl>
namespace Scop
{
#undef DebugLog
#define DebugLog(...) DebugLog(__LINE__, __FILE__, __func__, __VA_ARGS__)
#undef Message
#define Message(...) Message(__LINE__, __FILE__, __func__, __VA_ARGS__)
#undef Warning
#define Warning(...) Warning(__LINE__, __FILE__, __func__, __VA_ARGS__)
#undef Error
#define Error(...) Error(__LINE__, __FILE__, __func__, __VA_ARGS__)
#undef FatalError
#define FatalError(...) FatalError(__LINE__, __FILE__, __func__, __VA_ARGS__)
#undef Verify
#define Verify(cond, ...) Verify(cond, __LINE__, __FILE__, __func__, __VA_ARGS__)
#undef Assert
#define Assert(cond, ...) Assert(cond, __LINE__, __FILE__, __func__, __VA_ARGS__)
}
#endif

126
ScopEngine/Runtime/Includes/Core/Logs.inl git.filemode.normal_file
View File

@@ -0,0 +1,126 @@
#pragma once
#include <Core/Logs.h>
#include <Core/Format.h>
#include <iostream>
namespace Scop
{
template<typename... Args>
void DebugLog(unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args)
{
using namespace std::literals;
try
{
std::stringstream ss;
ss << Format(message, args...);
Logs::Report(LogType::Debug, line, file, function, ss.str());
}
catch(const std::exception& e)
{
Logs::Report(LogType::Error, "formatter exception catched in the log printer : "s + e.what());
}
}
template<typename... Args>
void Error(unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args)
{
using namespace std::literals;
try
{
std::stringstream ss;
ss << Format(message, args...);
Logs::Report(LogType::Error, line, file, function, ss.str());
}
catch(const std::exception& e)
{
Logs::Report(LogType::Error, "formatter exception catched in the log printer : "s + e.what());
}
}
template<typename... Args>
void Warning(unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args)
{
using namespace std::literals;
try
{
std::stringstream ss;
ss << Format(message, args...);
Logs::Report(LogType::Warning, line, file, function, ss.str());
}
catch(const std::exception& e)
{
Logs::Report(LogType::Error, "formatter exception catched in the log printer : "s + e.what());
}
}
template<typename... Args>
void Message(unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args)
{
using namespace std::literals;
try
{
std::stringstream ss;
ss << Format(message, args...);
Logs::Report(LogType::Message, line, file, function, ss.str());
}
catch(const std::exception& e)
{
Logs::Report(LogType::Error, "formatter exception catched in the log printer : "s + e.what());
}
}
template<typename... Args>
void FatalError(unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args)
{
using namespace std::literals;
try
{
std::stringstream ss;
ss << Format(message, args...);
Logs::Report(LogType::FatalError, line, file, function, ss.str());
}
catch(const std::exception& e)
{
Logs::Report(LogType::Error, "formatter exception catched in the log printer : "s + e.what());
}
}
template<typename... Args>
void Verify(bool cond, unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args)
{
using namespace std::literals;
if(cond)
return;
try
{
std::stringstream ss;
ss << Format("Verification failed : %", message, args...);
Logs::Report(LogType::FatalError, line, file, function, ss.str());
}
catch(const std::exception& e)
{
Logs::Report(LogType::Error, "formatter exception catched in the log printer : "s + e.what());
}
}
#if defined(DEBUG)
template<typename... Args>
void Assert(bool cond, unsigned int line, std::string_view file, std::string_view function, std::string message, const Args&... args)
{
using namespace std::literals;
if(cond)
return;
try
{
std::stringstream ss;
ss << Format("Assertion failed : %", message, args...);
Logs::Report(LogType::FatalError, line, file, function, ss.str());
}
catch(const std::exception& e)
{
Logs::Report(LogType::Error, "formatter exception catched in the log printer : "s + e.what());
}
}
#endif
}

View File

@@ -0,0 +1,49 @@
#ifndef __SCOP_NATIVE_SCRIPT__
#define __SCOP_NATIVE_SCRIPT__
#include <functional>
#include <Core/Script.h>
namespace Scop
{
class NativeActorScript : public ActorScript
{
public:
NativeActorScript(std::function<void(NonOwningPtr<class Actor>)> on_init, std::function<void(NonOwningPtr<class Scene>, NonOwningPtr<class Actor>, class Inputs&, float)> on_update, std::function<void(NonOwningPtr<class Actor>)> on_quit)
: f_on_init(std::move(on_init)), f_on_update(std::move(on_update)), f_on_quit(std::move(on_quit))
{}
inline void OnInit(NonOwningPtr<class Actor> actor) override { if(f_on_init) f_on_init(actor); }
inline void OnUpdate(NonOwningPtr<class Scene> scene, NonOwningPtr<class Actor> actor, class Inputs& input, float delta) override { if(f_on_update) f_on_update(scene, actor, input, delta); }
inline void OnQuit(NonOwningPtr<class Actor> actor) override { if(f_on_quit) f_on_quit(actor); }
~NativeActorScript() = default;
private:
std::function<void(NonOwningPtr<class Actor>)> f_on_init;
std::function<void(NonOwningPtr<class Scene>, NonOwningPtr<class Actor>, class Inputs&, float)> f_on_update;
std::function<void(NonOwningPtr<class Actor>)> f_on_quit;
};
class NativeSpriteScript : public SpriteScript
{
public:
NativeSpriteScript(std::function<void(NonOwningPtr<class Sprite>)> on_init, std::function<void(NonOwningPtr<class Scene>, NonOwningPtr<class Sprite>, class Inputs&, float)> on_update, std::function<void(NonOwningPtr<class Sprite>)> on_quit)
: f_on_init(std::move(on_init)), f_on_update(std::move(on_update)), f_on_quit(std::move(on_quit))
{}
inline void OnInit(NonOwningPtr<class Sprite> sprite) override { if(f_on_init) f_on_init(sprite); }
inline void OnUpdate(NonOwningPtr<class Scene> scene, NonOwningPtr<class Sprite> sprite, class Inputs& input, float delta) override { if(f_on_update) f_on_update(scene, sprite, input, delta); }
inline void OnQuit(NonOwningPtr<class Sprite> sprite) override { if(f_on_quit) f_on_quit(sprite); }
~NativeSpriteScript() = default;
private:
std::function<void(NonOwningPtr<class Sprite>)> f_on_init;
std::function<void(NonOwningPtr<class Scene>, NonOwningPtr<class Sprite>, class Inputs&, float)> f_on_update;
std::function<void(NonOwningPtr<class Sprite>)> f_on_quit;
};
}
#endif

33
ScopEngine/Runtime/Includes/Core/Script.h git.filemode.normal_file
View File

@@ -0,0 +1,33 @@
#ifndef __SCOP_SCRIPT__
#define __SCOP_SCRIPT__
#include <Utils/NonOwningPtr.h>
namespace Scop
{
class ActorScript
{
public:
ActorScript() = default;
virtual void OnInit(NonOwningPtr<class Actor> actor) = 0;
virtual void OnUpdate(NonOwningPtr<class Scene> scene, NonOwningPtr<class Actor> actor, class Inputs& input, float delta) = 0;
virtual void OnQuit(NonOwningPtr<class Actor> actor) = 0;
virtual ~ActorScript() = default;
};
class SpriteScript
{
public:
SpriteScript() = default;
virtual void OnInit(NonOwningPtr<class Sprite> sprite) = 0;
virtual void OnUpdate(NonOwningPtr<class Scene> scene, NonOwningPtr<class Sprite> sprite, class Inputs& input, float delta) = 0;
virtual void OnQuit(NonOwningPtr<class Sprite> sprite) = 0;
virtual ~SpriteScript() = default;
};
}
#endif

View File

@@ -0,0 +1,39 @@
#ifndef __SCOP_IMGUI_RENDERER__
#define __SCOP_IMGUI_RENDERER__
#include <vector>
#include <kvf.h>
#include <Renderer/RenderCore.h>
#include <Renderer/Renderer.h>
#include <Utils/NonOwningPtr.h>
namespace Scop
{
class ImGuiRenderer
{
public:
ImGuiRenderer(NonOwningPtr<Renderer> renderer);
void Init(class Inputs& inputs);
void Destroy();
bool BeginFrame();
void DisplayRenderStatistics();
void EndFrame();
~ImGuiRenderer() = default;
private:
void SetTheme();
void CreateFramebuffers();
private:
std::vector<VkFramebuffer> m_framebuffers;
VkRenderPass m_renderpass = VK_NULL_HANDLE;
VkDescriptorPool m_pool = VK_NULL_HANDLE;
NonOwningPtr<Renderer> p_renderer;
};
}
#endif

45
ScopEngine/Runtime/Includes/Graphics/Actor.h git.filemode.normal_file
View File

@@ -0,0 +1,45 @@
#ifndef __SCOP_RENDERER_ACTOR__
#define __SCOP_RENDERER_ACTOR__
#include <Maths/Vec3.h>
#include <Maths/Vec4.h>
#include <Maths/Quaternions.h>
#include <Core/Script.h>
#include <Graphics/Model.h>
namespace Scop
{
class Actor
{
public:
Actor();
Actor(Model model);
inline void AttachScript(std::shared_ptr<ActorScript> script) { p_script = script; }
void Update(NonOwningPtr<class Scene> scene, class Inputs& input, float timestep);
inline void SetColor(Vec4f color) noexcept { m_color = color; }
inline void SetPosition(Vec3f position) noexcept { m_position = position; }
inline void SetScale(Vec3f scale) noexcept { m_scale = scale; }
inline void SetOrientation(Quatf orientation) noexcept { m_orientation = orientation; }
[[nodiscard]] inline const Vec4f& GetColor() const noexcept { return m_color; }
[[nodiscard]] inline const Vec3f& GetPosition() const noexcept { return m_position; }
[[nodiscard]] inline const Vec3f& GetScale() const noexcept { return m_scale; }
[[nodiscard]] inline const Quatf& GetOrientation() const noexcept { return m_orientation; }
[[nodiscard]] inline const Model& GetModel() const noexcept { return m_model; }
[[nodiscard]] inline Model& GetModelRef() noexcept { return m_model; }
~Actor();
private:
Model m_model;
Quatf m_orientation = Quatf::Identity();
Vec4f m_color = Vec4f{ 1.0f, 1.0f, 1.0f, 1.0f };
Vec3f m_position = Vec3f{ 0.0f, 0.0f, 0.0f };
Vec3f m_scale = Vec3f{ 1.0f, 1.0f, 1.0f };
std::shared_ptr<ActorScript> p_script;
};
}
#endif

View File

@@ -0,0 +1,30 @@
#ifndef __SCOP_CAMERAS_BASE__
#define __SCOP_CAMERAS_BASE__
#include <Maths/Mat4.h>
namespace Scop
{
class BaseCamera
{
public:
BaseCamera() = default;
virtual void Update(class Inputs& input, float aspect, float timestep) {};
[[nodiscard]] inline const Mat4f& GetView() const noexcept { return m_view; }
[[nodiscard]] inline const Mat4f& GetProj() const noexcept { return m_proj; }
[[nodiscard]] virtual const Vec3f& GetPosition() const noexcept = 0;
virtual constexpr std::string GetCameraType() = 0;
virtual ~BaseCamera() = default;
protected:
Mat4f m_view;
Mat4f m_proj;
};
}
#endif

View File

@@ -0,0 +1,46 @@
#ifndef __SCOP_CAMERAS_FIRST_PERSON_3D__
#define __SCOP_CAMERAS_FIRST_PERSON_3D__
#include <Graphics/Cameras/Base.h>
#include <Maths/Vec3.h>
namespace Scop
{
class FirstPerson3D : public BaseCamera
{
public:
FirstPerson3D();
FirstPerson3D(Vec3f position, float fov = 90.0f);
void Update(class Inputs& input, float aspect, float timestep) override;
[[nodiscard]] inline constexpr std::string GetCameraType() override { return "FirstPerson3D"; }
[[nodiscard]] const Vec3f& GetPosition() const noexcept override { return m_position; }
~FirstPerson3D() = default;
private:
void UpdateView();
private:
const Vec3f m_up;
Vec3f m_position;
Vec3f m_left;
Vec3f m_forward;
Vec3f m_target;
Vec3f m_direction;
Vec3f m_mov;
float m_theta = 0.0;
float m_phi = 0.0;
const float m_speed = 50.0f;
const float m_sensivity = 0.7f;
float m_speed_factor = 1.0f;
float m_fov = 90.0f;
bool m_inputs_blocked = false;
};
}
#endif

View File

@@ -0,0 +1,14 @@
#ifndef __SCOP_BMP_LOADER__
#define __SCOP_BMP_LOADER__
#include <filesystem>
#include <Maths/Vec2.h>
#include <Utils/Buffer.h>
namespace Scop
{
CPUBuffer LoadBMPFile(const std::filesystem::path& path, Vec2ui32& dimensions);
}
#endif

View File

@@ -0,0 +1,72 @@
#ifndef __SCOP_OBJ_LOADER__
#define __SCOP_OBJ_LOADER__
#include <map>
#include <set>
#include <string>
#include <vector>
#include <cstdint>
#include <optional>
#include <filesystem>
#include <Maths/Vec2.h>
#include <Maths/Vec3.h>
#include <Maths/Vec4.h>
namespace Scop
{
struct ObjData
{
struct FaceVertex
{
FaceVertex() : v(-1), t(-1), n(-1) {}
std::int32_t v;
std::int32_t t;
std::int32_t n;
inline bool operator<(const FaceVertex& rhs) const
{
return (v < rhs.v) || (v == rhs.v && t < rhs.t ) || (v == rhs.v && t == rhs.t && n < rhs.n);
}
inline bool operator==(const FaceVertex& rhs) const
{
return (v == rhs.v && t == rhs.t && n == rhs.n);
}
};
using FaceList = std::pair<std::vector<FaceVertex>, std::vector<std::uint32_t>>;
std::vector<Vec4f> color;
std::vector<Vec3f> vertex;
std::vector<Vec3f> normal;
std::vector<Vec2f> tex_coord;
std::map<std::string, FaceList> faces;
};
struct ObjModel
{
std::vector<Vec4f> color;
std::vector<Vec3f> vertex;
std::vector<Vec3f> normal;
std::vector<Vec2f> tex_coord;
std::map<std::string, std::vector<std::uint32_t>> faces;
};
std::optional<ObjData> LoadObjFromFile(const std::filesystem::path& path);
void TesselateObjData(ObjData& data);
ObjModel ConvertObjDataToObjModel(const ObjData& data);
template<typename T>
inline std::istream& operator>>(std::istream& in, std::vector<T>& vec);
template<typename T>
inline std::istream& operator>>(std::istream& in, std::set<T>& vec);
inline std::istream& operator>>(std::istream& in, ObjData::FaceVertex& f);
}
#include <Graphics/Loaders/OBJ.inl>
#endif

View File

@@ -0,0 +1,48 @@
#pragma once
#include <Graphics/Loaders/OBJ.h>
namespace Scop
{
template<typename T>
inline std::istream& operator>>(std::istream& in, std::vector<T>& vec)
{
T temp;
if(in >> temp)
vec.push_back(temp);
return in;
}
template<typename T>
inline std::istream& operator>>(std::istream& in, std::set<T>& vec)
{
T temp;
if(in >> temp)
vec.insert(temp);
return in;
}
inline std::istream& operator>>(std::istream& in, ObjData::FaceVertex& f)
{
std::int32_t val;
if(in >> f.v)
{
if(in.peek() == '/')
{
in.get();
in >> f.t;
in.clear();
if(in.peek() == '/')
{
in.get();
in >> f.n;
in.clear();
}
}
in.clear();
f.v--;
f.t--;
f.n--;
}
return in;
}
}

View File

@@ -0,0 +1,81 @@
#ifndef __SCOP_RENDERER_MATERIAL__
#define __SCOP_RENDERER_MATERIAL__
#include <memory>
#include <Core/EventBus.h>
#include <Renderer/Image.h>
#include <Renderer/Buffer.h>
#include <Renderer/Descriptor.h>
namespace Scop
{
struct MaterialTextures
{
std::shared_ptr<Texture> albedo;
};
struct MaterialData
{
float dissolve_texture_factor = 1.0f;
float dissolve_black_white_colors_factor = 1.0f;
float dissolve_normals_colors_factor = 0.0f;
std::uint8_t __padding[4];
};
class Material
{
friend class Model;
public:
Material() { m_data_buffer.Init(sizeof(m_data)); SetupEventListener(); }
Material(const MaterialTextures& textures) : m_textures(textures) { m_data_buffer.Init(sizeof(m_data)); SetupEventListener(); }
inline void SetMaterialData(const MaterialData& data) noexcept { m_data = data; }
~Material() { m_data_buffer.Destroy(); }
private:
[[nodiscard]] inline bool IsSetInit() const noexcept { return m_set.IsInit(); }
[[nodiscard]] inline VkDescriptorSet GetSet(std::size_t frame_index) const noexcept { return m_set.GetSet(frame_index); }
inline void SetupEventListener()
{
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
if(event.What() == Event::FrameBeginEventCode)
m_have_been_updated_this_frame = false;
};
EventBus::RegisterListener({ functor, "__ScopMaterial" + std::to_string(reinterpret_cast<std::uintptr_t>(this)) });
}
inline void UpdateDescriptorSet(const DescriptorSet& set)
{
m_set = set.Duplicate();
}
inline void Bind(std::size_t frame_index, VkCommandBuffer cmd)
{
if(m_have_been_updated_this_frame)
return;
m_set.SetImage(frame_index, 0, *m_textures.albedo);
m_set.SetUniformBuffer(frame_index, 1, m_data_buffer.Get(frame_index));
m_set.Update(frame_index, cmd);
static CPUBuffer buffer(sizeof(MaterialData));
std::memcpy(buffer.GetData(), &m_data, buffer.GetSize());
m_data_buffer.SetData(buffer, frame_index);
m_have_been_updated_this_frame = true;
}
private:
UniformBuffer m_data_buffer;
MaterialTextures m_textures;
MaterialData m_data;
DescriptorSet m_set;
bool m_have_been_updated_this_frame = false;
};
}
#endif

57
ScopEngine/Runtime/Includes/Graphics/Mesh.h git.filemode.normal_file
View File

@@ -0,0 +1,57 @@
#ifndef __SCOPE_RENDERER_MESH__
#define __SCOPE_RENDERER_MESH__
#include <vector>
#include <cstdint>
#include <cstring>
#include <Renderer/Vertex.h>
#include <Renderer/Buffer.h>
#include <Utils/Buffer.h>
namespace Scop
{
class Mesh
{
public:
struct SubMesh
{
VertexBuffer vbo;
IndexBuffer ibo;
std::size_t triangle_count = 0;
inline SubMesh(const std::vector<Vertex>& vertices, const std::vector<std::uint32_t>& indices)
{
CPUBuffer vb(vertices.size() * sizeof(Vertex));
std::memcpy(vb.GetData(), vertices.data(), vb.GetSize());
vbo.Init(vb.GetSize());
vbo.SetData(std::move(vb));
CPUBuffer ib(indices.size() * sizeof(std::uint32_t));
std::memcpy(ib.GetData(), indices.data(), ib.GetSize());
ibo.Init(ib.GetSize());
ibo.SetData(std::move(ib));
triangle_count = vertices.size() / 3;
}
};
public:
Mesh() = default;
void Draw(VkCommandBuffer cmd, std::size_t& drawcalls, std::size_t& polygondrawn) const noexcept;
void Draw(VkCommandBuffer cmd, std::size_t& drawcalls, std::size_t& polygondrawn, std::size_t submesh_index) const noexcept;
inline std::size_t GetSubMeshCount() const { return m_sub_meshes.size(); }
inline void AddSubMesh(SubMesh mesh) { m_sub_meshes.emplace_back(std::move(mesh)); }
[[nodiscard]] inline SubMesh& GetSubMesh(std::size_t index) { return m_sub_meshes.at(index); }
~Mesh();
private:
std::vector<SubMesh> m_sub_meshes;
};
}
#endif

View File

@@ -0,0 +1,21 @@
#ifndef __SCOP_MESH_FACTORY__
#define __SCOP_MESH_FACTORY__
#include <memory>
#include <Maths/Vec2.h>
#include <Maths/Vec3.h>
namespace Scop
{
std::shared_ptr<class Mesh> CreateQuad();
std::shared_ptr<class Mesh> CreateQuad(float x, float y, float width, float height);
std::shared_ptr<class Mesh> CreateQuad(const Vec2f& position, const Vec2f& size);
std::shared_ptr<class Mesh> CreateCube();
std::shared_ptr<class Mesh> CreatePyramid();
std::shared_ptr<class Mesh> CreateSphere(std::uint32_t x_segments = 32, std::uint32_t y_segments = 32);
std::shared_ptr<class Mesh> CreateCapsule(float radius = 0.5f, float mid_height = 2.0f, int radial_degments = 64, int rings = 8);
std::shared_ptr<class Mesh> CreatePlane(float width, float height, const Vec3f& normal);
}
#endif

44
ScopEngine/Runtime/Includes/Graphics/Model.h git.filemode.normal_file
View File

@@ -0,0 +1,44 @@
#ifndef __SCOPE_RENDERER_MODEL__
#define __SCOPE_RENDERER_MODEL__
#include <memory>
#include <filesystem>
#include <kvf.h>
#include <Maths/Vec3.h>
#include <Graphics/Mesh.h>
#include <Graphics/Material.h>
namespace Scop
{
// Only static meshes for now
class Model
{
friend Model LoadModelFromObjFile(std::filesystem::path path) noexcept;
public:
Model() = default;
Model(std::shared_ptr<Mesh> mesh);
inline void SetMaterial(std::shared_ptr<Material> material, std::size_t mesh_index) { m_materials[mesh_index] = material; }
inline std::size_t GetSubMeshCount() const { return p_mesh->GetSubMeshCount(); }
[[nodiscard]] inline std::shared_ptr<Material> GetMaterial(std::size_t mesh_index) { return m_materials[mesh_index]; }
[[nodiscard]] inline std::vector<std::shared_ptr<Material>>& GetAllMaterials() { return m_materials; }
[[nodiscard]] inline Vec3f GetCenter() const noexcept { return m_center; }
void Draw(VkCommandBuffer cmd, const DescriptorSet& matrices_set, const class GraphicPipeline& pipeline, DescriptorSet& set, std::size_t& drawcalls, std::size_t& polygondrawn, std::size_t frame_index) const;
~Model() = default;
private:
Vec3f m_center = { 0.0f, 0.0f, 0.0f };
std::vector<std::shared_ptr<Material>> m_materials;
std::shared_ptr<Mesh> p_mesh;
};
Model LoadModelFromObjFile(std::filesystem::path path) noexcept;
}
#endif

91
ScopEngine/Runtime/Includes/Graphics/Scene.h git.filemode.normal_file
View File

@@ -0,0 +1,91 @@
#ifndef __SCOP_SCENE__
#define __SCOP_SCENE__
#include <memory>
#include <string>
#include <string_view>
#include <Utils/NonOwningPtr.h>
#include <Graphics/Actor.h>
#include <Graphics/Sprite.h>
#include <Renderer/Buffer.h>
#include <Renderer/Descriptor.h>
#include <Renderer/RenderCore.h>
#include <Graphics/Cameras/Base.h>
#include <Renderer/Pipelines/Shader.h>
#include <Renderer/Pipelines/Graphics.h>
namespace Scop
{
struct SceneDescriptor
{
std::shared_ptr<Shader> fragment_shader;
std::shared_ptr<BaseCamera> camera;
bool render_3D_enabled = true;
bool render_2D_enabled = true;
bool render_skybox_enabled = true;
};
class Scene
{
friend class ScopEngine;
public:
struct ForwardData
{
std::shared_ptr<DescriptorSet> matrices_set;
std::shared_ptr<DescriptorSet> albedo_set;
std::shared_ptr<UniformBuffer> matrices_buffer;
bool wireframe = false;
};
public:
Scene(std::string_view name, SceneDescriptor desc);
Scene(std::string_view name, SceneDescriptor desc, NonOwningPtr<Scene> parent);
Actor& CreateActor(Model model) noexcept;
Actor& CreateActor(std::string_view name, Model model);
Sprite& CreateSprite(std::shared_ptr<Texture> texture) noexcept;
Sprite& CreateSprite(std::string_view name, std::shared_ptr<Texture> texture);
[[nodiscard]] inline Scene& AddChildScene(std::string_view name, SceneDescriptor desc) { return m_scene_children.emplace_back(name, std::move(desc), this); }
inline void AddSkybox(std::shared_ptr<CubeTexture> cubemap) { p_skybox = cubemap; }
void SwitchToChild(std::string_view name) const noexcept;
void SwitchToParent() const noexcept;
[[nodiscard]] inline ForwardData& GetForwardData() noexcept { return m_forward; }
[[nodiscard]] inline const std::vector<std::shared_ptr<Actor>>& GetActors() const noexcept { return m_actors; }
[[nodiscard]] inline const std::vector<std::shared_ptr<Sprite>>& GetSprites() const noexcept { return m_sprites; }
[[nodiscard]] inline const std::string& GetName() const noexcept { return m_name; }
[[nodiscard]] inline GraphicPipeline& GetPipeline() noexcept { return m_pipeline; }
[[nodiscard]] inline std::shared_ptr<BaseCamera> GetCamera() const { return m_descriptor.camera; }
[[nodiscard]] inline DepthImage& GetDepth() noexcept { return m_depth; }
[[nodiscard]] inline std::shared_ptr<Shader> GetFragmentShader() const { return m_descriptor.fragment_shader; }
[[nodiscard]] inline std::shared_ptr<CubeTexture> GetSkybox() const { return p_skybox; }
[[nodiscard]] inline const SceneDescriptor& GetDescription() const noexcept { return m_descriptor; }
~Scene() = default;
private:
Scene() = default;
void Init(NonOwningPtr<class Renderer> renderer);
void Update(class Inputs& input, float delta, float aspect);
void Destroy();
private:
GraphicPipeline m_pipeline;
ForwardData m_forward;
DepthImage m_depth;
SceneDescriptor m_descriptor;
std::shared_ptr<CubeTexture> p_skybox;
std::vector<std::shared_ptr<Actor>> m_actors;
std::vector<std::shared_ptr<Sprite>> m_sprites;
std::vector<Scene> m_scene_children;
std::string m_name;
NonOwningPtr<Scene> p_parent;
};
}
#endif

63
ScopEngine/Runtime/Includes/Graphics/Sprite.h git.filemode.normal_file
View File

@@ -0,0 +1,63 @@
#ifndef __SCOP_RENDERER_SPRITE__
#define __SCOP_RENDERER_SPRITE__
#include <memory>
#include <Maths/Vec2.h>
#include <Maths/Vec4.h>
#include <Core/Script.h>
#include <Graphics/Mesh.h>
#include <Renderer/Descriptor.h>
#include <Renderer/Image.h>
namespace Scop
{
class Sprite
{
friend class Render2DPass;
public:
Sprite(std::shared_ptr<Texture> texture);
inline void AttachScript(std::shared_ptr<SpriteScript> script) { p_script = script; }
void Update(NonOwningPtr<class Scene> scene, class Inputs& input, float timestep);
inline void SetColor(Vec4f color) noexcept { m_color = color; }
inline void SetPosition(Vec2ui position) noexcept { m_position = position; }
inline void SetScale(Vec2f scale) noexcept { m_scale = scale; }
[[nodiscard]] inline const Vec4f& GetColor() const noexcept { return m_color; }
[[nodiscard]] inline const Vec2ui& GetPosition() const noexcept { return m_position; }
[[nodiscard]] inline const Vec2f& GetScale() const noexcept { return m_scale; }
[[nodiscard]] inline std::shared_ptr<Mesh> GetMesh() const { return p_mesh; }
[[nodiscard]] inline std::shared_ptr<Texture> GetTexture() const { return p_texture; }
~Sprite();
private:
[[nodiscard]] inline bool IsSetInit() const noexcept { return m_set.IsInit(); }
[[nodiscard]] inline VkDescriptorSet GetSet(std::size_t frame_index) const noexcept { return m_set.GetSet(frame_index); }
inline void UpdateDescriptorSet(const DescriptorSet& set)
{
m_set = set.Duplicate();
}
inline void Bind(std::size_t frame_index, VkCommandBuffer cmd)
{
m_set.SetImage(frame_index, 0, *p_texture);
m_set.Update(frame_index, cmd);
}
private:
DescriptorSet m_set;
std::shared_ptr<Texture> p_texture;
std::shared_ptr<class SpriteScript> p_script;
std::shared_ptr<Mesh> p_mesh;
Vec4f m_color = Vec4f{ 1.0f, 1.0f, 1.0f, 1.0f };
Vec2ui m_position = Vec2ui{ 0, 0 };
Vec2f m_scale = Vec2f{ 1.0f, 1.0f };
};
}
#endif

108
ScopEngine/Runtime/Includes/Maths/Angles.h git.filemode.normal_file
View File

@@ -0,0 +1,108 @@
#ifndef __SCOP_ANGLES__
#define __SCOP_ANGLES__
#include <Maths/Enums.h>
#include <utility>
#include <string>
namespace Scop
{
template<typename T> struct EulerAngles;
template<typename T> struct Quat;
template<AngleUnit Unit, typename T>
struct Angle
{
T value;
constexpr Angle() = default;
constexpr Angle(T angle);
template<typename U> constexpr explicit Angle(const Angle<Unit, U>& Angle);
template<AngleUnit FromUnit> constexpr Angle(const Angle<FromUnit, T>& angle);
constexpr Angle(const Angle&) = default;
constexpr Angle(Angle&&) noexcept = default;
~Angle() = default;
constexpr bool ApproxEqual(const Angle& angle) const;
constexpr bool ApproxEqual(const Angle& angle, T max_difference) const;
T GetCos() const;
T GetSin() const;
std::pair<T, T> GetSinCos() const;
T GetTan() const;
constexpr Angle& Normalize();
template<AngleUnit ToUnit> T To() const;
template<AngleUnit ToUnit> Angle<ToUnit, T> ToAngle() const;
constexpr T ToDegrees() const;
constexpr Angle<AngleUnit::Degree, T> ToDegreeAngle() const;
EulerAngles<T> ToEulerAngles() const;
Quat<T> ToQuat() const;
constexpr T ToRadians() const;
constexpr Angle<AngleUnit::Radian, T> ToRadianAngle() const;
std::string ToString() const;
constexpr T ToTurns() const;
constexpr Angle<AngleUnit::Turn, T> ToTurnAngle() const;
constexpr Angle& operator=(const Angle&) = default;
constexpr Angle& operator=(Angle&&) noexcept = default;
constexpr Angle operator+() const;
constexpr Angle operator-() const;
constexpr Angle operator+(Angle other) const;
constexpr Angle operator-(Angle other) const;
constexpr Angle operator*(T scalar) const;
constexpr Angle operator/(T divider) const;
constexpr Angle& operator+=(Angle other);
constexpr Angle& operator-=(Angle other);
constexpr Angle& operator*=(T scalar);
constexpr Angle& operator/=(T divider);
constexpr bool operator==(Angle other) const;
constexpr bool operator!=(Angle other) const;
constexpr bool operator<(Angle other) const;
constexpr bool operator<=(Angle other) const;
constexpr bool operator>(Angle other) const;
constexpr bool operator>=(Angle other) const;
static constexpr bool ApproxEqual(const Angle& lhs, const Angle& rhs);
static constexpr bool ApproxEqual(const Angle& lhs, const Angle& rhs, T max_difference);
static constexpr Angle Clamp(Angle angle, Angle min, Angle max);
template<AngleUnit FromUnit> static constexpr Angle From(T value);
static constexpr Angle FromDegrees(T degrees);
static constexpr Angle FromRadians(T radians);
static constexpr Angle FromTurns(T turn);
static constexpr Angle Zero();
};
template<typename T>
using DegreeAngle = Angle<AngleUnit::Degree, T>;
using DegreeAngled = DegreeAngle<double>;
using DegreeAnglef = DegreeAngle<float>;
template<typename T>
using RadianAngle = Angle<AngleUnit::Radian, T>;
using RadianAngled = RadianAngle<double>;
using RadianAnglef = RadianAngle<float>;
template<typename T>
using TurnAngle = Angle<AngleUnit::Turn, T>;
using TurnAngled = TurnAngle<double>;
using TurnAnglef = TurnAngle<float>;
template<AngleUnit Unit, typename T> Angle<Unit, T> operator*(T scale, Angle<Unit, T> angle);
template<AngleUnit Unit, typename T> Angle<Unit, T> operator/(T divider, Angle<Unit, T> angle);
template<AngleUnit Unit, typename T> std::ostream& operator<<(std::ostream& out, Angle<Unit, T> angle);
}
#include <Maths/Angles.inl>
#endif

488
ScopEngine/Runtime/Includes/Maths/Angles.inl git.filemode.normal_file
View File

@@ -0,0 +1,488 @@
#pragma once
#include <Maths/Angles.h>
#include <algorithm>
#include <sstream>
#include <Maths/Constants.h>
#include <Maths/MathsUtils.h>
namespace Scop
{
namespace Internal
{
template<AngleUnit From, AngleUnit To> struct AngleConversion;
template<AngleUnit Unit>
struct AngleConversion<Unit, Unit>
{
template<typename T>
static constexpr T Convert(T angle)
{
return angle;
}
};
template<>
struct AngleConversion<AngleUnit::Degree, AngleUnit::Radian>
{
template<typename T>
static constexpr T Convert(T angle)
{
return DegreeToRadian(angle);
}
};
template<>
struct AngleConversion<AngleUnit::Degree, AngleUnit::Turn>
{
template<typename T>
static constexpr T Convert(T angle)
{
return angle / T(360);
}
};
template<>
struct AngleConversion<AngleUnit::Radian, AngleUnit::Degree>
{
template<typename T>
static constexpr T Convert(T angle)
{
return RadianToDegree(angle);
}
};
template<>
struct AngleConversion<AngleUnit::Radian, AngleUnit::Turn>
{
template<typename T>
static constexpr T Convert(T angle)
{
return angle / Tau<T>();
}
};
template<>
struct AngleConversion<AngleUnit::Turn, AngleUnit::Degree>
{
template<typename T>
static constexpr T Convert(T angle)
{
return angle * T(360);
}
};
template<>
struct AngleConversion<AngleUnit::Turn, AngleUnit::Radian>
{
template<typename T>
static constexpr T Convert(T angle)
{
return angle * Tau<T>();
}
};
template<AngleUnit Unit> struct AngleUtils;
template<>
struct AngleUtils<AngleUnit::Degree>
{
template<typename T>
static constexpr T GetEpsilon()
{
return T(1e-4);
}
template<typename T>
static constexpr T GetLimit()
{
return 360;
}
template<typename T> static std::ostream& ToString(std::ostream& out, T value)
{
return out << "Angle(" << value << "deg)";
}
};
template<>
struct AngleUtils<AngleUnit::Radian>
{
template<typename T>
static constexpr T GetEpsilon()
{
return T(1e-5);
}
template<typename T>
static constexpr T GetLimit()
{
return Tau<T>();
}
template<typename T>
static std::ostream& ToString(std::ostream& out, T value)
{
return out << "Angle(" << value << "rad)";
}
};
template<>
struct AngleUtils<AngleUnit::Turn>
{
template<typename T>
static constexpr T GetEpsilon()
{
return T(1e-5);
}
template<typename T>
static constexpr T GetLimit()
{
return 1;
}
template<typename T>
static std::ostream& ToString(std::ostream& out, T value)
{
return out << "Angle(" << value << "turn)";
}
};
template<typename T>
void SinCos(T x, T* sin, T* cos)
{
double s, c;
::sincos(x, &s, &c);
*sin = static_cast<T>(s);
*cos = static_cast<T>(c);
}
template<>
inline void SinCos(float x, float* s, float* c)
{
::sincosf(x, s, c);
}
template<>
inline void SinCos(long double x, long double* s, long double* c)
{
::sincosl(x, s, c);
}
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T>::Angle(T angle) :
value(angle)
{
}
template<AngleUnit Unit, typename T>
template<typename U>
constexpr Angle<Unit, T>::Angle(const Angle<Unit, U>& angle) :
value(static_cast<T>(angle.value))
{
}
template<AngleUnit Unit, typename T>
template<AngleUnit FromUnit>
constexpr Angle<Unit, T>::Angle(const Angle<FromUnit, T>& angle) :
value(Internal::AngleConversion<FromUnit, Unit>::Convert(angle.value))
{
}
template<AngleUnit Unit, typename T>
constexpr bool Angle<Unit, T>::ApproxEqual(const Angle& angle) const
{
return ApproxEqual(angle, Internal::AngleUtils<Unit>::template GetEpsilon<T>());
}
template<AngleUnit Unit, typename T>
constexpr bool Angle<Unit, T>::ApproxEqual(const Angle& angle, T maxDifference) const
{
return NumberEquals(value, angle.value, maxDifference);
}
template<AngleUnit Unit, typename T>
T Angle<Unit, T>::GetCos() const
{
return std::cos(ToRadians());
}
template<AngleUnit Unit, typename T>
T Angle<Unit, T>::GetSin() const
{
return std::sin(ToRadians());
}
template<AngleUnit Unit, typename T>
std::pair<T, T> Angle<Unit, T>::GetSinCos() const
{
T sin, cos;
Internal::SinCos<T>(ToRadians(), &sin, &cos);
return std::make_pair(sin, cos);
}
template<AngleUnit Unit, typename T>
T Angle<Unit, T>::GetTan() const
{
return std::tan(ToRadians());
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T>& Angle<Unit, T>::Normalize()
{
constexpr T limit = Internal::AngleUtils<Unit>::template GetLimit<T>();
constexpr T halfLimit = limit / T(2);
value = Mod(value + halfLimit, limit);
if (value < T(0))
value += limit;
value -= halfLimit;
return *this;
}
template<AngleUnit Unit, typename T>
template<AngleUnit ToUnit>
T Angle<Unit, T>::To() const
{
return Internal::AngleConversion<Unit, ToUnit>::Convert(value);
}
template<AngleUnit Unit, typename T>
template<AngleUnit ToUnit>
Angle<ToUnit, T> Angle<Unit, T>::ToAngle() const
{
return Angle<ToUnit, T>(To<ToUnit>());
}
template<AngleUnit Unit, typename T>
constexpr T Angle<Unit, T>::ToDegrees() const
{
return To<AngleUnit::Degree>();
}
template<AngleUnit Unit, typename T>
constexpr Angle<AngleUnit::Degree, T> Angle<Unit, T>::ToDegreeAngle() const
{
return ToAngle<AngleUnit::Degree>();
}
template<AngleUnit Unit, typename T>
EulerAngles<T> Angle<Unit, T>::ToEulerAngles() const
{
return EulerAngles<T>(0, 0, ToDegrees());
}
template<AngleUnit Unit, typename T>
Quat<T> Angle<Unit, T>::ToQuat() const
{
auto halfAngle = Angle(*this) / 2.f;
auto sincos = halfAngle.GetSinCos();
return Quat<T>(sincos.second, 0, 0, sincos.first);
}
template<AngleUnit Unit, typename T>
constexpr T Angle<Unit, T>::ToRadians() const
{
return To<AngleUnit::Radian>();
}
template<AngleUnit Unit, typename T>
constexpr Angle<AngleUnit::Radian, T> Angle<Unit, T>::ToRadianAngle() const
{
return ToAngle<AngleUnit::Radian>();
}
template<AngleUnit Unit, typename T>
std::string Angle<Unit, T>::ToString() const
{
std::ostringstream oss;
Internal::AngleUtils<Unit>::ToString(oss, value);
return oss.str();
}
template<AngleUnit Unit, typename T>
constexpr T Angle<Unit, T>::ToTurns() const
{
return To<AngleUnit::Turn>(value);
}
template<AngleUnit Unit, typename T>
constexpr Angle<AngleUnit::Turn, T> Angle<Unit, T>::ToTurnAngle() const
{
return ToAngle<AngleUnit::Turn>();
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::operator+() const
{
return *this;
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::operator-() const
{
return Angle(-value);
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::operator+(Angle other) const
{
return Angle(value + other.value);
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::operator-(Angle other) const
{
return Angle(value - other.value);
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::operator*(T scalar) const
{
return Angle(value * scalar);
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::operator/(T divider) const
{
return Angle(value / divider);
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T>& Angle<Unit, T>::operator+=(Angle other)
{
value += other.value;
return *this;
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T>& Angle<Unit, T>::operator-=(Angle other)
{
value -= other.value;
return *this;
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T>& Angle<Unit, T>::operator*=(T scalar)
{
value *= scalar;
return *this;
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T>& Angle<Unit, T>::operator/=(T divider)
{
value /= divider;
return *this;
}
template<AngleUnit Unit, typename T>
constexpr bool Angle<Unit, T>::operator==(Angle other) const
{
return value == other.value;
}
template<AngleUnit Unit, typename T>
constexpr bool Angle<Unit, T>::operator!=(Angle other) const
{
return value != other.value;
}
template<AngleUnit Unit, typename T>
constexpr bool Angle<Unit, T>::operator<(Angle other) const
{
return value < other.value;
}
template<AngleUnit Unit, typename T>
constexpr bool Angle<Unit, T>::operator<=(Angle other) const
{
return value <= other.value;
}
template<AngleUnit Unit, typename T>
constexpr bool Angle<Unit, T>::operator>(Angle other) const
{
return value > other.value;
}
template<AngleUnit Unit, typename T>
constexpr bool Angle<Unit, T>::operator>=(Angle other) const
{
return value >= other.value;
}
template<AngleUnit Unit, typename T>
constexpr bool Angle<Unit, T>::ApproxEqual(const Angle& lhs, const Angle& rhs)
{
return lhs.ApproxEqual(rhs);
}
template<AngleUnit Unit, typename T>
constexpr bool Angle<Unit, T>::ApproxEqual(const Angle& lhs, const Angle& rhs, T maxDifference)
{
return lhs.ApproxEqual(rhs, maxDifference);
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::Clamp(Angle angle, Angle min, Angle max)
{
return Angle(std::clamp(angle.value, min.value, max.value));
}
template<AngleUnit Unit, typename T>
template<AngleUnit FromUnit>
constexpr Angle<Unit, T> Angle<Unit, T>::From(T value)
{
return Angle(Internal::AngleConversion<FromUnit, Unit>::Convert(value));
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::FromDegrees(T degrees)
{
return From<AngleUnit::Degree>(degrees);
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::FromRadians(T radians)
{
return From<AngleUnit::Radian>(radians);
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::FromTurns(T turns)
{
return From<AngleUnit::Turn>(turns);
}
template<AngleUnit Unit, typename T>
constexpr Angle<Unit, T> Angle<Unit, T>::Zero()
{
return Angle(0);
}
template<AngleUnit Unit, typename T>
Angle<Unit, T> operator/(T scale, Angle<Unit, T> angle)
{
return Angle<Unit, T>(scale / angle.value);
}
template<AngleUnit Unit, typename T>
std::ostream& operator<<(std::ostream& out, Angle<Unit, T> angle)
{
return Internal::AngleUtils<Unit>::ToString(out, angle.value);
}
template<typename T, AngleUnit Unit>
constexpr Angle<Unit, T> Clamp(Angle<Unit, T> value, T min, T max)
{
return std::max(std::min(value.value, max), min);
}
}

87
ScopEngine/Runtime/Includes/Maths/Constants.h git.filemode.normal_file
View File

@@ -0,0 +1,87 @@
#ifndef __SCOP_MATHS_CONSTANTS__
#define __SCOP_MATHS_CONSTANTS__
#include <climits>
#include <limits>
#include <type_traits>
namespace Scop
{
template<typename T> constexpr std::size_t BitCount = CHAR_BIT * sizeof(T);
template<typename T>
struct MathConstants
{
static constexpr T Infinity()
{
static_assert(std::numeric_limits<T>::has_infinity);
return std::numeric_limits<T>::infinity();
}
static constexpr T Max()
{
return std::numeric_limits<T>::max();
}
static constexpr T Min()
{
return std::numeric_limits<T>::min();
}
static constexpr T NaN()
{
static_assert(std::numeric_limits<T>::has_signaling_NaN);
return std::numeric_limits<T>::quiet_NaN();
}
// Math constants
static constexpr T HalfPi()
{
static_assert(std::is_floating_point_v<T>);
return T(1.5707963267948966192313216916398);
}
static constexpr T Pi()
{
static_assert(std::is_floating_point_v<T>);
return T(3.1415926535897932384626433832795);
}
static constexpr T Sqrt2()
{
static_assert(std::is_floating_point_v<T>);
return T(1.4142135623730950488016887242097);
}
static constexpr T Sqrt3()
{
static_assert(std::is_floating_point_v<T>);
return T(1.7320508075688772935274463415059);
}
static constexpr T Sqrt5()
{
static_assert(std::is_floating_point_v<T>);
return T(2.2360679774997896964091736687313);
}
static constexpr T Tau()
{
static_assert(std::is_floating_point_v<T>);
return T(6.2831853071795864769252867665590);
}
};
template<typename T = void> constexpr auto Infinity() { return MathConstants<T>::Infinity(); }
template<typename T = void> constexpr auto MaxValue() { return MathConstants<T>::Max(); }
template<typename T = void> constexpr auto MinValue() { return MathConstants<T>::Min(); }
template<typename T = void> constexpr auto NaN() { return MathConstants<T>::NaN(); }
template<typename T = void> constexpr auto HalfPi() { return MathConstants<T>::HalfPi(); }
template<typename T = void> constexpr auto Pi() { return MathConstants<T>::Pi(); }
template<typename T = void> constexpr auto Sqrt2() { return MathConstants<T>::Sqrt2(); }
template<typename T = void> constexpr auto Sqrt3() { return MathConstants<T>::Sqrt3(); }
template<typename T = void> constexpr auto Sqrt5() { return MathConstants<T>::Sqrt5(); }
template<typename T = void> constexpr auto Tau() { return MathConstants<T>::Tau(); }
}
#endif

20
ScopEngine/Runtime/Includes/Maths/Enums.h git.filemode.normal_file
View File

@@ -0,0 +1,20 @@
#ifndef __SCOPE_MATHS_ENUMS__
#define __SCOPE_MATHS_ENUMS__
#include <cstddef>
namespace Scop
{
enum class AngleUnit
{
Degree = 0,
Radian,
Turn,
EndEnum
};
constexpr std::size_t AngleUnitCount = static_cast<std::size_t>(AngleUnit::EndEnum);
}
#endif

View File

@@ -0,0 +1,57 @@
#ifndef __SCOP_EULER_ANGLES__
#define __SCOP_EULER_ANGLES__
#include <string>
#include <Maths/Angles.h>
namespace Scop
{
template<typename T>
struct EulerAngles
{
constexpr EulerAngles() = default;
constexpr EulerAngles(DegreeAngle<T> P, DegreeAngle<T> Y, DegreeAngle<T> R);
constexpr EulerAngles(const DegreeAngle<T> angles[3]);
template<AngleUnit Unit> constexpr EulerAngles(const Angle<Unit, T>& angle);
constexpr EulerAngles(const Quat<T>& quat);
template<typename U> constexpr explicit EulerAngles(const EulerAngles<U>& angles);
constexpr EulerAngles(const EulerAngles&) = default;
constexpr EulerAngles(EulerAngles&&) = default;
~EulerAngles() = default;
constexpr bool ApproxEqual(const EulerAngles& angles, T maxDifference = std::numeric_limits<T>::epsilon()) const;
constexpr EulerAngles& Normalize();
Quat<T> ToQuat() const;
std::string ToString() const;
constexpr EulerAngles operator+(const EulerAngles& angles) const;
constexpr EulerAngles operator-(const EulerAngles& angles) const;
constexpr EulerAngles& operator=(const EulerAngles&) = default;
constexpr EulerAngles& operator=(EulerAngles&&) = default;
constexpr EulerAngles& operator+=(const EulerAngles& angles);
constexpr EulerAngles& operator-=(const EulerAngles& angles);
constexpr bool operator==(const EulerAngles& angles) const;
constexpr bool operator!=(const EulerAngles& angles) const;
constexpr bool operator<(const EulerAngles& angles) const;
constexpr bool operator<=(const EulerAngles& angles) const;
constexpr bool operator>(const EulerAngles& angles) const;
constexpr bool operator>=(const EulerAngles& angles) const;
static constexpr bool ApproxEqual(const EulerAngles& lhs, const EulerAngles& rhs, T maxDifference = std::numeric_limits<T>::epsilon());
static constexpr EulerAngles Zero();
DegreeAngle<T> pitch, yaw, roll;
};
using EulerAnglesf = EulerAngles<float>;
}
#include <Maths/EulerAngles.inl>
#endif

View File

@@ -0,0 +1,169 @@
#pragma once
#include <Maths/EulerAngles.h>
namespace Scop
{
template<typename T>
constexpr EulerAngles<T>::EulerAngles(DegreeAngle<T> P, DegreeAngle<T> Y, DegreeAngle<T> R) :
pitch(P), yaw(Y), roll(R)
{}
template<typename T>
constexpr EulerAngles<T>::EulerAngles(const DegreeAngle<T> angles[3]) :
EulerAngles(angles[0], angles[1], angles[2])
{}
template<typename T>
template<AngleUnit Unit>
constexpr EulerAngles<T>::EulerAngles(const Angle<Unit, T>& angle) :
EulerAngles(angle.ToEulerAngles())
{}
template<typename T>
constexpr EulerAngles<T>::EulerAngles(const Quat<T>& quat) :
EulerAngles(quat.ToEulerAngles())
{}
template<typename T>
template<typename U>
constexpr EulerAngles<T>::EulerAngles(const EulerAngles<U>& angles) :
pitch(DegreeAngle<T>(angles.pitch)), yaw(DegreeAngle<T>(angles.yaw)), roll(DegreeAngle<T>(angles.roll))
{}
template<typename T>
constexpr bool EulerAngles<T>::ApproxEqual(const EulerAngles& angles, T maxDifference) const
{
return pitch.ApproxEqual(angles.pitch, maxDifference) && yaw.ApproxEqual(angles.yaw, maxDifference) && roll.ApproxEqual(angles.roll, maxDifference);
}
template<typename T>
constexpr EulerAngles<T>& EulerAngles<T>::Normalize()
{
pitch.Normalize();
yaw.Normalize();
roll.Normalize();
return *this;
}
template<typename T>
Quat<T> EulerAngles<T>::ToQuat() const
{
// XYZ
auto [s1, c1] = (yaw / T(2.0)).GetSinCos();
auto [s2, c2] = (roll / T(2.0)).GetSinCos();
auto [s3, c3] = (pitch / T(2.0)).GetSinCos();
return Quat<T>(c1 * c2 * c3 - s1 * s2 * s3,
s1 * s2 * c3 + c1 * c2 * s3,
s1 * c2 * c3 + c1 * s2 * s3,
c1 * s2 * c3 - s1 * c2 * s3);
}
template<typename T>
std::string EulerAngles<T>::ToString() const
{
std::ostringstream ss;
ss << *this;
return ss.str();
}
template<typename T>
constexpr EulerAngles<T> EulerAngles<T>::operator+(const EulerAngles& angles) const
{
return EulerAngles(pitch + angles.pitch, yaw + angles.yaw, roll + angles.roll);
}
template<typename T>
constexpr EulerAngles<T> EulerAngles<T>::operator-(const EulerAngles& angles) const
{
return EulerAngles(pitch - angles.pitch, yaw - angles.yaw, roll - angles.roll);
}
template<typename T>
constexpr EulerAngles<T>& EulerAngles<T>::operator+=(const EulerAngles& angles)
{
pitch += angles.pitch;
yaw += angles.yaw;
roll += angles.roll;
return *this;
}
template<typename T>
constexpr EulerAngles<T>& EulerAngles<T>::operator-=(const EulerAngles& angles)
{
pitch -= angles.pitch;
yaw -= angles.yaw;
roll -= angles.roll;
return *this;
}
template<typename T>
constexpr bool EulerAngles<T>::operator==(const EulerAngles& angles) const
{
return pitch == angles.pitch && yaw == angles.yaw && roll == angles.roll;
}
template<typename T>
constexpr bool EulerAngles<T>::operator!=(const EulerAngles& angles) const
{
return !operator==(angles);
}
template<typename T>
constexpr bool EulerAngles<T>::operator<(const EulerAngles& angles) const
{
if (pitch != angles.pitch)
return pitch < angles.pitch;
if (yaw != angles.yaw)
return yaw < angles.yaw;
return roll < angles.roll;
}
template<typename T>
constexpr bool EulerAngles<T>::operator<=(const EulerAngles& angles) const
{
if (pitch != angles.pitch)
return pitch < angles.pitch;
if (yaw != angles.yaw)
return yaw < angles.yaw;
return roll <= angles.roll;
}
template<typename T>
constexpr bool EulerAngles<T>::operator>(const EulerAngles& angles) const
{
if (pitch != angles.pitch)
return pitch > angles.pitch;
if (yaw != angles.yaw)
return yaw > angles.yaw;
return roll > angles.roll;
}
template<typename T>
constexpr bool EulerAngles<T>::operator>=(const EulerAngles& angles) const
{
if (pitch != angles.pitch)
return pitch > angles.pitch;
if (yaw != angles.yaw)
return yaw > angles.yaw;
return roll >= angles.roll;
}
template<typename T>
constexpr bool EulerAngles<T>::ApproxEqual(const EulerAngles& lhs, const EulerAngles& rhs, T maxDifference)
{
return lhs.ApproxEqual(rhs, maxDifference);
}
template<typename T>
constexpr EulerAngles<T> EulerAngles<T>::Zero()
{
return EulerAngles(0, 0, 0);
}
template<typename T>
std::ostream& operator<<(std::ostream& out, const EulerAngles<T>& angles)
{
return out << "EulerAngles(" << angles.pitch << ", " << angles.yaw << ", " << angles.roll << ')';
}
}

122
ScopEngine/Runtime/Includes/Maths/Mat4.h git.filemode.normal_file
View File

@@ -0,0 +1,122 @@
#ifndef __SCOP_MAT4__
#define __SCOP_MAT4__
#include <cstddef>
#include <limits>
#include <string>
#include <Maths/Angles.h>
namespace Scop
{
template<typename T> struct Vec2;
template<typename T> struct Vec3;
template<typename T> struct Vec4;
template<typename T> struct Quat;
template<typename T>
struct Mat4
{
T m11, m12, m13, m14;
T m21, m22, m23, m24;
T m31, m32, m33, m34;
T m41, m42, m43, m44;
constexpr Mat4() = default;
constexpr Mat4(T r11, T r12, T r13, T r14,
T r21, T r22, T r23, T r24,
T r31, T r32, T r33, T r34,
T r41, T r42, T r43, T r44);
constexpr Mat4(const T matrix[16]);
constexpr Mat4(const Mat4&) = default;
constexpr Mat4(Mat4&&) = default;
constexpr Mat4& ApplyRotation(const Quat<T>& rotation);
constexpr Mat4& ApplyScale(const Vec3<T>& scale);
constexpr Mat4& ApplyTranslation(const Vec3<T>& translation);
constexpr bool ApproxEqual(const Mat4& vec, T max_difference = std::numeric_limits<T>::epsilon()) const;
constexpr Mat4& Concatenate(const Mat4& matrix);
constexpr Mat4& ConcatenateTransform(const Mat4& matrix);
constexpr Vec4<T> GetColumn(std::size_t column) const;
constexpr T GetDeterminant() const;
constexpr T GetDeterminantTransform() const;
constexpr bool GetInverse(Mat4* dest) const;
constexpr bool GetInverseTransform(Mat4* dest) const;
Quat<T> GetRotation() const;
constexpr Vec4<T> GetRow(std::size_t row) const;
constexpr Vec3<T> GetScale() const;
constexpr Vec3<T> GetSquaredScale() const;
constexpr Vec3<T> GetTranslation() const;
constexpr void GetTransposed(Mat4* dest) const;
constexpr bool HasNegativeScale() const;
constexpr bool HasScale() const;
constexpr Mat4& Inverse(bool* succeeded = nullptr);
constexpr Mat4& InverseTransform(bool* succeeded = nullptr);
constexpr bool IsTransformMatrix() const;
constexpr bool IsIdentity() const;
constexpr Mat4& SetRotation(const Quat<T>& rotation);
constexpr Mat4& SetScale(const Vec3<T>& scale);
constexpr Mat4& SetTranslation(const Vec3<T>& translation);
std::string ToString() const;
constexpr Vec2<T> Transform(const Vec2<T>& vector, T z = 0.0, T w = 1.0) const;
constexpr Vec3<T> Transform(const Vec3<T>& vector, T w = 1.0) const;
constexpr Vec4<T> Transform(const Vec4<T>& vector) const;
constexpr Mat4& Transpose();
constexpr T& operator()(std::size_t x, std::size_t y);
constexpr const T& operator()(std::size_t x, std::size_t y) const;
constexpr T& operator[](std::size_t i);
constexpr const T& operator[](std::size_t i) const;
constexpr Mat4& operator=(const Mat4&) = default;
constexpr Mat4& operator=(Mat4&&) = default;
constexpr Mat4 operator*(const Mat4& matrix) const;
constexpr Vec2<T> operator*(const Vec2<T>& vector) const;
constexpr Vec3<T> operator*(const Vec3<T>& vector) const;
constexpr Vec4<T> operator*(const Vec4<T>& vector) const;
constexpr Mat4 operator*(T scalar) const;
constexpr Mat4& operator*=(const Mat4& matrix);
constexpr Mat4& operator*=(T scalar);
constexpr bool operator==(const Mat4& mat) const;
constexpr bool operator!=(const Mat4& mat) const;
static constexpr bool ApproxEqual(const Mat4& lhs, const Mat4& rhs, T max_difference = std::numeric_limits<T>::epsilon());
static constexpr Mat4 Concatenate(const Mat4& left, const Mat4& right);
static constexpr Mat4 ConcatenateTransform(const Mat4& left, const Mat4& right);
static constexpr Mat4 Identity();
static constexpr Mat4 LookAt(const Vec3<T>& eye, const Vec3<T>& target, const Vec3<T>& up = Vec3<T>::Up());
static constexpr Mat4 Ortho(T left, T right, T top, T bottom, T zNear = -1.0, T zFar = 1.0);
static Mat4 Perspective(RadianAngle<T> angle, T ratio, T zNear, T zFar);
static constexpr Mat4 Rotate(const Quat<T>& rotation);
static constexpr Mat4 Scale(const Vec3<T>& scale);
static constexpr Mat4 Translate(const Vec3<T>& translation);
static constexpr Mat4 Transform(const Vec3<T>& translation, const Quat<T>& rotation);
static constexpr Mat4 Transform(const Vec3<T>& translation, const Quat<T>& rotation, const Vec3<T>& scale);
static constexpr Mat4 TransformInverse(const Vec3<T>& translation, const Quat<T>& rotation);
static constexpr Mat4 TransformInverse(const Vec3<T>& translation, const Quat<T>& rotation, const Vec3<T>& scale);
static constexpr Mat4 Zero();
~Mat4() = default;
};
using Mat4d = Mat4<double>;
using Mat4f = Mat4<float>;
}
#include <Maths/Mat4.inl>
#endif

879
ScopEngine/Runtime/Includes/Maths/Mat4.inl git.filemode.normal_file
View File

@@ -0,0 +1,879 @@
#pragma once
#include <Maths/Mat4.h>
#include <Core/Logs.h>
#include <Maths/EulerAngles.h>
#include <Maths/Quaternions.h>
#include <Maths/Vec2.h>
#include <Maths/Vec3.h>
#include <Maths/Vec4.h>
#include <Maths/MathsUtils.h>
#include <cstring>
#include <sstream>
namespace Scop
{
template<typename T>
constexpr Mat4<T>::Mat4(T r11, T r12, T r13, T r14,
T r21, T r22, T r23, T r24,
T r31, T r32, T r33, T r34,
T r41, T r42, T r43, T r44) :
m11(r11), m12(r12), m13(r13), m14(r14),
m21(r21), m22(r22), m23(r23), m24(r24),
m31(r31), m32(r32), m33(r33), m34(r34),
m41(r41), m42(r42), m43(r43), m44(r44)
{}
template<typename T>
constexpr Mat4<T>::Mat4(const T matrix[16]) :
Mat4(matrix[ 0], matrix[ 1], matrix[ 2], matrix[ 3],
matrix[ 4], matrix[ 5], matrix[ 6], matrix[ 7],
matrix[ 8], matrix[ 9], matrix[10], matrix[11],
matrix[12], matrix[13], matrix[14], matrix[15])
{}
template<typename T>
constexpr Mat4<T>& Mat4<T>::ApplyRotation(const Quat<T>& rotation)
{
return Concatenate(Mat4<T>::Rotate(rotation));
}
template<typename T>
constexpr Mat4<T>& Mat4<T>::ApplyScale(const Vec3<T>& scale)
{
m11 *= scale.x;
m12 *= scale.x;
m13 *= scale.x;
m21 *= scale.y;
m22 *= scale.y;
m23 *= scale.y;
m31 *= scale.z;
m32 *= scale.z;
m33 *= scale.z;
return *this;
}
template<typename T>
constexpr Mat4<T>& Mat4<T>::ApplyTranslation(const Vec3<T>& translation)
{
m41 += translation.x;
m42 += translation.y;
m43 += translation.z;
return *this;
}
template<typename T>
constexpr bool Mat4<T>::ApproxEqual(const Mat4& mat, T maxDifference) const
{
for(unsigned int i = 0; i < 16; ++i)
if(!NumberEquals((&m11)[i], (&mat.m11)[i], maxDifference))
return false;
return true;
}
template<typename T>
constexpr Mat4<T>& Mat4<T>::Concatenate(const Mat4& matrix)
{
return operator=(Mat4(
m11 * matrix.m11 + m12 * matrix.m21 + m13 * matrix.m31 + m14 * matrix.m41,
m11 * matrix.m12 + m12 * matrix.m22 + m13 * matrix.m32 + m14 * matrix.m42,
m11 * matrix.m13 + m12 * matrix.m23 + m13 * matrix.m33 + m14 * matrix.m43,
m11 * matrix.m14 + m12 * matrix.m24 + m13 * matrix.m34 + m14 * matrix.m44,
m21 * matrix.m11 + m22 * matrix.m21 + m23 * matrix.m31 + m24 * matrix.m41,
m21 * matrix.m12 + m22 * matrix.m22 + m23 * matrix.m32 + m24 * matrix.m42,
m21 * matrix.m13 + m22 * matrix.m23 + m23 * matrix.m33 + m24 * matrix.m43,
m21 * matrix.m14 + m22 * matrix.m24 + m23 * matrix.m34 + m24 * matrix.m44,
m31 * matrix.m11 + m32 * matrix.m21 + m33 * matrix.m31 + m34 * matrix.m41,
m31 * matrix.m12 + m32 * matrix.m22 + m33 * matrix.m32 + m34 * matrix.m42,
m31 * matrix.m13 + m32 * matrix.m23 + m33 * matrix.m33 + m34 * matrix.m43,
m31 * matrix.m14 + m32 * matrix.m24 + m33 * matrix.m34 + m34 * matrix.m44,
m41 * matrix.m11 + m42 * matrix.m21 + m43 * matrix.m31 + m44 * matrix.m41,
m41 * matrix.m12 + m42 * matrix.m22 + m43 * matrix.m32 + m44 * matrix.m42,
m41 * matrix.m13 + m42 * matrix.m23 + m43 * matrix.m33 + m44 * matrix.m43,
m41 * matrix.m14 + m42 * matrix.m24 + m43 * matrix.m34 + m44 * matrix.m44
));
}
template<typename T>
constexpr Mat4<T>& Mat4<T>::ConcatenateTransform(const Mat4& matrix)
{
return operator=(Mat4(
m11*matrix.m11 + m12*matrix.m21 + m13*matrix.m31,
m11*matrix.m12 + m12*matrix.m22 + m13*matrix.m32,
m11*matrix.m13 + m12*matrix.m23 + m13*matrix.m33,
T(0.0),
m21*matrix.m11 + m22*matrix.m21 + m23*matrix.m31,
m21*matrix.m12 + m22*matrix.m22 + m23*matrix.m32,
m21*matrix.m13 + m22*matrix.m23 + m23*matrix.m33,
T(0.0),
m31*matrix.m11 + m32*matrix.m21 + m33*matrix.m31,
m31*matrix.m12 + m32*matrix.m22 + m33*matrix.m32,
m31*matrix.m13 + m32*matrix.m23 + m33*matrix.m33,
T(0.0),
m41*matrix.m11 + m42*matrix.m21 + m43*matrix.m31 + matrix.m41,
m41*matrix.m12 + m42*matrix.m22 + m43*matrix.m32 + matrix.m42,
m41*matrix.m13 + m42*matrix.m23 + m43*matrix.m33 + matrix.m43,
T(1.0)
));
}
template<typename T>
constexpr Vec4<T> Mat4<T>::GetColumn(std::size_t column) const
{
Assert(column < 4, "column index out of range");
const T* ptr = &m11 + column * 4;
return Vec4<T>(ptr[0], ptr[1], ptr[2], ptr[3]);
}
template<typename T>
constexpr T Mat4<T>::GetDeterminant() const
{
T A = m22*(m33*m44 - m43*m34) - m32*(m23*m44 - m43*m24) + m42*(m23*m34 - m33*m24);
T B = m12*(m33*m44 - m43*m34) - m32*(m13*m44 - m43*m14) + m42*(m13*m34 - m33*m14);
T C = m12*(m23*m44 - m43*m24) - m22*(m13*m44 - m43*m14) + m42*(m13*m24 - m23*m14);
T D = m12*(m23*m34 - m33*m24) - m22*(m13*m34 - m33*m14) + m32*(m13*m24 - m23*m14);
return m11*A - m21*B + m31*C - m41*D;
}
template<typename T>
constexpr T Mat4<T>::GetDeterminantTransform() const
{
T A = m22*m33 - m32*m23;
T B = m12*m33 - m32*m13;
T C = m12*m23 - m22*m13;
return m11*A - m21*B + m31*C;
}
template<typename T>
constexpr bool Mat4<T>::GetInverse(Mat4* dest) const
{
Assert(dest, "destination matrix must be valid");
T det = GetDeterminant();
if(det == T(0.0))
return false;
// http://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix
T inv[16];
inv[0] = m22 * m33 * m44 -
m22 * m34 * m43 -
m32 * m23 * m44 +
m32 * m24 * m43 +
m42 * m23 * m34 -
m42 * m24 * m33;
inv[1] = -m12 * m33 * m44 +
m12 * m34 * m43 +
m32 * m13 * m44 -
m32 * m14 * m43 -
m42 * m13 * m34 +
m42 * m14 * m33;
inv[2] = m12 * m23 * m44 -
m12 * m24 * m43 -
m22 * m13 * m44 +
m22 * m14 * m43 +
m42 * m13 * m24 -
m42 * m14 * m23;
inv[3] = -m12 * m23 * m34 +
m12 * m24 * m33 +
m22 * m13 * m34 -
m22 * m14 * m33 -
m32 * m13 * m24 +
m32 * m14 * m23;
inv[4] = -m21 * m33 * m44 +
m21 * m34 * m43 +
m31 * m23 * m44 -
m31 * m24 * m43 -
m41 * m23 * m34 +
m41 * m24 * m33;
inv[5] = m11 * m33 * m44 -
m11 * m34 * m43 -
m31 * m13 * m44 +
m31 * m14 * m43 +
m41 * m13 * m34 -
m41 * m14 * m33;
inv[6] = -m11 * m23 * m44 +
m11 * m24 * m43 +
m21 * m13 * m44 -
m21 * m14 * m43 -
m41 * m13 * m24 +
m41 * m14 * m23;
inv[7] = m11 * m23 * m34 -
m11 * m24 * m33 -
m21 * m13 * m34 +
m21 * m14 * m33 +
m31 * m13 * m24 -
m31 * m14 * m23;
inv[8] = m21 * m32 * m44 -
m21 * m34 * m42 -
m31 * m22 * m44 +
m31 * m24 * m42 +
m41 * m22 * m34 -
m41 * m24 * m32;
inv[9] = -m11 * m32 * m44 +
m11 * m34 * m42 +
m31 * m12 * m44 -
m31 * m14 * m42 -
m41 * m12 * m34 +
m41 * m14 * m32;
inv[10] = m11 * m22 * m44 -
m11 * m24 * m42 -
m21 * m12 * m44 +
m21 * m14 * m42 +
m41 * m12 * m24 -
m41 * m14 * m22;
inv[11] = -m11 * m22 * m34 +
m11 * m24 * m32 +
m21 * m12 * m34 -
m21 * m14 * m32 -
m31 * m12 * m24 +
m31 * m14 * m22;
inv[12] = -m21 * m32 * m43 +
m21 * m33 * m42 +
m31 * m22 * m43 -
m31 * m23 * m42 -
m41 * m22 * m33 +
m41 * m23 * m32;
inv[13] = m11 * m32 * m43 -
m11 * m33 * m42 -
m31 * m12 * m43 +
m31 * m13 * m42 +
m41 * m12 * m33 -
m41 * m13 * m32;
inv[14] = -m11 * m22 * m43 +
m11 * m23 * m42 +
m21 * m12 * m43 -
m21 * m13 * m42 -
m41 * m12 * m23 +
m41 * m13 * m22;
inv[15] = m11 * m22 * m33 -
m11 * m23 * m32 -
m21 * m12 * m33 +
m21 * m13 * m32 +
m31 * m12 * m23 -
m31 * m13 * m22;
T invDet = T(1.0) / det;
for(unsigned int i = 0; i < 16; ++i)
inv[i] *= invDet;
*dest = inv;
return true;
}
template<typename T>
constexpr bool Mat4<T>::GetInverseTransform(Mat4* dest) const
{
Assert(dest, "destination matrix must be valid");
T det = GetDeterminantTransform();
if(det == T(0.0))
return false;
// http://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix
T inv[16];
inv[0] = m22 * m33 -
m32 * m23;
inv[1] = -m12 * m33 +
m32 * m13;
inv[2] = m12 * m23 -
m22 * m13;
inv[3] = T(0.0);
inv[4] = -m21 * m33 +
m31 * m23;
inv[5] = m11 * m33 -
m31 * m13;
inv[6] = -m11 * m23 +
m21 * m13;
inv[7] = T(0.0);
inv[8] = m21 * m32 -
m31 * m22;
inv[9] = -m11 * m32 +
m31 * m12;
inv[10] = m11 * m22 -
m21 * m12;
inv[11] = T(0.0);
inv[12] = -m21 * m32 * m43 +
m21 * m33 * m42 +
m31 * m22 * m43 -
m31 * m23 * m42 -
m41 * m22 * m33 +
m41 * m23 * m32;
inv[13] = m11 * m32 * m43 -
m11 * m33 * m42 -
m31 * m12 * m43 +
m31 * m13 * m42 +
m41 * m12 * m33 -
m41 * m13 * m32;
inv[14] = -m11 * m22 * m43 +
m11 * m23 * m42 +
m21 * m12 * m43 -
m21 * m13 * m42 -
m41 * m12 * m23 +
m41 * m13 * m22;
T invDet = T(1.0) / det;
for(unsigned int i = 0; i < 16; ++i)
inv[i] *= invDet;
inv[15] = T(1.0);
*dest = inv;
return true;
}
template<typename T>
Quat<T> Mat4<T>::GetRotation() const
{
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuat/
Quat<T> quat;
T trace = m11 + m22 + m33;
if(trace > T(0.0))
{
T s = T(0.5) / std::sqrt(trace + T(1.0));
quat.w = T(0.25) / s;
quat.x = (m23 - m32) * s;
quat.y = (m31 - m13) * s;
quat.z = (m12 - m21) * s;
}
else
{
if(m11 > m22 && m11 > m33)
{
T s = T(2.0) * std::sqrt(T(1.0) + m11 - m22 - m33);
quat.w = (m23 - m32) / s;
quat.x = T(0.25) * s;
quat.y = (m21 + m12) / s;
quat.z = (m31 + m13) / s;
}
else if(m22 > m33)
{
T s = T(2.0) * std::sqrt(T(1.0) + m22 - m11 - m33);
quat.w = (m31 - m13) / s;
quat.x = (m21 + m12) / s;
quat.y = T(0.25) * s;
quat.z = (m32 + m23) / s;
}
else
{
T s = T(2.0) * std::sqrt(T(1.0) + m33 - m11 - m22);
quat.w = (m12 - m21) / s;
quat.x = (m31 + m13) / s;
quat.y = (m32 + m23) / s;
quat.z = T(0.25) * s;
}
}
return quat;
}
template<typename T>
constexpr Vec4<T> Mat4<T>::GetRow(std::size_t row) const
{
Assert(row < 4, "row index out of range");
const T* ptr = &m11;
return Vec4<T>(ptr[row], ptr[row+4], ptr[row+8], ptr[row+12]);
}
template<typename T>
constexpr Vec3<T> Mat4<T>::GetScale() const
{
Vec3<T> squaredScale = GetSquaredScale();
return Vec3<T>(std::sqrt(squaredScale.x), std::sqrt(squaredScale.y), std::sqrt(squaredScale.z));
}
template<typename T>
constexpr Vec3<T> Mat4<T>::GetSquaredScale() const
{
return Vec3<T>(m11 * m11 + m12 * m12 + m13 * m13,
m21 * m21 + m22 * m22 + m23 * m23,
m31 * m31 + m32 * m32 + m33 * m33);
}
template<typename T>
constexpr Vec3<T> Mat4<T>::GetTranslation() const
{
return Vec3<T>(m41, m42, m43);
}
template<typename T>
constexpr void Mat4<T>::GetTransposed(Mat4* dest) const
{
(*dest) = Mat4f(
m11, m21, m31, m41,
m12, m22, m32, m42,
m13, m23, m33, m43,
m14, m24, m34, m44
);
}
template<typename T>
constexpr bool Mat4<T>::HasNegativeScale() const
{
return GetDeterminant() < T(0.0);
}
template<typename T>
constexpr bool Mat4<T>::HasScale() const
{
T t = m11*m11 + m21*m21 + m31*m31;
if(!NumberEquals(t, T(1.0)))
return true;
t = m12*m12 + m22*m22 + m32*m32;
if(!NumberEquals(t, T(1.0)))
return true;
t = m13*m13 + m23*m23 + m33*m33;
if(!NumberEquals(t, T(1.0)))
return true;
return false;
}
template<typename T>
constexpr Mat4<T>& Mat4<T>::Inverse(bool* succeeded)
{
bool result = GetInverse(this);
if(succeeded)
*succeeded = result;
return *this;
}
template<typename T>
constexpr Mat4<T>& Mat4<T>::InverseTransform(bool* succeeded)
{
bool result = GetInverseTransform(this);
if(succeeded)
*succeeded = result;
return *this;
}
template<typename T>
constexpr bool Mat4<T>::IsTransformMatrix() const
{
return NumberEquals(m14, T(0.0)) && NumberEquals(m24, T(0.0)) && NumberEquals(m34, T(0.0)) && NumberEquals(m44, T(1.0));
}
template<typename T>
constexpr bool Mat4<T>::IsIdentity() const
{
return (NumberEquals(m11, T(1.0)) && NumberEquals(m12, T(0.0)) && NumberEquals(m13, T(0.0)) && NumberEquals(m14, T(0.0)) &&
NumberEquals(m21, T(0.0)) && NumberEquals(m22, T(1.0)) && NumberEquals(m23, T(0.0)) && NumberEquals(m24, T(0.0)) &&
NumberEquals(m31, T(0.0)) && NumberEquals(m32, T(0.0)) && NumberEquals(m33, T(1.0)) && NumberEquals(m34, T(0.0)) &&
NumberEquals(m41, T(0.0)) && NumberEquals(m42, T(0.0)) && NumberEquals(m43, T(0.0)) && NumberEquals(m44, T(1.0)));
}
template<typename T>
constexpr Mat4<T>& Mat4<T>::SetRotation(const Quat<T>& rotation)
{
T qw = rotation.w;
T qx = rotation.x;
T qy = rotation.y;
T qz = rotation.z;
T qx2 = qx * qx;
T qy2 = qy * qy;
T qz2 = qz * qz;
m11 = T(1.0) - T(2.0) * qy2 - T(2.0) * qz2;
m21 = T(2.0) * qx * qy - T(2.0) * qz * qw;
m31 = T(2.0) * qx * qz + T(2.0) * qy * qw;
m12 = T(2.0) * qx * qy + T(2.0) * qz * qw;
m22 = T(1.0) - T(2.0) * qx2 - T(2.0) * qz2;
m32 = T(2.0) * qy * qz - T(2.0) * qx * qw;
m13 = T(2.0) * qx * qz - T(2.0) * qy * qw;
m23 = T(2.0) * qy * qz + T(2.0) * qx * qw;
m33 = T(1.0) - T(2.0) * qx2 - T(2.0) * qy2;
return *this;
}
template<typename T>
constexpr Mat4<T>& Mat4<T>::SetScale(const Vec3<T>& scale)
{
m11 = scale.x;
m22 = scale.y;
m33 = scale.z;
return *this;
}
template<typename T>
constexpr Mat4<T>& Mat4<T>::SetTranslation(const Vec3<T>& translation)
{
m41 = translation.x;
m42 = translation.y;
m43 = translation.z;
return *this;
}
template<typename T>
std::string Mat4<T>::ToString() const
{
std::ostringstream ss;
ss << *this;
return ss.str();
}
template<typename T>
constexpr Vec2<T> Mat4<T>::Transform(const Vec2<T>& vector, T z, T w) const
{
return Vec2<T>(m11 * vector.x + m21 * vector.y + m31 * z + m41 * w,
m12 * vector.x + m22 * vector.y + m32 * z + m42 * w);
}
template<typename T>
constexpr Vec3<T> Mat4<T>::Transform(const Vec3<T>& vector, T w) const
{
return Vec3<T>(m11 * vector.x + m21 * vector.y + m31 * vector.z + m41 * w,
m12 * vector.x + m22 * vector.y + m32 * vector.z + m42 * w,
m13 * vector.x + m23 * vector.y + m33 * vector.z + m43 * w);
}
template<typename T>
constexpr Vec4<T> Mat4<T>::Transform(const Vec4<T>& vector) const
{
return Vec4<T>(m11 * vector.x + m21 * vector.y + m31 * vector.z + m41 * vector.w,
m12 * vector.x + m22 * vector.y + m32 * vector.z + m42 * vector.w,
m13 * vector.x + m23 * vector.y + m33 * vector.z + m43 * vector.w,
m14 * vector.x + m24 * vector.y + m34 * vector.z + m44 * vector.w);
}
template<typename T>
constexpr Mat4<T>& Mat4<T>::Transpose()
{
std::swap(m12, m21);
std::swap(m13, m31);
std::swap(m14, m41);
std::swap(m23, m32);
std::swap(m24, m42);
std::swap(m34, m43);
return *this;
}
template<typename T>
constexpr T& Mat4<T>::operator()(std::size_t x, std::size_t y)
{
Assert(x <= 3, "index out of range");
Assert(y <= 3, "index out of range");
return (&m11)[y*4 + x];
}
template<typename T>
constexpr const T& Mat4<T>::operator()(std::size_t x, std::size_t y) const
{
Assert(x <= 3, "index out of range");
Assert(y <= 3, "index out of range");
return (&m11)[y*4+x];
}
template<typename T>
constexpr T& Mat4<T>::operator[](std::size_t i)
{
Assert(i <= 16, "index out of range");
return (&m11)[i];
}
template<typename T>
constexpr const T& Mat4<T>::operator[](std::size_t i) const
{
Assert(i <= 16, "index out of range");
return (&m11)[i];
}
template<typename T>
constexpr Mat4<T> Mat4<T>::operator*(const Mat4& matrix) const
{
Mat4 result(*this);
return result.Concatenate(matrix);
}
template<typename T>
constexpr Vec2<T> Mat4<T>::operator*(const Vec2<T>& vector) const
{
return Transform(vector);
}
template<typename T>
constexpr Vec3<T> Mat4<T>::operator*(const Vec3<T>& vector) const
{
return Transform(vector);
}
template<typename T>
constexpr Vec4<T> Mat4<T>::operator*(const Vec4<T>& vector) const
{
return Transform(vector);
}
template<typename T>
constexpr Mat4<T> Mat4<T>::operator*(T scalar) const
{
Mat4 mat;
for(unsigned int i = 0; i < 16; ++i)
mat[i] = (&m11)[i] * scalar;
return mat;
}
template<typename T>
constexpr Mat4<T>& Mat4<T>::operator*=(const Mat4& matrix)
{
Concatenate(matrix);
return *this;
}
template<typename T>
constexpr Mat4<T>& Mat4<T>::operator*=(T scalar)
{
for(unsigned int i = 0; i < 16; ++i)
(&m11)[i] *= scalar;
return *this;
}
template<typename T>
constexpr bool Mat4<T>::operator==(const Mat4& mat) const
{
for(unsigned int i = 0; i < 16; ++i)
if((&m11)[i] != (&mat.m11)[i])
return false;
return true;
}
template<typename T>
constexpr bool Mat4<T>::operator!=(const Mat4& mat) const
{
return !operator==(mat);
}
template<typename T>
constexpr bool Mat4<T>::ApproxEqual(const Mat4& lhs, const Mat4& rhs, T maxDifference)
{
return lhs.ApproxEqual(rhs, maxDifference);
}
template<typename T>
constexpr Mat4<T> Mat4<T>::Concatenate(const Mat4& left, const Mat4& right)
{
Mat4 matrix(left); // Copy of left-hand side matrix
matrix.Concatenate(right); // Concatenation with right-hand side
return matrix;
}
template<typename T>
constexpr Mat4<T> Mat4<T>::ConcatenateTransform(const Mat4& left, const Mat4& right)
{
Mat4 matrix(left); // Copy of left-hand side matrix
matrix.ConcatenateTransform(right); // Affine concatenation with right-hand side
return matrix;
}
template<typename T>
constexpr Mat4<T> Mat4<T>::Identity()
{
return Mat4(
T(1.0), T(0.0), T(0.0), T(0.0),
T(0.0), T(1.0), T(0.0), T(0.0),
T(0.0), T(0.0), T(1.0), T(0.0),
T(0.0), T(0.0), T(0.0), T(1.0)
);
}
template<typename T>
constexpr Mat4<T> Mat4<T>::LookAt(const Vec3<T>& eye, const Vec3<T>& target, const Vec3<T>& up)
{
Vec3<T> f = Vec3<T>::Normalize(target - eye);
Vec3<T> s = Vec3<T>::Normalize(f.CrossProduct(up));
Vec3<T> u = s.CrossProduct(f);
return Mat4(
s.x, u.x, -f.x, T(0.0),
s.y, u.y, -f.y, T(0.0),
s.z, u.z, -f.z, T(0.0),
-s.DotProduct(eye), -u.DotProduct(eye), f.DotProduct(eye), T(1.0)
);
}
template<typename T>
constexpr Mat4<T> Mat4<T>::Ortho(T left, T right, T top, T bottom, T zNear, T zFar)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb204942(v=vs.85).aspx
return Mat4(
T(2.0) / (right - left), T(0.0), T(0.0), T(0.0),
T(0.0), T(2.0) / (top - bottom), T(0.0), T(0.0),
T(0.0), T(0.0), T(1.0) / (zNear - zFar), T(0.0),
(left + right) / (left - right), (top + bottom) / (bottom - top), zNear / (zNear - zFar), T(1.0)
);
}
template<typename T>
Mat4<T> Mat4<T>::Perspective(RadianAngle<T> angle, T ratio, T zNear, T zFar)
{
angle /= T(2.0);
T yScale = angle.GetTan();
return Mat4(
T(1.0) / (ratio * yScale), T(0.0), T(0.0), T(0.0),
T(0.0), T(-1.0) / (yScale), T(0.0), T(0.0),
T(0.0), T(0.0), zFar / (zNear - zFar), T(-1.0),
T(0.0), T(0.0), -(zNear * zFar) / (zFar - zNear), T(0.0)
);
}
template<typename T>
constexpr Mat4<T> Mat4<T>::Rotate(const Quat<T>& rotation)
{
Mat4 matrix = Mat4::Identity();
matrix.SetRotation(rotation);
return matrix;
}
template<typename T>
constexpr Mat4<T> Mat4<T>::Scale(const Vec3<T>& scale)
{
return Mat4(
scale.x, T(0.0), T(0.0), T(0.0),
T(0.0), scale.y, T(0.0), T(0.0),
T(0.0), T(0.0), scale.z, T(0.0),
T(0.0), T(0.0), T(0.0), T(1.0)
);
}
template<typename T>
constexpr Mat4<T> Mat4<T>::Translate(const Vec3<T>& translation)
{
return Mat4(
T(1.0), T(0.0), T(0.0), T(0.0),
T(0.0), T(1.0), T(0.0), T(0.0),
T(0.0), T(0.0), T(1.0), T(0.0),
translation.x, translation.y, translation.z, T(1.0)
);
}
template<typename T>
constexpr Mat4<T> Mat4<T>::Transform(const Vec3<T>& translation, const Quat<T>& rotation)
{
Mat4 mat = Mat4f::Identity();
mat.SetRotation(rotation);
mat.SetTranslation(translation);
return mat;
}
template<typename T>
constexpr Mat4<T> Mat4<T>::Transform(const Vec3<T>& translation, const Quat<T>& rotation, const Vec3<T>& scale)
{
Mat4 mat = Transform(translation, rotation);
mat.ApplyScale(scale);
return mat;
}
template<typename T>
constexpr Mat4<T> Mat4<T>::TransformInverse(const Vec3<T>& translation, const Quat<T>& rotation)
{
// A view matrix must apply an inverse transformation of the 'world' matrix
Quat<T> invRot = rotation.GetConjugate(); // Inverse of the rotation
return Transform(-(invRot * translation), invRot);
}
template<typename T>
constexpr Mat4<T> Mat4<T>::TransformInverse(const Vec3<T>& translation, const Quat<T>& rotation, const Vec3<T>& scale)
{
return TransformInverse(translation, rotation).ApplyScale(T(1.0) / scale);
}
template<typename T>
constexpr Mat4<T> Mat4<T>::Zero()
{
return Mat4(
T(0.0), T(0.0), T(0.0), T(0.0),
T(0.0), T(0.0), T(0.0), T(0.0),
T(0.0), T(0.0), T(0.0), T(0.0),
T(0.0), T(0.0), T(0.0), T(0.0)
);
}
template<typename T>
std::ostream& operator<<(std::ostream& out, const Mat4<T>& matrix)
{
return out << "Mat4(" << matrix.m11 << ", " << matrix.m12 << ", " << matrix.m13 << ", " << matrix.m14 << ",\n"
<< " " << matrix.m21 << ", " << matrix.m22 << ", " << matrix.m23 << ", " << matrix.m24 << ",\n"
<< " " << matrix.m31 << ", " << matrix.m32 << ", " << matrix.m33 << ", " << matrix.m34 << ",\n"
<< " " << matrix.m41 << ", " << matrix.m42 << ", " << matrix.m43 << ", " << matrix.m44 << ')';
}
template<typename T>
constexpr Mat4<T> operator*(T scale, const Mat4<T>& matrix)
{
return matrix * scale;
}
}

View File

@@ -0,0 +1,26 @@
#ifndef __SCOP_MATHS_UTILS__
#define __SCOP_MATHS_UTILS__
#include <concepts>
namespace Scop
{
template<typename T>
[[nodiscard]] constexpr T Mod(T x, T y) noexcept;
template<std::floating_point T>
[[nodiscard]] constexpr T DegreeToRadian(T degrees) noexcept;
template<std::floating_point T>
[[nodiscard]] constexpr T RadianToDegree(T radians) noexcept;
template<typename T>
[[nodiscard]] constexpr T Clamp(T value, T min, T max) noexcept;
template<typename T, typename T2>
[[nodiscard]] constexpr T Lerp(const T& from, const T& to, const T2& interpolation) noexcept;
}
#include <Maths/MathsUtils.inl>
#endif

View File

@@ -0,0 +1,47 @@
#pragma once
#include <Maths/MathsUtils.h>
#include <type_traits>
#include <cmath>
#include <Maths/Constants.h>
namespace Scop
{
template<typename T>
[[nodiscard]] constexpr T Mod(T x, T y) noexcept
{
if constexpr(std::is_floating_point_v<T>)
{
if(!std::is_constant_evaluated())
return x - static_cast<long long>(x / y) * y;
else
return std::fmod(x, y);
}
return x % y;
}
template<std::floating_point T>
[[nodiscard]] constexpr T DegreeToRadian(T degrees) noexcept
{
return degrees * (Pi<T>() / T(180.0));
}
template<std::floating_point T>
[[nodiscard]] constexpr T RadianToDegree(T radians) noexcept
{
return radians * (T(180.0) / Pi<T>());
}
template<typename T>
[[nodiscard]] constexpr T Clamp(T value, T min, T max) noexcept
{
return std::max(std::min(value, max), min);
}
template<typename T, typename T2>
[[nodiscard]] constexpr T Lerp(const T& from, const T& to, const T2& interpolation) noexcept
{
return static_cast<T>(from + interpolation * (to - from));
}
}

View File

@@ -0,0 +1,91 @@
#ifndef __SCOP_QUATERNIONS__
#define __SCOP_QUATERNIONS__
#include <Maths/Angles.h>
#include <Maths/Vec3.h>
namespace Scop
{
template<typename T>
struct Quat
{
T w, x, y, z;
constexpr Quat() = default;
constexpr Quat(T W, T X, T Y, T Z);
template<AngleUnit Unit> Quat(const Angle<Unit, T>& angle);
Quat(const EulerAngles<T>& angles);
constexpr Quat(RadianAngle<T> angle, const Vec3<T>& axis);
constexpr Quat(const T quat[4]);
template<typename U> constexpr explicit Quat(const Quat<U>& quat);
constexpr Quat(const Quat&) = default;
constexpr Quat(Quat&&) = default;
~Quat() = default;
RadianAngle<T> AngleBetween(const Quat& vec) const;
constexpr bool ApproxEqual(const Quat& quat, T maxDifference = std::numeric_limits<T>::epsilon()) const;
Quat& ComputeW();
constexpr Quat& Conjugate();
constexpr T DotProduct(const Quat& vec) const;
constexpr Quat GetConjugate() const;
Quat GetInverse() const;
Quat GetNormal(T* length = nullptr) const;
Quat& Inverse();
T Magnitude() const;
Quat& Normalize(T* length = nullptr);
constexpr T SquaredMagnitude() const;
RadianAngle<T> To2DAngle() const;
EulerAngles<T> ToEulerAngles() const;
std::string ToString() const;
constexpr Quat& operator=(const Quat& quat) = default;
constexpr Quat& operator=(Quat&&) = default;
constexpr Quat operator+(const Quat& quat) const;
constexpr Quat operator*(const Quat& quat) const;
constexpr Vec3<T> operator*(const Vec3<T>& vec) const;
constexpr Quat operator*(T scale) const;
constexpr Quat operator/(const Quat& quat) const;
constexpr Quat& operator+=(const Quat& quat);
constexpr Quat& operator*=(const Quat& quat);
constexpr Quat& operator*=(T scale);
constexpr Quat& operator/=(const Quat& quat);
constexpr bool operator==(const Quat& quat) const;
constexpr bool operator!=(const Quat& quat) const;
constexpr bool operator<(const Quat& quat) const;
constexpr bool operator<=(const Quat& quat) const;
constexpr bool operator>(const Quat& quat) const;
constexpr bool operator>=(const Quat& quat) const;
static RadianAngle<T> AngleBetween(const Quat& lhs, const Quat& rhs);
static constexpr bool ApproxEqual(const Quat& lhs, const Quat& rhs, T maxDifference = std::numeric_limits<T>::epsilon());
static constexpr Quat Identity();
static constexpr Quat Lerp(const Quat& from, const Quat& to, T interpolation);
static Quat LookAt(const Vec3<T>& forward, const Vec3<T>& up);
static Quat Normalize(const Quat& quat, T* length = nullptr);
static Quat RotationBetween(const Vec3<T>& from, const Vec3<T>& to);
static Quat RotateTowards(const Quat& from, const Quat& to, RadianAngle<T> maxRotation);
static Quat Mirror(Quat quat, const Vec3<T>& axis);
static Quat Slerp(const Quat& from, const Quat& to, T interpolation);
static constexpr Quat Zero();
};
using Quatd = Quat<double>;
using Quatf = Quat<float>;
template<typename T> std::ostream& operator<<(std::ostream& out, const Quat<T>& quat);
}
#include <Maths/Quaternions.inl>
#endif

View File

@@ -0,0 +1,508 @@
#pragma once
#include <Maths/Quaternions.h>
namespace Scop
{
template<typename T>
constexpr Quat<T>::Quat(T W, T X, T Y, T Z) : w(W), x(X), y(Y), z(Z)
{}
template<typename T>
template<AngleUnit Unit>
Quat<T>::Quat(const Angle<Unit, T>& angle) : Quat(angle.ToQuat())
{}
template<typename T>
Quat<T>::Quat(const EulerAngles<T>& angles) : Quat(angles.ToQuat())
{}
template<typename T>
constexpr Quat<T>::Quat(RadianAngle<T> angle, const Vec3<T>& axis)
{
angle /= T(2.0);
Vec3<T> normalizedAxis = axis.GetNormal();
auto sincos = angle.GetSinCos();
w = sincos.second;
x = normalizedAxis.x * sincos.first;
y = normalizedAxis.y * sincos.first;
z = normalizedAxis.z * sincos.first;
Normalize();
}
template<typename T>
constexpr Quat<T>::Quat(const T quat[4]) : w(quat[0]), x(quat[1]), y(quat[2]), z(quat[3])
{}
template<typename T>
template<typename U>
constexpr Quat<T>::Quat(const Quat<U>& quat) : w(static_cast<T>(quat.w)), x(static_cast<T>(quat.x)), y(static_cast<T>(quat.y)), z(static_cast<T>(quat.z))
{}
template<typename T>
RadianAngle<T> Quat<T>::AngleBetween(const Quat& quat) const
{
T alpha = Vec3<T>::DotProduct(Vec3<T>(x, y, z), Vec3<T>(quat.x, quat.y, quat.z));
return std::acos(Scop::Clamp(alpha, T(-1.0), T(1.0)));
}
template<typename T>
constexpr bool Quat<T>::ApproxEqual(const Quat& quat, T maxDifference) const
{
return NumberEquals(w, quat.w, maxDifference) &&
NumberEquals(x, quat.x, maxDifference) &&
NumberEquals(y, quat.y, maxDifference) &&
NumberEquals(z, quat.z, maxDifference);
}
template<typename T>
Quat<T>& Quat<T>::ComputeW()
{
T t = T(1.0) - SquaredMagnitude();
if(t < T(0.0))
w = T(0.0);
else
w = -std::sqrt(t);
return *this;
}
template<typename T>
constexpr Quat<T>& Quat<T>::Conjugate()
{
x = -x;
y = -y;
z = -z;
return *this;
}
template<typename T>
constexpr T Quat<T>::DotProduct(const Quat& quat) const
{
return w * quat.w + x * quat.x + y * quat.y + z * quat.z;
}
template<typename T>
constexpr Quat<T> Quat<T>::GetConjugate() const
{
Quat<T> quat(*this);
quat.Conjugate();
return quat;
}
template<typename T>
Quat<T> Quat<T>::GetInverse() const
{
Quat<T> quat(*this);
quat.Inverse();
return quat;
}
template<typename T>
Quat<T> Quat<T>::GetNormal(T* length) const
{
Quat<T> quat(*this);
quat.Normalize(length);
return quat;
}
template<typename T>
Quat<T>& Quat<T>::Inverse()
{
T norm = SquaredMagnitude();
if(norm > T(0.0))
{
T invNorm = T(1.0) / std::sqrt(norm);
w *= invNorm;
x *= -invNorm;
y *= -invNorm;
z *= -invNorm;
}
return *this;
}
template<typename T>
T Quat<T>::Magnitude() const
{
return std::sqrt(SquaredMagnitude());
}
template<typename T>
Quat<T>& Quat<T>::Normalize(T* length)
{
T norm = std::sqrt(SquaredMagnitude());
if(norm > T(0.0))
{
T invNorm = T(1.0) / norm;
w *= invNorm;
x *= invNorm;
y *= invNorm;
z *= invNorm;
}
if(length)
*length = norm;
return *this;
}
template<typename T>
constexpr T Quat<T>::SquaredMagnitude() const
{
return w * w + x * x + y * y + z * z;
}
template<typename T>
RadianAngle<T> Quat<T>::To2DAngle() const
{
T siny_cosp = T(2.0) * (w * z + x * y);
T cosy_cosp = T(1.0) - T(2.0) * (y * y + z * z);
return std::atan2(siny_cosp, cosy_cosp);
}
template<typename T>
EulerAngles<T> Quat<T>::ToEulerAngles() const
{
T test = x * y + z * w;
if(test > T(0.499))
// singularity at north pole
return EulerAngles<T>(DegreeAngle<T>(T(0.0)), RadianAngle<T>(T(2.0) * std::atan2(x, w)), DegreeAngle<T>(T(90.0)));
if(test < T(-0.499))
// singularity at south pole
return EulerAngles<T>(DegreeAngle<T>(T(0.0)), RadianAngle<T>(T(-2.0) * std::atan2(x, w)), DegreeAngle<T>(T(-90.0)));
return EulerAngles<T>(RadianAngle<T>(std::atan2(T(2.0) * x * w - T(2.0) * y * z, T(1.0) - T(2.0) * x * x - T(2.0) * z * z)),
RadianAngle<T>(std::atan2(T(2.0) * y * w - T(2.0) * x * z, T(1.0) - T(2.0) * y * y - T(2.0) * z * z)),
RadianAngle<T>(std::asin(T(2.0) * test)));
}
template<typename T>
std::string Quat<T>::ToString() const
{
std::ostringstream ss;
ss << *this;
return ss.str();
}
template<typename T>
constexpr Quat<T> Quat<T>::operator+(const Quat& quat) const
{
Quat result;
result.w = w + quat.w;
result.x = x + quat.x;
result.y = y + quat.y;
result.z = z + quat.z;
return result;
}
template<typename T>
constexpr Quat<T> Quat<T>::operator*(const Quat& quat) const
{
Quat result;
result.w = w * quat.w - x * quat.x - y * quat.y - z * quat.z;
result.x = w * quat.x + x * quat.w + y * quat.z - z * quat.y;
result.y = w * quat.y + y * quat.w + z * quat.x - x * quat.z;
result.z = w * quat.z + z * quat.w + x * quat.y - y * quat.x;
return result;
}
template<typename T>
constexpr Vec3<T> Quat<T>::operator*(const Vec3<T>& vec) const
{
Vec3<T> quatVec(x, y, z);
Vec3<T> uv = quatVec.CrossProduct(vec);
Vec3<T> uuv = quatVec.CrossProduct(uv);
uv *= T(2.0) * w;
uuv *= T(2.0);
return vec + uv + uuv;
}
template<typename T>
constexpr Quat<T> Quat<T>::operator*(T scale) const
{
return Quat(w * scale,
x * scale,
y * scale,
z * scale);
}
template<typename T>
constexpr Quat<T> Quat<T>::operator/(const Quat& quat) const
{
return quat.GetConjugate() * (*this);
}
template<typename T>
constexpr Quat<T>& Quat<T>::operator+=(const Quat& quat)
{
return operator=(operator+(quat));
}
template<typename T>
constexpr Quat<T>& Quat<T>::operator*=(const Quat& quat)
{
return operator=(operator*(quat));
}
template<typename T>
constexpr Quat<T>& Quat<T>::operator*=(T scale)
{
return operator=(operator*(scale));
}
template<typename T>
constexpr Quat<T>& Quat<T>::operator/=(const Quat& quat)
{
return operator=(operator/(quat));
}
template<typename T>
constexpr bool Quat<T>::operator==(const Quat& quat) const
{
return w == quat.w && x == quat.x && y == quat.y && z == quat.z;
}
template<typename T>
constexpr bool Quat<T>::operator!=(const Quat& quat) const
{
return !operator==(quat);
}
template<typename T>
constexpr bool Quat<T>::operator<(const Quat& quat) const
{
if(w != quat.w)
return w < quat.w;
if(x != quat.x)
return x < quat.x;
if(y != quat.y)
return y < quat.y;
if(z != quat.z)
return z < quat.z;
}
template<typename T>
constexpr bool Quat<T>::operator<=(const Quat& quat) const
{
if(w != quat.w)
return w < quat.w;
if(x != quat.x)
return x < quat.x;
if(y != quat.y)
return y < quat.y;
if(z != quat.z)
return z <= quat.z;
}
template<typename T>
constexpr bool Quat<T>::operator>(const Quat& quat) const
{
if(w != quat.w)
return w > quat.w;
if(x != quat.x)
return x > quat.x;
if(y != quat.y)
return y > quat.y;
if(z != quat.z)
return z > quat.z;
}
template<typename T>
constexpr bool Quat<T>::operator>=(const Quat& quat) const
{
if(w != quat.w)
return w > quat.w;
if(x != quat.x)
return x > quat.x;
if(y != quat.y)
return y > quat.y;
if(z != quat.z)
return z >= quat.z;
}
template<typename T>
RadianAngle<T> Quat<T>::AngleBetween(const Quat& lhs, const Quat& rhs)
{
return lhs.AngleBetween(rhs);
}
template<typename T>
constexpr bool Quat<T>::ApproxEqual(const Quat& lhs, const Quat& rhs, T maxDifference)
{
return lhs.ApproxEqual(rhs, maxDifference);
}
template<typename T>
constexpr Quat<T> Quat<T>::Identity()
{
return Quat(1, 0, 0, 0);
}
template<typename T>
constexpr Quat<T> Quat<T>::Lerp(const Quat& from, const Quat& to, T interpolation)
{
Quat interpolated;
interpolated.w = Scop::Lerp(from.w, to.w, interpolation);
interpolated.x = Scop::Lerp(from.x, to.x, interpolation);
interpolated.y = Scop::Lerp(from.y, to.y, interpolation);
interpolated.z = Scop::Lerp(from.z, to.z, interpolation);
return interpolated;
}
template<typename T>
Quat<T> Quat<T>::LookAt(const Vec3<T>& forward, const Vec3<T>& up)
{
// From https://gamedev.stackexchange.com/questions/53129/quaternion-look-at-with-up-vector
Vec3<T> forward_w = Vec3<T>::Forward();
Vec3<T> axis = Vec3<T>::CrossProduct(forward, forward_w);
RadianAngle<T> angle = std::acos(Vec3<T>::DotProduct(forward, forward_w));
Vec3<T> third = Vec3<T>::CrossProduct(axis, forward_w);
if(Vec3<T>::DotProduct(third, forward) < 0)
angle = -angle;
Quat<T> q1 = Quat(angle, axis);
Vec3<T> up_l = q1 * up;
Vec3<T> right = Vec3<T>::Normalize(Vec3<T>::CrossProduct(forward, up));
Vec3<T> up_w = Vec3<T>::Normalize(Vec3<T>::CrossProduct(right, forward));
Vec3<T> axis2 = Vec3<T>::CrossProduct(up_l, up_w);
RadianAngle<T> angle2 = std::acos(Vec3<T>::DotProduct(forward, forward_w));
Quat<T> q2 = Quat(angle2, axis2);
return q2 * q1;
}
template<typename T>
Quat<T> Quat<T>::Normalize(const Quat& quat, T* length)
{
return quat.GetNormal(length);
}
template<typename T>
Quat<T> Quat<T>::RotationBetween(const Vec3<T>& from, const Vec3<T>& to)
{
T dot = from.DotProduct(to);
if(dot < T(-0.999999))
{
Vec3<T> crossProduct;
if(from.DotProduct(Vec3<T>::UnitX()) < T(0.999999))
crossProduct = Vec3<T>::UnitX().CrossProduct(from);
else
crossProduct = Vec3<T>::UnitY().CrossProduct(from);
crossProduct.Normalize();
return Quat(Pi<T>(), crossProduct);
}
else if(dot > T(0.999999))
return Quat::Identity();
else
{
T norm = std::sqrt(from.GetSquaredLength() * to.GetSquaredLength());
Vec3<T> crossProduct = from.CrossProduct(to);
return Quat(norm + dot, crossProduct.x, crossProduct.y, crossProduct.z).GetNormal();
}
}
template<typename T>
Quat<T> Quat<T>::RotateTowards(const Quat& from, const Quat& to, RadianAngle<T> maxRotation)
{
RadianAngle<T> rotationBetween = AngleBetween(from, to);
if(rotationBetween < maxRotation)
return to;
return Slerp(from, to, std::min(maxRotation.value / rotationBetween.value), 1.f);
}
template<typename T>
Quat<T> Quat<T>::Mirror(Quat quat, const Vec3<T>& axis)
{
T x = std::copysign(T(1.0), axis.x);
T y = std::copysign(T(1.0), axis.y);
T z = std::copysign(T(1.0), axis.z);
quat.x = y * z * quat.x;
quat.y = x * z * quat.y;
quat.z = x * y * quat.z;
return quat;
}
template<typename T>
Quat<T> Quat<T>::Slerp(const Quat& from, const Quat& to, T interpolation)
{
Quat q;
T cosOmega = from.DotProduct(to);
if(cosOmega < T(0.0))
{
// We invert everything
q = Quat(-to.w, -to.x, -to.y, -to.z);
cosOmega = -cosOmega;
}
else
q = Quat(to);
T k0, k1;
if(cosOmega > T(0.9999))
{
// Linear interpolation to avoid division by zero
k0 = T(1.0) - interpolation;
k1 = interpolation;
}
else
{
T sinOmega = std::sqrt(T(1.0) - cosOmega*cosOmega);
T omega = std::atan2(sinOmega, cosOmega);
// To avoid two divisions
sinOmega = T(1.0)/sinOmega;
k0 = std::sin((T(1.0) - interpolation) * omega) * sinOmega;
k1 = std::sin(interpolation*omega) * sinOmega;
}
Quat result(k0 * from.w, k0 * from.x, k0 * from.y, k0 * from.z);
return result += q * k1;
}
template<typename T>
constexpr Quat<T> Quat<T>::Zero()
{
return Quat(0, 0, 0, 0);
}
template<typename T>
std::ostream& operator<<(std::ostream& out, const Quat<T>& quat)
{
return out << "Quat(" << quat.w << " | " << quat.x << ", " << quat.y << ", " << quat.z << ')';
}
}

116
ScopEngine/Runtime/Includes/Maths/Vec2.h git.filemode.executable_file
View File

@@ -0,0 +1,116 @@
#ifndef __SCOP_VEC2__
#define __SCOP_VEC2__
#include <string>
#include <limits>
#include <cstdint>
#include <cmath>
#include <Core/Logs.h>
namespace Scop
{
template <typename T> class Vec3;
template <typename T> class Vec4;
template <typename T>
struct Vec2
{
T x;
T y;
constexpr Vec2() = default;
constexpr Vec2(T X, T Y);
constexpr explicit Vec2(T scale);
template<typename U> constexpr explicit Vec2(const Vec2<U>& vec);
constexpr Vec2(const Vec2&) = default;
constexpr Vec2(Vec2&&) = default;
constexpr explicit Vec2(const Vec3<T>& vec);
constexpr explicit Vec2(const Vec4<T>& vec);
T AbsDotProduct(const Vec2& vec) const;
constexpr bool ApproxEqual(const Vec2& vec, T max_difference = std::numeric_limits<T>::epsilon()) const;
template<typename U = T> U Distance(const Vec2& vec) const;
constexpr T DotProduct(const Vec2& vec) const;
template<typename U = T> T GetLength() const;
Vec2 GetNormal(T* length = nullptr) const;
constexpr T GetSquaredLength() const;
constexpr Vec2& Maximize(const Vec2& vec);
constexpr Vec2& Minimize(const Vec2& vec);
Vec2& Normalize(T* length = nullptr);
constexpr T SquaredDistance(const Vec2& vec) const;
std::string ToString() const;
constexpr T& operator[](std::size_t i);
constexpr T operator[](std::size_t i) const;
constexpr const Vec2& operator+() const;
constexpr Vec2 operator-() const;
constexpr Vec2 operator+(const Vec2& vec) const;
constexpr Vec2 operator-(const Vec2& vec) const;
constexpr Vec2 operator*(const Vec2& vec) const;
constexpr Vec2 operator*(T scale) const;
constexpr Vec2 operator/(const Vec2& vec) const;
constexpr Vec2 operator/(T scale) const;
constexpr Vec2 operator%(const Vec2& vec) const;
constexpr Vec2 operator%(T mod) const;
constexpr Vec2& operator=(const Vec2&) = default;
constexpr Vec2& operator=(Vec2&&) = default;
constexpr Vec2& operator+=(const Vec2& vec);
constexpr Vec2& operator-=(const Vec2& vec);
constexpr Vec2& operator*=(const Vec2& vec);
constexpr Vec2& operator*=(T scale);
constexpr Vec2& operator/=(const Vec2& vec);
constexpr Vec2& operator/=(T scale);
constexpr Vec2& operator%=(const Vec2& vec);
constexpr Vec2& operator%=(T mod);
constexpr bool operator==(const Vec2& vec) const;
constexpr bool operator!=(const Vec2& vec) const;
constexpr bool operator<(const Vec2& vec) const;
constexpr bool operator<=(const Vec2& vec) const;
constexpr bool operator>(const Vec2& vec) const;
constexpr bool operator>=(const Vec2& vec) const;
static constexpr Vec2 Apply(T(*func)(T), const Vec2& vec);
static constexpr bool ApproxEqual(const Vec2& lhs, const Vec2& rhs, T max_difference = std::numeric_limits<T>::epsilon());
template<typename U = T> static U Distance(const Vec2& vec1, const Vec2& vec2);
static constexpr T DotProduct(const Vec2& vec1, const Vec2& vec2);
static constexpr Vec2 Lerp(const Vec2& from, const Vec2& to, T interpolation);
static Vec2 Normalize(const Vec2& vec);
static constexpr Vec2 Unit();
static constexpr Vec2 UnitX();
static constexpr Vec2 UnitY();
static constexpr Vec2 Zero();
~Vec2() = default;
};
using Vec2d = Vec2<double>;
using Vec2f = Vec2<float>;
using Vec2i = Vec2<int>;
using Vec2ui = Vec2<unsigned int>;
using Vec2i32 = Vec2<std::int32_t>;
using Vec2i64 = Vec2<std::int64_t>;
using Vec2ui32 = Vec2<std::uint32_t>;
using Vec2ui64 = Vec2<std::uint64_t>;
template<typename T> std::ostream& operator<<(std::ostream& out, const Vec2<T>& vec);
template<typename T> constexpr Vec2<T> operator*(T scale, const Vec2<T>& vec);
template<typename T> constexpr Vec2<T> operator/(T scale, const Vec2<T>& vec);
template<typename T> constexpr Vec2<T> operator%(T mod, const Vec2<T>& vec);
}
#include <Maths/Vec2.inl>
#endif

388
ScopEngine/Runtime/Includes/Maths/Vec2.inl git.filemode.executable_file
View File

@@ -0,0 +1,388 @@
#pragma once
#include <Maths/Vec2.h>
namespace Scop
{
template<typename T>
constexpr Vec2<T>::Vec2(T X, T Y) : x(X), y(Y) {}
template<typename T>
constexpr Vec2<T>::Vec2(T scale) : x(scale), y(scale) {}
template<typename T>
template<typename U>
constexpr Vec2<T>::Vec2(const Vec2<U>& vec) : x(static_cast<T>(vec.x)), y(static_cast<T>(vec.y)) {}
template<typename T>
constexpr Vec2<T>::Vec2(const Vec3<T>& vec) : x(vec.x), y(vec.y) {}
template<typename T>
constexpr Vec2<T>::Vec2(const Vec4<T>& vec) : x(vec.x), y(vec.y) {}
template<typename T>
T Vec2<T>::AbsDotProduct(const Vec2& vec) const
{
return std::abs(x * vec.x) + std::abs(y * vec.y);
}
template<typename T>
constexpr bool Vec2<T>::ApproxEqual(const Vec2& vec, T maxDifference) const
{
return NumberEquals(x, vec.x, maxDifference) && NumberEquals(y, vec.y, maxDifference);
}
template<typename T>
template<typename U>
U Vec2<T>::Distance(const Vec2& vec) const
{
return static_cast<U>(std::sqrt(SquaredDistance(vec)));
}
template<typename T>
constexpr T Vec2<T>::DotProduct(const Vec2& vec) const
{
return x * vec.x + y * vec.y;
}
template<typename T>
template<typename U>
T Vec2<T>::GetLength() const
{
return static_cast<U>(std::sqrt(static_cast<U>(GetSquaredLength())));
}
template<typename T>
Vec2<T> Vec2<T>::GetNormal(T* length) const
{
Vec2 vec(*this);
vec.Normalize(length);
return vec;
}
template<typename T>
constexpr T Vec2<T>::GetSquaredLength() const
{
return x * x + y * y;
}
template<typename T>
constexpr Vec2<T>& Vec2<T>::Maximize(const Vec2& vec)
{
if(vec.x > x)
x = vec.x;
if(vec.y > y)
y = vec.y;
return *this;
}
template<typename T>
constexpr Vec2<T>& Vec2<T>::Minimize(const Vec2& vec)
{
if(vec.x < x)
x = vec.x;
if(vec.y < y)
y = vec.y;
return *this;
}
template<typename T>
Vec2<T>& Vec2<T>::Normalize(T* length)
{
T norm = GetLength();
if(norm > T(0.0))
{
T invNorm = T(1.0) / norm;
x *= invNorm;
y *= invNorm;
}
if(length)
*length = norm;
return *this;
}
template<typename T>
constexpr T Vec2<T>::SquaredDistance(const Vec2& vec) const
{
return (*this - vec).GetSquaredLength();
}
template<typename T>
std::string Vec2<T>::ToString() const
{
return "Vec2(" + std::to_string(x) + ", " + std::to_string(y) + ')';
}
template<typename T>
constexpr T& Vec2<T>::operator[](std::size_t i)
{
Scop::Assert(i < 2, "index out of range");
return *(&x + i);
}
template<typename T>
constexpr T Vec2<T>::operator[](std::size_t i) const
{
Scop::Assert(i < 2, "index out of range");
return *(&x + i);
}
template<typename T>
constexpr const Vec2<T>& Vec2<T>::operator+() const
{
return *this;
}
template<typename T>
constexpr Vec2<T> Vec2<T>::operator-() const
{
return Vec2(-x, -y);
}
template<typename T>
constexpr Vec2<T> Vec2<T>::operator+(const Vec2& vec) const
{
return Vec2(x + vec.x, y + vec.y);
}
template<typename T>
constexpr Vec2<T> Vec2<T>::operator-(const Vec2& vec) const
{
return Vec2(x - vec.x, y - vec.y);
}
template<typename T>
constexpr Vec2<T> Vec2<T>::operator*(const Vec2& vec) const
{
return Vec2(x * vec.x, y * vec.y);
}
template<typename T>
constexpr Vec2<T> Vec2<T>::operator*(T scale) const
{
return Vec2(x * scale, y * scale);
}
template<typename T>
constexpr Vec2<T> Vec2<T>::operator/(const Vec2& vec) const
{
return Vec2(x / vec.x, y / vec.y);
}
template<typename T>
constexpr Vec2<T> Vec2<T>::operator/(T scale) const
{
return Vec2(x / scale, y / scale);
}
template<typename T>
constexpr Vec2<T> Vec2<T>::operator%(const Vec2& vec) const
{
return Vec2(Mod(x, vec.x), Mod(y, vec.y));
}
template<typename T>
constexpr Vec2<T> Vec2<T>::operator%(T mod) const
{
return Vec2(Mod(x, mod), Mod(y, mod));
}
template<typename T>
constexpr Vec2<T>& Vec2<T>::operator+=(const Vec2& vec)
{
x += vec.x;
y += vec.y;
return *this;
}
template<typename T>
constexpr Vec2<T>& Vec2<T>::operator-=(const Vec2& vec)
{
x -= vec.x;
y -= vec.y;
return *this;
}
template<typename T>
constexpr Vec2<T>& Vec2<T>::operator*=(const Vec2& vec)
{
x *= vec.x;
y *= vec.y;
return *this;
}
template<typename T>
constexpr Vec2<T>& Vec2<T>::operator*=(T scale)
{
x *= scale;
y *= scale;
return *this;
}
template<typename T>
constexpr Vec2<T>& Vec2<T>::operator/=(const Vec2& vec)
{
x /= vec.x;
y /= vec.y;
return *this;
}
template<typename T>
constexpr Vec2<T>& Vec2<T>::operator/=(T scale)
{
x /= scale;
y /= scale;
return *this;
}
template<typename T>
constexpr Vec2<T>& Vec2<T>::operator%=(const Vec2& vec)
{
x = Mod(x, vec.x);
y = Mod(y, vec.y);
return *this;
}
template<typename T>
constexpr Vec2<T>& Vec2<T>::operator%=(T value)
{
x = Mod(x, value);
y = Mod(y, value);
return *this;
}
template<typename T>
constexpr bool Vec2<T>::operator==(const Vec2& vec) const
{
return x == vec.x && y == vec.y;
}
template<typename T>
constexpr bool Vec2<T>::operator!=(const Vec2& vec) const
{
return !operator==(vec);
}
template<typename T>
constexpr bool Vec2<T>::operator<(const Vec2& vec) const
{
if (x != vec.x)
return x < vec.x;
return y < vec.y;
}
template<typename T>
constexpr bool Vec2<T>::operator<=(const Vec2& vec) const
{
if (x != vec.x)
return x < vec.x;
return y <= vec.y;
}
template<typename T>
constexpr bool Vec2<T>::operator>(const Vec2& vec) const
{
if (x != vec.x)
return x > vec.x;
return y > vec.y;
}
template<typename T>
constexpr bool Vec2<T>::operator>=(const Vec2& vec) const
{
if (x != vec.x)
return x > vec.x;
return y >= vec.y;
}
template<typename T>
constexpr Vec2<T> Vec2<T>::Apply(T(*func)(T), const Vec2& vec)
{
return Vec2(func(vec.x), func(vec.y));
}
template<typename T>
constexpr bool Vec2<T>::ApproxEqual(const Vec2& lhs, const Vec2& rhs, T maxDifference)
{
return lhs.ApproxEqual(rhs, maxDifference);
}
template<typename T>
template<typename U>
U Vec2<T>::Distance(const Vec2& vec1, const Vec2& vec2)
{
return vec1.Distance<U>(vec2);
}
template<typename T>
constexpr T Vec2<T>::DotProduct(const Vec2& vec1, const Vec2& vec2)
{
return vec1.DotProduct(vec2);
}
template<typename T>
Vec2<T> Vec2<T>::Normalize(const Vec2& vec)
{
return vec.GetNormal();
}
template<typename T>
constexpr Vec2<T> Vec2<T>::Unit()
{
return Vec2(1, 1);
}
template<typename T>
constexpr Vec2<T> Vec2<T>::UnitX()
{
return Vec2(1, 0);
}
template<typename T>
constexpr Vec2<T> Vec2<T>::UnitY()
{
return Vec2(0, 1);
}
template<typename T>
constexpr Vec2<T> Vec2<T>::Zero()
{
return Vec2(0, 0);
}
template<typename T>
std::ostream& operator<<(std::ostream& out, const Vec2<T>& vec)
{
return out << "Vec2(" << vec.x << ", " << vec.y << ')';
}
template<typename T>
constexpr Vec2<T> operator*(T scale, const Vec2<T>& vec)
{
return Vec2<T>(scale * vec.x, scale * vec.y);
}
template<typename T>
constexpr Vec2<T> operator/(T scale, const Vec2<T>& vec)
{
return Vec2<T>(scale / vec.x, scale / vec.y);
}
template<typename T>
constexpr Vec2<T> operator%(T mod, const Vec2<T>& vec)
{
return Vec2<T>(Mod(mod, vec.x), Mod(mod, vec.y));
}
}

133
ScopEngine/Runtime/Includes/Maths/Vec3.h git.filemode.executable_file
View File

@@ -0,0 +1,133 @@
#ifndef __SCOP_VEC3__
#define __SCOP_VEC3__
#include <string>
#include <limits>
#include <cstdint>
#include <cmath>
#include <Core/Logs.h>
namespace Scop
{
template<typename T> class Vec2;
template<typename T> class Vec4;
template<typename T>
struct Vec3
{
T x;
T y;
T z;
constexpr Vec3() = default;
constexpr Vec3(T X, T Y, T Z);
constexpr Vec3(T X, const Vec2<T>& vec);
constexpr explicit Vec3(T scale);
constexpr Vec3(const Vec2<T>& vec, T Z = 0.0);
template<typename U> constexpr explicit Vec3(const Vec3<U>& vec);
constexpr Vec3(const Vec3&) = default;
constexpr Vec3(Vec3&&) = default;
constexpr explicit Vec3(const Vec4<T>& vec);
T AbsDotProduct(const Vec3& vec) const;
constexpr bool ApproxEqual(const Vec3& vec, T max_difference = std::numeric_limits<T>::epsilon()) const;
constexpr Vec3 CrossProduct(const Vec3& vec) const;
template<typename U = T> U Distance(const Vec3& vec) const;
constexpr T DotProduct(const Vec3& vec) const;
Vec3 GetAbs() const;
template<typename U = T> U GetLength() const;
Vec3 GetNormal(T* length = nullptr) const;
constexpr T GetSquaredLength() const;
constexpr Vec3& Maximize(const Vec3& vec);
constexpr Vec3& Minimize(const Vec3& vec);
Vec3& Normalize(T* length = nullptr);
constexpr T SquaredDistance(const Vec3& vec) const;
std::string ToString() const;
constexpr T& operator[](std::size_t i);
constexpr const T& operator[](std::size_t i) const;
constexpr const Vec3& operator+() const;
constexpr Vec3 operator-() const;
constexpr Vec3 operator+(const Vec3& vec) const;
constexpr Vec3 operator-(const Vec3& vec) const;
constexpr Vec3 operator*(const Vec3& vec) const;
constexpr Vec3 operator*(T scale) const;
constexpr Vec3 operator/(const Vec3& vec) const;
constexpr Vec3 operator/(T scale) const;
constexpr Vec3 operator%(const Vec3& vec) const;
constexpr Vec3 operator%(T mod) const;
constexpr Vec3& operator=(const Vec3&) = default;
constexpr Vec3& operator=(Vec3&&) = default;
constexpr Vec3& operator+=(const Vec3& vec);
constexpr Vec3& operator-=(const Vec3& vec);
constexpr Vec3& operator*=(const Vec3& vec);
constexpr Vec3& operator*=(T scale);
constexpr Vec3& operator/=(const Vec3& vec);
constexpr Vec3& operator/=(T scale);
constexpr Vec3& operator%=(const Vec3& vec);
constexpr Vec3& operator%=(T mod);
constexpr bool operator==(const Vec3& vec) const;
constexpr bool operator!=(const Vec3& vec) const;
constexpr bool operator<(const Vec3& vec) const;
constexpr bool operator<=(const Vec3& vec) const;
constexpr bool operator>(const Vec3& vec) const;
constexpr bool operator>=(const Vec3& vec) const;
static constexpr Vec3 Apply(T(*func)(T), const Vec3& vec);
static constexpr bool ApproxEqual(const Vec3& lhs, const Vec3& rhs, T max_difference = std::numeric_limits<T>::epsilon());
static constexpr Vec3 Backward();
static constexpr Vec3 Clamp(const Vec3& vec, const Vec3& min, const Vec3& max);
static constexpr Vec3 CrossProduct(const Vec3& vec1, const Vec3& vec2);
template<typename U = T> static U Distance(const Vec3& vec1, const Vec3& vec2);
static constexpr T DotProduct(const Vec3& vec1, const Vec3& vec2);
static constexpr Vec3 Down();
static constexpr Vec3 Forward();
static constexpr Vec3 Left();
static constexpr Vec3 Max(const Vec3& lhs, const Vec3& rhs);
static constexpr Vec3 Min(const Vec3& lhs, const Vec3& rhs);
static Vec3 Normalize(const Vec3& vec);
static constexpr Vec3 Right();
static constexpr T SquaredDistance(const Vec3& vec1, const Vec3& vec2);
static constexpr Vec3 Unit();
static constexpr Vec3 UnitX();
static constexpr Vec3 UnitY();
static constexpr Vec3 UnitZ();
static constexpr Vec3 Up();
static constexpr Vec3 Zero();
~Vec3() = default;
};
using Vec3b = Vec3<std::uint8_t>;
using Vec3d = Vec3<double>;
using Vec3f = Vec3<float>;
using Vec3i = Vec3<int>;
using Vec3ui = Vec3<unsigned int>;
using Vec3i32 = Vec3<std::int32_t>;
using Vec3i64 = Vec3<std::int64_t>;
using Vec3ui32 = Vec3<std::uint32_t>;
using Vec3ui64 = Vec3<std::uint64_t>;
template<typename T> std::ostream& operator<<(std::ostream& out, const Vec3<T>& vec);
template<typename T> constexpr Vec3<T> operator*(T scale, const Vec3<T>& vec);
template<typename T> constexpr Vec3<T> operator/(T scale, const Vec3<T>& vec);
template<typename T> constexpr Vec3<T> operator%(T scale, const Vec3<T>& vec);
}
#include <Maths/Vec3.inl>
#endif

509
ScopEngine/Runtime/Includes/Maths/Vec3.inl git.filemode.executable_file
View File

@@ -0,0 +1,509 @@
#pragma once
#include <Maths/Vec3.h>
namespace Scop
{
template<typename T>
constexpr Vec3<T>::Vec3(T X, T Y, T Z) : x(X), y(Y), z(Z) {}
template<typename T>
constexpr Vec3<T>::Vec3(T X, const Vec2<T>& vec) : x(X), y(vec.x), z(vec.y) {}
template<typename T>
constexpr Vec3<T>::Vec3(T scale) : x(scale), y(scale), z(scale) {}
template<typename T>
constexpr Vec3<T>::Vec3(const Vec2<T>& vec, T Z) : x(vec.x), y(vec.y), z(Z) {}
template<typename T>
template<typename U>
constexpr Vec3<T>::Vec3(const Vec3<U>& vec) : x(static_cast<T>(vec.x)), y(static_cast<T>(vec.y)), z(static_cast<T>(vec.z)) {}
template<typename T>
constexpr Vec3<T>::Vec3(const Vec4<T>& vec) : x(vec.x), y(vec.y), z(vec.z) {}
template<typename T>
T Vec3<T>::AbsDotProduct(const Vec3& vec) const
{
return std::abs(x * vec.x) + std::abs(y * vec.y) + std::abs(z * vec.z);
}
template<typename T>
constexpr bool Vec3<T>::ApproxEqual(const Vec3& vec, T maxDifference) const
{
return NumberEquals(x, vec.x, maxDifference) && NumberEquals(y, vec.y, maxDifference) && NumberEquals(z, vec.z, maxDifference);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::CrossProduct(const Vec3& vec) const
{
return Vec3(y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x);
}
template<typename T>
template<typename U>
U Vec3<T>::Distance(const Vec3& vec) const
{
return static_cast<U>(std::sqrt(static_cast<U>(SquaredDistance(vec))));
}
template<typename T>
constexpr T Vec3<T>::DotProduct(const Vec3& vec) const
{
return x * vec.x + y * vec.y + z * vec.z;
}
template<typename T>
Vec3<T> Vec3<T>::GetAbs() const
{
return Vec3(std::abs(x), std::abs(y), std::abs(z));
}
template<typename T>
template<typename U>
U Vec3<T>::GetLength() const
{
return static_cast<U>(std::sqrt(static_cast<U>(GetSquaredLength())));
}
template<typename T>
Vec3<T> Vec3<T>::GetNormal(T* length) const
{
Vec3 vec(*this);
vec.Normalize(length);
return vec;
}
template<typename T>
constexpr T Vec3<T>::GetSquaredLength() const
{
return x*x + y*y + z*z;
}
template<typename T>
constexpr Vec3<T>& Vec3<T>::Maximize(const Vec3& vec)
{
if (vec.x > x)
x = vec.x;
if (vec.y > y)
y = vec.y;
if (vec.z > z)
z = vec.z;
return *this;
}
template<typename T>
constexpr Vec3<T>& Vec3<T>::Minimize(const Vec3& vec)
{
if (vec.x < x)
x = vec.x;
if (vec.y < y)
y = vec.y;
if (vec.z < z)
z = vec.z;
return *this;
}
template<typename T>
Vec3<T>& Vec3<T>::Normalize(T* length)
{
T norm = GetLength();
if (norm > T(0.0))
{
T invNorm = T(1.0) / norm;
x *= invNorm;
y *= invNorm;
z *= invNorm;
}
if (length)
*length = norm;
return *this;
}
template<typename T>
constexpr T Vec3<T>::SquaredDistance(const Vec3& vec) const
{
return (*this - vec).GetSquaredLength();
}
template<typename T>
std::string Vec3<T>::ToString() const
{
return "Vec3(" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z) + ')';
}
template<typename T>
constexpr T& Vec3<T>::operator[](std::size_t i)
{
Scop::Assert(i < 3, "index out of range");
return *(&x + i);
}
template<typename T>
constexpr const T& Vec3<T>::operator[](std::size_t i) const
{
Scop::Assert(i < 3, "index out of range");
return *(&x + i);
}
template<typename T>
constexpr const Vec3<T>& Vec3<T>::operator+() const
{
return *this;
}
template<typename T>
constexpr Vec3<T> Vec3<T>::operator-() const
{
return Vec3(-x, -y, -z);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::operator+(const Vec3& vec) const
{
return Vec3(x + vec.x, y + vec.y, z + vec.z);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::operator-(const Vec3& vec) const
{
return Vec3(x - vec.x, y - vec.y, z - vec.z);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::operator*(const Vec3& vec) const
{
return Vec3(x * vec.x, y * vec.y, z * vec.z);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::operator*(T scale) const
{
return Vec3(x * scale, y * scale, z * scale);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::operator/(const Vec3& vec) const
{
return Vec3(x / vec.x, y / vec.y, z / vec.z);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::operator/(T scale) const
{
return Vec3(x / scale, y / scale, z / scale);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::operator%(const Vec3& vec) const
{
return Vec3(Mod(x, vec.x), Mod(y, vec.y), Mod(z, vec.z));
}
template<typename T>
constexpr Vec3<T> Vec3<T>::operator%(T mod) const
{
return Vec3(Mod(x, mod), Mod(y, mod), Mod(z, mod));
}
template<typename T>
constexpr Vec3<T>& Vec3<T>::operator+=(const Vec3& vec)
{
x += vec.x;
y += vec.y;
z += vec.z;
return *this;
}
template<typename T>
constexpr Vec3<T>& Vec3<T>::operator-=(const Vec3& vec)
{
x -= vec.x;
y -= vec.y;
z -= vec.z;
return *this;
}
template<typename T>
constexpr Vec3<T>& Vec3<T>::operator*=(const Vec3& vec)
{
x *= vec.x;
y *= vec.y;
z *= vec.z;
return *this;
}
template<typename T>
constexpr Vec3<T>& Vec3<T>::operator*=(T scale)
{
x *= scale;
y *= scale;
z *= scale;
return *this;
}
template<typename T>
constexpr Vec3<T>& Vec3<T>::operator/=(const Vec3& vec)
{
x /= vec.x;
y /= vec.y;
z /= vec.z;
return *this;
}
template<typename T>
constexpr Vec3<T>& Vec3<T>::operator/=(T scale)
{
x /= scale;
y /= scale;
z /= scale;
return *this;
}
template<typename T>
constexpr Vec3<T>& Vec3<T>::operator%=(const Vec3& vec)
{
x = Mod(x, vec.x);
y = Mod(y, vec.y);
z = Mod(z, vec.z);
return *this;
}
template<typename T>
constexpr Vec3<T>& Vec3<T>::operator%=(T mod)
{
x = Mod(x, mod);
y = Mod(y, mod);
z = Mod(z, mod);
return *this;
}
template<typename T>
constexpr bool Vec3<T>::operator==(const Vec3& vec) const
{
return x == vec.x && y == vec.y && z == vec.z;
}
template<typename T>
constexpr bool Vec3<T>::operator!=(const Vec3& vec) const
{
return !operator==(vec);
}
template<typename T>
constexpr bool Vec3<T>::operator<(const Vec3& vec) const
{
if (x != vec.x)
return x < vec.x;
if (y != vec.y)
return y < vec.y;
return z < vec.z;
}
template<typename T>
constexpr bool Vec3<T>::operator<=(const Vec3& vec) const
{
if (x != vec.x)
return x < vec.x;
if (y != vec.y)
return y < vec.y;
return z <= vec.z;
}
template<typename T>
constexpr bool Vec3<T>::operator>(const Vec3& vec) const
{
if (x != vec.x)
return x > vec.x;
if (y != vec.y)
return y > vec.y;
return z > vec.z;
}
template<typename T>
constexpr bool Vec3<T>::operator>=(const Vec3& vec) const
{
if (x != vec.x)
return x > vec.x;
if (y != vec.y)
return y > vec.y;
return z >= vec.z;
}
template<typename T>
constexpr Vec3<T> Vec3<T>::Apply(T(*func)(T), const Vec3& vec)
{
return Vec3(func(vec.x), func(vec.y), func(vec.z));
}
template<typename T>
constexpr bool Vec3<T>::ApproxEqual(const Vec3& lhs, const Vec3& rhs, T maxDifference)
{
return lhs.ApproxEqual(rhs, maxDifference);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::CrossProduct(const Vec3& vec1, const Vec3& vec2)
{
return vec1.CrossProduct(vec2);
}
template<typename T>
constexpr T Vec3<T>::DotProduct(const Vec3& vec1, const Vec3& vec2)
{
return vec1.DotProduct(vec2);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::Backward()
{
return Vec3(0, 0, 1);
}
template<typename T>
template<typename U>
U Vec3<T>::Distance(const Vec3& vec1, const Vec3& vec2)
{
return vec1.Distance<U>(vec2);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::Down()
{
return Vec3(0, -1, 0);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::Forward()
{
return Vec3(0, 0, -1);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::Left()
{
return Vec3(-1, 0, 0);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::Max(const Vec3& lhs, const Vec3& rhs)
{
Vec3 max = lhs;
max.Maximize(rhs);
return max;
}
template<typename T>
constexpr Vec3<T> Vec3<T>::Min(const Vec3& lhs, const Vec3& rhs)
{
Vec3 min = lhs;
min.Minimize(rhs);
return min;
}
template<typename T>
Vec3<T> Vec3<T>::Normalize(const Vec3& vec)
{
return vec.GetNormal();
}
template<typename T>
constexpr Vec3<T> Vec3<T>::Right()
{
return Vec3(1, 0, 0);
}
template<typename T>
constexpr T Vec3<T>::SquaredDistance(const Vec3& vec1, const Vec3& vec2)
{
return vec1.SquaredDistance(vec2);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::Unit()
{
return Vec3(1);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::UnitX()
{
return Vec3(1, 0, 0);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::UnitY()
{
return Vec3(0, 1, 0);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::UnitZ()
{
return Vec3(0, 0, 1);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::Up()
{
return Vec3(0, 1, 0);
}
template<typename T>
constexpr Vec3<T> Vec3<T>::Zero()
{
return Vec3(0, 0, 0);
}
template<typename T>
std::ostream& operator<<(std::ostream& out, const Vec3<T>& vec)
{
return out << "Vec3(" << vec.x << ", " << vec.y << ", " << vec.z << ')';
}
template<typename T>
constexpr Vec3<T> operator*(T scale, const Vec3<T>& vec)
{
return Vec3<T>(scale * vec.x, scale * vec.y, scale * vec.z);
}
template<typename T>
constexpr Vec3<T> operator/(T scale, const Vec3<T>& vec)
{
return Vec3<T>(scale / vec.x, scale / vec.y, scale / vec.z);
}
template<typename T>
constexpr Vec3<T> operator%(T mod, const Vec3<T>& vec)
{
return Vec3<T>(Mod(mod, vec.x), Mod(mod, vec.y), Mod(mod, vec.z));
}
}

115
ScopEngine/Runtime/Includes/Maths/Vec4.h git.filemode.executable_file
View File

@@ -0,0 +1,115 @@
#ifndef __SCOP_VEC4__
#define __SCOP_VEC4__
#include <string>
#include <limits>
#include <cstdint>
#include <cmath>
#include <Core/Logs.h>
namespace Scop
{
template<typename T> class Vec2;
template<typename T> class Vec3;
template<typename T>
struct Vec4
{
T x;
T y;
T z;
T w;
constexpr Vec4() = default;
constexpr Vec4(T X, T Y, T Z, T W = 1.0);
constexpr Vec4(T X, T Y, const Vec2<T>& vec);
constexpr Vec4(T X, const Vec2<T>& vec, T W);
constexpr Vec4(T X, const Vec3<T>& vec);
constexpr explicit Vec4(T scale);
constexpr Vec4(const Vec2<T>& vec, T Z = 0.0, T W = 1.0);
constexpr Vec4(const Vec3<T>& vec, T W = 1.0);
template<typename U> constexpr explicit Vec4(const Vec4<U>& vec);
constexpr Vec4(const Vec4&) = default;
constexpr Vec4(Vec4&&) = default;
T AbsDotProduct(const Vec4& vec) const;
constexpr bool ApproxEqual(const Vec4& vec, T max_difference = std::numeric_limits<T>::epsilon()) const;
constexpr T DotProduct(const Vec4& vec) const;
Vec4 GetNormal(T* length = nullptr) const;
constexpr Vec4& Maximize(const Vec4& vec);
constexpr Vec4& Minimize(const Vec4& vec);
Vec4& Normalize(T* length = nullptr);
std::string ToString() const;
constexpr Vec4& operator=(const Vec4&) = default;
constexpr Vec4& operator=(Vec4&&) = default;
constexpr T& operator[](std::size_t i);
constexpr const T& operator[](std::size_t i) const;
constexpr const Vec4& operator+() const;
constexpr Vec4 operator-() const;
constexpr Vec4 operator+(const Vec4& vec) const;
constexpr Vec4 operator-(const Vec4& vec) const;
constexpr Vec4 operator*(const Vec4& vec) const;
constexpr Vec4 operator*(T scale) const;
constexpr Vec4 operator/(const Vec4& vec) const;
constexpr Vec4 operator/(T scale) const;
constexpr Vec4 operator%(const Vec4& vec) const;
constexpr Vec4 operator%(T mod) const;
constexpr Vec4& operator+=(const Vec4& vec);
constexpr Vec4& operator-=(const Vec4& vec);
constexpr Vec4& operator*=(const Vec4& vec);
constexpr Vec4& operator*=(T scale);
constexpr Vec4& operator/=(const Vec4& vec);
constexpr Vec4& operator/=(T scale);
constexpr Vec4& operator%=(const Vec4& vec);
constexpr Vec4& operator%=(T mod);
constexpr bool operator==(const Vec4& vec) const;
constexpr bool operator!=(const Vec4& vec) const;
constexpr bool operator<(const Vec4& vec) const;
constexpr bool operator<=(const Vec4& vec) const;
constexpr bool operator>(const Vec4& vec) const;
constexpr bool operator>=(const Vec4& vec) const;
static constexpr Vec4 Apply(T(*func)(T), const Vec4& vec);
static constexpr bool ApproxEqual(const Vec4& lhs, const Vec4& rhs, T max_difference = std::numeric_limits<T>::epsilon());
static constexpr T DotProduct(const Vec4& vec1, const Vec4& vec2);
static Vec4 Normalize(const Vec4& vec);
static constexpr Vec4 UnitX();
static constexpr Vec4 UnitY();
static constexpr Vec4 UnitZ();
static constexpr Vec4 Zero();
~Vec4() = default;
};
using Vec4d = Vec4<double>;
using Vec4f = Vec4<float>;
using Vec4i = Vec4<int>;
using Vec4ui = Vec4<unsigned int>;
using Vec4i32 = Vec4<std::int32_t>;
using Vec4i64 = Vec4<std::int64_t>;
using Vec4ui32 = Vec4<std::uint32_t>;
using Vec4ui64 = Vec4<std::uint64_t>;
template<typename T> std::ostream& operator<<(std::ostream& out, const Vec4<T>& vec);
template<typename T> constexpr Vec4<T> operator*(T scale, const Vec4<T>& vec);
template<typename T> constexpr Vec4<T> operator/(T scale, const Vec4<T>& vec);
template<typename T> constexpr Vec4<T> operator%(T mod, const Vec4<T>& vec);
}
#include <Maths/Vec4.inl>
#endif

424
ScopEngine/Runtime/Includes/Maths/Vec4.inl git.filemode.executable_file
View File

@@ -0,0 +1,424 @@
#pragma once
#include <Maths/Vec4.h>
namespace Scop
{
template<typename T>
constexpr Vec4<T>::Vec4(T X, T Y, T Z, T W) : x(X), y(Y), z(Z), w(W) {}
template<typename T>
constexpr Vec4<T>::Vec4(T X, T Y, const Vec2<T>& vec) : x(X), y(Y), z(vec.x), w(vec.y) {}
template<typename T>
constexpr Vec4<T>::Vec4(T X, const Vec2<T>& vec, T W) : x(X), y(vec.x), z(vec.y), w(W) {}
template<typename T>
constexpr Vec4<T>::Vec4(T X, const Vec3<T>& vec) : x(X), y(vec.x), z(vec.y), w(vec.z) {}
template<typename T>
constexpr Vec4<T>::Vec4(T scale) : x(scale), y(scale), z(scale), w(scale) {}
template<typename T>
constexpr Vec4<T>::Vec4(const Vec2<T>& vec, T Z, T W) : x(vec.x), y(vec.y), z(Z), w(W) {}
template<typename T>
constexpr Vec4<T>::Vec4(const Vec3<T>& vec, T W) : x(vec.x), y(vec.y), z(vec.z), w(W) {}
template<typename T>
template<typename U>
constexpr Vec4<T>::Vec4(const Vec4<U>& vec) : x(static_cast<T>(vec.x)), y(static_cast<T>(vec.y)), z(static_cast<T>(vec.z)), w(static_cast<T>(vec.w)) {}
template<typename T>
T Vec4<T>::AbsDotProduct(const Vec4& vec) const
{
return std::abs(x * vec.x) + std::abs(y * vec.y) + std::abs(z * vec.z) + std::abs(w * vec.w);
}
template<typename T>
constexpr bool Vec4<T>::ApproxEqual(const Vec4& vec, T maxDifference) const
{
return NumberEquals(x, vec.x, maxDifference) && NumberEquals(y, vec.y, maxDifference) && NumberEquals(z, vec.z, maxDifference) && NumberEquals(w, vec.w, maxDifference);
}
template<typename T>
constexpr T Vec4<T>::DotProduct(const Vec4& vec) const
{
return x*vec.x + y*vec.y + z*vec.z + w*vec.w;
}
template<typename T>
Vec4<T> Vec4<T>::GetNormal(T* length) const
{
Vec4<T> vec(*this);
vec.Normalize(length);
return vec;
}
template<typename T>
constexpr Vec4<T>& Vec4<T>::Maximize(const Vec4& vec)
{
if (vec.x > x)
x = vec.x;
if (vec.y > y)
y = vec.y;
if (vec.z > z)
z = vec.z;
if (vec.w > w)
w = vec.w;
return *this;
}
template<typename T>
constexpr Vec4<T>& Vec4<T>::Minimize(const Vec4& vec)
{
if (vec.x < x)
x = vec.x;
if (vec.y < y)
y = vec.y;
if (vec.z < z)
z = vec.z;
if (vec.w < w)
w = vec.w;
return *this;
}
template<typename T>
Vec4<T>& Vec4<T>::Normalize(T* length)
{
T invLength = T(1.0) / w;
x *= invLength;
y *= invLength;
z *= invLength;
if (length)
*length = w;
w = T(1.0);
return *this;
}
template<typename T>
std::string Vec4<T>::ToString() const
{
std::ostringstream ss;
ss << *this;
return ss.str();
}
template<typename T>
constexpr T& Vec4<T>::operator[](std::size_t i)
{
Scop::Assert(i < 4, "index out of range");
return *(&x + i);
}
template<typename T>
constexpr const T& Vec4<T>::operator[](std::size_t i) const
{
Scop::Assert(i < 4, "index out of range");
return *(&x + i);
}
template<typename T>
constexpr const Vec4<T>& Vec4<T>::operator+() const
{
return *this;
}
template<typename T>
constexpr Vec4<T> Vec4<T>::operator-() const
{
return Vec4(-x, -y, -z, -w);
}
template<typename T>
constexpr Vec4<T> Vec4<T>::operator+(const Vec4& vec) const
{
return Vec4(x + vec.x, y + vec.y, z + vec.z, w + vec.w);
}
template<typename T>
constexpr Vec4<T> Vec4<T>::operator-(const Vec4& vec) const
{
return Vec4(x - vec.x, y - vec.y, z - vec.z, w - vec.w);
}
template<typename T>
constexpr Vec4<T> Vec4<T>::operator*(const Vec4& vec) const
{
return Vec4(x * vec.x, y * vec.y, z * vec.z, w * vec.w);
}
template<typename T>
constexpr Vec4<T> Vec4<T>::operator*(T scale) const
{
return Vec4(x * scale, y * scale, z * scale, w * scale);
}
template<typename T>
constexpr Vec4<T> Vec4<T>::operator/(const Vec4& vec) const
{
return Vec4(x / vec.x, y / vec.y, z / vec.z, w / vec.w);
}
template<typename T>
constexpr Vec4<T> Vec4<T>::operator/(T scale) const
{
return Vec4(x / scale, y / scale, z / scale, w / scale);
}
template<typename T>
constexpr Vec4<T> Vec4<T>::operator%(const Vec4& vec) const
{
return Vec4(Mod(x, vec.x), Mod(y, vec.y), Mod(z, vec.z), Mod(w, vec.w));
}
template<typename T>
constexpr Vec4<T> Vec4<T>::operator%(T mod) const
{
return Vec4(Mod(x, mod), Mod(y, mod), Mod(z, mod), Mod(z, mod));
}
template<typename T>
constexpr Vec4<T>& Vec4<T>::operator+=(const Vec4& vec)
{
x += vec.x;
y += vec.y;
z += vec.z;
w += vec.w;
return *this;
}
template<typename T>
constexpr Vec4<T>& Vec4<T>::operator-=(const Vec4& vec)
{
x -= vec.x;
y -= vec.y;
z -= vec.z;
w -= vec.w;
return *this;
}
template<typename T>
constexpr Vec4<T>& Vec4<T>::operator*=(const Vec4& vec)
{
x *= vec.x;
y *= vec.y;
z *= vec.z;
w *= vec.w;
return *this;
}
template<typename T>
constexpr Vec4<T>& Vec4<T>::operator*=(T scale)
{
x *= scale;
y *= scale;
z *= scale;
w *= scale;
return *this;
}
template<typename T>
constexpr Vec4<T>& Vec4<T>::operator/=(const Vec4& vec)
{
x /= vec.x;
y /= vec.y;
z /= vec.z;
w /= vec.w;
return *this;
}
template<typename T>
constexpr Vec4<T>& Vec4<T>::operator/=(T scale)
{
x /= scale;
y /= scale;
z /= scale;
w /= scale;
return *this;
}
template<typename T>
constexpr Vec4<T>& Vec4<T>::operator%=(const Vec4& vec)
{
x = Mod(x, vec.x);
y = Mod(y, vec.y);
z = Mod(z, vec.z);
w = Mod(w, vec.w);
return *this;
}
template<typename T>
constexpr Vec4<T>& Vec4<T>::operator%=(T mod)
{
x = Mod(x, mod);
y = Mod(y, mod);
z = Mod(z, mod);
w = Mod(w, mod);
return *this;
}
template<typename T>
constexpr bool Vec4<T>::operator==(const Vec4& vec) const
{
return x == vec.x && y == vec.y && z == vec.z && w == vec.w;
}
template<typename T>
constexpr bool Vec4<T>::operator!=(const Vec4& vec) const
{
return !operator==(vec);
}
template<typename T>
constexpr bool Vec4<T>::operator<(const Vec4& vec) const
{
if (x != vec.x)
return x < vec.x;
if (y != vec.y)
return y < vec.y;
if (z != vec.z)
return z < vec.z;
return w < vec.w;
}
template<typename T>
constexpr bool Vec4<T>::operator<=(const Vec4& vec) const
{
if (x != vec.x)
return x < vec.x;
if (y != vec.y)
return y < vec.y;
if (z != vec.z)
return z < vec.z;
return w <= vec.w;
}
template<typename T>
constexpr bool Vec4<T>::operator>(const Vec4& vec) const
{
if (x != vec.x)
return x > vec.x;
if (y != vec.y)
return y > vec.y;
if (z != vec.z)
return z > vec.z;
return w > vec.w;
}
template<typename T>
constexpr bool Vec4<T>::operator>=(const Vec4& vec) const
{
if (x != vec.x)
return x > vec.x;
if (y != vec.y)
return y > vec.y;
if (z != vec.z)
return z > vec.z;
return w >= vec.w;
}
template<typename T>
constexpr Vec4<T> Vec4<T>::Apply(T(*func)(T), const Vec4& vec)
{
return Vec4(func(vec.x), func(vec.y), func(vec.z), func(vec.w));
}
template<typename T>
constexpr bool Vec4<T>::ApproxEqual(const Vec4& lhs, const Vec4& rhs, T maxDifference)
{
return lhs.ApproxEqual(rhs, maxDifference);
}
template<typename T>
constexpr T Vec4<T>::DotProduct(const Vec4& vec1, const Vec4& vec2)
{
return vec1.DotProduct(vec2);
}
template<typename T>
Vec4<T> Vec4<T>::Normalize(const Vec4& vec)
{
return vec.GetNormal();
}
template<typename T>
constexpr Vec4<T> Vec4<T>::UnitX()
{
return Vec4(1, 0, 0, 1);
}
template<typename T>
constexpr Vec4<T> Vec4<T>::UnitY()
{
return Vec4(0, 1, 0, 1);
}
template<typename T>
constexpr Vec4<T> Vec4<T>::UnitZ()
{
return Vec4(0, 0, 1, 1);
}
template<typename T>
constexpr Vec4<T> Vec4<T>::Zero()
{
return Vec4(0, 0, 0, 1);
}
template<typename T>
std::ostream& operator<<(std::ostream& out, const Vec4<T>& vec)
{
return out << "Vec4(" << vec.x << ", " << vec.y << ", " << vec.z << ", " << vec.w << ')';
}
template<typename T>
constexpr Vec4<T> operator*(T scale, const Vec4<T>& vec)
{
return Vec4<T>(scale * vec.x, scale * vec.y, scale * vec.z, scale * vec.w);
}
template<typename T>
constexpr Vec4<T> operator/(T scale, const Vec4<T>& vec)
{
return Vec4<T>(scale / vec.x, scale / vec.y, scale / vec.z, scale / vec.w);
}
template<typename T>
constexpr Vec4<T> operator%(T mod, const Vec4<T>& vec)
{
return Vec4<T>(Mod(mod, vec.x), Mod(mod, vec.y), Mod(mod, vec.z), Mod(mod, vec.w));
}
}

61
ScopEngine/Runtime/Includes/Platform/Inputs.h git.filemode.normal_file
View File

@@ -0,0 +1,61 @@
#ifndef __SCOP_PLATFORM_INPUTS__
#define __SCOP_PLATFORM_INPUTS__
#include <array>
#include <vector>
#include <cstdint>
#include <functional>
#include <SDL2/SDL.h>
namespace Scop
{
using EventUpdateHook = std::function<void(SDL_Event*)>;
class Inputs
{
friend class ScopEngine;
public:
Inputs();
[[nodiscard]] inline bool IsKeyPressed(const std::uint32_t button) const noexcept { return m_keys[button]; }
[[nodiscard]] inline bool IsMouseButtonPressed(const std::uint8_t button) const noexcept { return m_mouse[button - 1]; }
[[nodiscard]] inline bool IsMouseWheelUp() const noexcept { return m_is_mouse_wheel_up; }
[[nodiscard]] inline bool IsMouseWheelDown() const noexcept { return m_is_mouse_wheel_down; }
[[nodiscard]] inline std::int32_t GetX() const noexcept { return m_x; }
[[nodiscard]] inline std::int32_t GetY() const noexcept { return m_y; }
[[nodiscard]] inline std::int32_t GetXRel() const noexcept { return m_x_rel; }
[[nodiscard]] inline std::int32_t GetYRel() const noexcept { return m_y_rel; }
inline void AddEventUpdateHook(const EventUpdateHook& hook) { m_hooks.push_back(hook); }
inline void GrabMouse() noexcept { SDL_SetRelativeMouseMode(SDL_TRUE); SDL_ShowCursor(SDL_DISABLE); m_is_mouse_grabbed = true; }
inline void ReleaseMouse() noexcept { SDL_SetRelativeMouseMode(SDL_FALSE); SDL_ShowCursor(SDL_ENABLE); m_is_mouse_grabbed = false; }
[[nodiscard]] inline bool IsMouseGrabbed() const noexcept { return m_is_mouse_grabbed; }
[[nodiscard]] inline bool HasRecievedCloseEvent() const noexcept { return m_has_recieved_close_event; }
~Inputs() = default;
private:
void Update();
private:
SDL_Event m_event;
std::vector<EventUpdateHook> m_hooks;
const std::uint8_t* m_keys;
std::array<bool, 5> m_mouse; // 5 bytes, shitty padding, maybe fix
std::int32_t m_keys_count = 0;
std::int32_t m_x = 0;
std::int32_t m_y = 0;
std::int32_t m_x_rel = 0;
std::int32_t m_y_rel = 0;
bool m_has_recieved_close_event = false;
bool m_is_mouse_grabbed = false;
bool m_is_mouse_wheel_up = false;
bool m_is_mouse_wheel_down = false;
};
}
#endif

46
ScopEngine/Runtime/Includes/Platform/Window.h git.filemode.normal_file
View File

@@ -0,0 +1,46 @@
#ifndef __SCOP_PLATFORM_WINDOW__
#define __SCOP_PLATFORM_WINDOW__
#include <SDL2/SDL.h>
#include <cstdint>
#include <string>
#include <Maths/Vec2.h>
#include <vector>
#include <vulkan/vulkan_core.h>
namespace Scop
{
class Window
{
friend class ScopEngine;
public:
Window(const std::string& title, std::uint32_t width, std::uint32_t height, bool hidden = false);
[[nodiscard]] inline const std::string& GetTitle() const noexcept { return m_title; }
[[nodiscard]] inline std::uint32_t GetWidth() const noexcept { return m_width; }
[[nodiscard]] inline std::uint32_t GetHeight() const noexcept { return m_height; }
[[nodiscard]] inline SDL_Window* GetSDLWindow() const noexcept { return p_window; }
void FetchWindowInfos() noexcept;
[[nodiscard]] VkSurfaceKHR CreateVulkanSurface(VkInstance instance) const noexcept;
[[nodiscard]] std::vector<const char*> GetRequiredVulkanInstanceExtentions() const noexcept;
[[nodiscard]] Vec2ui GetVulkanDrawableSize() const noexcept;
~Window();
private:
// Can only be called by the engine
void Destroy() noexcept;
private:
SDL_Window* p_window = nullptr;
std::string m_title;
std::uint32_t m_height = 0;
std::uint32_t m_width = 0;
};
}
#endif

89
ScopEngine/Runtime/Includes/Renderer/Buffer.h git.filemode.normal_file
View File

@@ -0,0 +1,89 @@
#ifndef __SCOP_GPU_BUFFER__
#define __SCOP_GPU_BUFFER__
#include <kvf.h>
#include <Renderer/Enums.h>
#include <Core/Logs.h>
#include <Renderer/RenderCore.h>
#include <Utils/Buffer.h>
#include <Renderer/Memory/Block.h>
namespace Scop
{
class GPUBuffer
{
public:
GPUBuffer() = default;
void Init(BufferType type, VkDeviceSize size, VkBufferUsageFlags usage, CPUBuffer data);
void Destroy() noexcept;
bool CopyFrom(const GPUBuffer& buffer) noexcept;
void Swap(GPUBuffer& buffer) noexcept;
[[nodiscard]] inline void* GetMap() const noexcept { return m_memory.map; }
[[nodiscard]] inline VkBuffer operator()() const noexcept { return m_buffer; }
[[nodiscard]] inline VkBuffer Get() const noexcept { return m_buffer; }
[[nodiscard]] inline VkDeviceMemory GetMemory() const noexcept { return m_memory.memory; }
[[nodiscard]] inline VkDeviceSize GetSize() const noexcept { return m_memory.size; }
[[nodiscard]] inline VkDeviceSize GetOffset() const noexcept { return 0; }
[[nodiscard]] inline static std::size_t GetBufferCount() noexcept { return s_buffer_count; }
[[nodiscard]] inline bool IsInit() const noexcept { return m_buffer != VK_NULL_HANDLE; }
~GPUBuffer() = default;
protected:
void PushToGPU() noexcept;
protected:
VkBuffer m_buffer = VK_NULL_HANDLE;
MemoryBlock m_memory = NULL_MEMORY_BLOCK;
private:
void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, bool use_raw_size);
private:
inline static std::size_t s_buffer_count = 0;
VkBufferUsageFlags m_usage = 0;
VkMemoryPropertyFlags m_flags = 0;
};
class VertexBuffer : public GPUBuffer
{
public:
inline void Init(std::uint32_t size, VkBufferUsageFlags additional_flags = 0) { GPUBuffer::Init(BufferType::LowDynamic, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | additional_flags, {}); }
void SetData(CPUBuffer data);
inline void Bind(VkCommandBuffer cmd) const noexcept { VkDeviceSize offset = 0; RenderCore::Get().vkCmdBindVertexBuffers(cmd, 0, 1, &m_buffer, &offset); }
};
class IndexBuffer : public GPUBuffer
{
public:
inline void Init(std::uint32_t size, VkBufferUsageFlags additional_flags = 0) { GPUBuffer::Init(BufferType::LowDynamic, size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | additional_flags, {}); }
void SetData(CPUBuffer data);
inline void Bind(VkCommandBuffer cmd) const noexcept { RenderCore::Get().vkCmdBindIndexBuffer(cmd, m_buffer, 0, VK_INDEX_TYPE_UINT32); }
};
class UniformBuffer
{
public:
void Init(std::uint32_t size);
void SetData(CPUBuffer data, std::size_t frame_index);
void Destroy() noexcept;
inline VkDeviceSize GetSize(int i) const noexcept { return m_buffers[i].GetSize(); }
inline VkDeviceSize GetOffset(int i) const noexcept { return m_buffers[i].GetOffset(); }
inline VkBuffer GetVk(int i) const noexcept { return m_buffers[i].Get(); }
inline GPUBuffer& Get(int i) noexcept { return m_buffers[i]; }
private:
std::array<GPUBuffer, MAX_FRAMES_IN_FLIGHT> m_buffers;
std::array<void*, MAX_FRAMES_IN_FLIGHT> m_maps;
};
}
#endif

View File

@@ -0,0 +1,51 @@
#ifndef __SCOP_DESCRIPTOR_SET__
#define __SCOP_DESCRIPTOR_SET__
#include <vector>
#include <cstdint>
#include <kvf.h>
#include <Renderer/RenderCore.h>
#include <Utils/NonOwningPtr.h>
#include <Renderer/Pipelines/Shader.h>
namespace Scop
{
struct Descriptor
{
NonOwningPtr<class GPUBuffer> storage_buffer_ptr;
NonOwningPtr<class GPUBuffer> uniform_buffer_ptr;
NonOwningPtr<class Image> image_ptr;
VkDescriptorType type;
ShaderType shader_type;
std::uint32_t binding;
};
class DescriptorSet
{
public:
DescriptorSet() { m_set.fill(VK_NULL_HANDLE); }
DescriptorSet(const ShaderSetLayout& layout, VkDescriptorSetLayout vklayout, ShaderType shader_type);
void SetImage(std::size_t i, std::uint32_t binding, class Image& image);
void SetStorageBuffer(std::size_t i, std::uint32_t binding, class GPUBuffer& buffer);
void SetUniformBuffer(std::size_t i, std::uint32_t binding, class GPUBuffer& buffer);
void Update(std::size_t i, VkCommandBuffer cmd = VK_NULL_HANDLE) noexcept;
[[nodiscard]] inline VkDescriptorSet GetSet(std::size_t i) const noexcept { return m_set[i]; }
[[nodiscard]] inline DescriptorSet Duplicate() const { return DescriptorSet{ m_set_layout, m_descriptors }; }
[[nodiscard]] inline bool IsInit() const noexcept { return m_set[0] != VK_NULL_HANDLE; }
~DescriptorSet() = default;
private:
DescriptorSet(VkDescriptorSetLayout layout, const std::vector<Descriptor>& descriptors);
private:
std::vector<Descriptor> m_descriptors;
std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_set;
VkDescriptorSetLayout m_set_layout;
};
}
#endif

31
ScopEngine/Runtime/Includes/Renderer/Enums.h git.filemode.normal_file
View File

@@ -0,0 +1,31 @@
#ifndef __SCOP_RENDERER_ENUMS__
#define __SCOP_RENDERER_ENUMS__
#include <cstddef>
namespace Scop
{
enum class BufferType
{
Constant = 0,
Staging,
HighDynamic, // typically stored in RAM
LowDynamic, // typically stored in VRAM
EndEnum
};
constexpr std::size_t BufferTypeCount = static_cast<std::size_t>(BufferType::EndEnum);
enum class ImageType
{
Color = 0,
Cube,
Depth,
// Maybe add depth array
EndEnum
};
constexpr std::size_t ImageTypeCount = static_cast<std::size_t>(ImageType::EndEnum);
}
#endif

138
ScopEngine/Runtime/Includes/Renderer/Image.h git.filemode.normal_file
View File

@@ -0,0 +1,138 @@
#ifndef __SCOP_IMAGE__
#define __SCOP_IMAGE__
#include <cstdint>
#include <vector>
#include <kvf.h>
#include <Maths/Vec4.h>
#include <Renderer/RenderCore.h>
#include <Renderer/Buffer.h>
#include <Utils/Buffer.h>
#include <Renderer/Enums.h>
#include <Renderer/Memory/Block.h>
namespace Scop
{
class Image
{
public:
Image() = default;
inline void Init(VkImage image, VkFormat format, std::uint32_t width, std::uint32_t height, VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED) noexcept
{
m_image = image;
m_format = format;
m_width = width;
m_height = height;
m_layout = layout;
}
void Init(ImageType type, std::uint32_t width, std::uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, bool is_multisampled = false);
void CreateImageView(VkImageViewType type, VkImageAspectFlags aspectFlags, int layer_count = 1) noexcept;
void CreateSampler() noexcept;
void TransitionLayout(VkImageLayout new_layout, VkCommandBuffer cmd = VK_NULL_HANDLE);
void Clear(VkCommandBuffer cmd, Vec4f color);
void DestroySampler() noexcept;
void DestroyImageView() noexcept;
virtual void Destroy() noexcept;
[[nodiscard]] inline VkImage Get() const noexcept { return m_image; }
[[nodiscard]] inline VkImage operator()() const noexcept { return m_image; }
[[nodiscard]] inline VkDeviceMemory GetDeviceMemory() const noexcept { return m_memory.memory; }
[[nodiscard]] inline VkImageView GetImageView() const noexcept { return m_image_view; }
[[nodiscard]] inline VkFormat GetFormat() const noexcept { return m_format; }
[[nodiscard]] inline VkImageTiling GetTiling() const noexcept { return m_tiling; }
[[nodiscard]] inline VkImageLayout GetLayout() const noexcept { return m_layout; }
[[nodiscard]] inline VkSampler GetSampler() const noexcept { return m_sampler; }
[[nodiscard]] inline std::uint32_t GetWidth() const noexcept { return m_width; }
[[nodiscard]] inline std::uint32_t GetHeight() const noexcept { return m_height; }
[[nodiscard]] inline bool IsInit() const noexcept { return m_image != VK_NULL_HANDLE; }
[[nodiscard]] inline ImageType GetType() const noexcept { return m_type; }
[[nodiscard]] inline static std::size_t GetImageCount() noexcept { return s_image_count; }
virtual ~Image() = default;
private:
inline static std::size_t s_image_count = 0;
MemoryBlock m_memory = NULL_MEMORY_BLOCK;
VkImage m_image = VK_NULL_HANDLE;
VkImageView m_image_view = VK_NULL_HANDLE;
VkSampler m_sampler = VK_NULL_HANDLE;
VkFormat m_format;
VkImageTiling m_tiling;
VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
ImageType m_type;
std::uint32_t m_width = 0;
std::uint32_t m_height = 0;
bool m_is_multisampled = false;
};
class DepthImage : public Image
{
public:
DepthImage() = default;
inline void Init(std::uint32_t width, std::uint32_t height, bool is_multisampled = false)
{
std::vector<VkFormat> candidates = { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT };
VkFormat format = kvfFindSupportFormatInCandidates(RenderCore::Get().GetDevice(), candidates.data(), candidates.size(), VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
Image::Init(ImageType::Depth, width, height, format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, is_multisampled);
Image::CreateImageView(VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_DEPTH_BIT);
Image::TransitionLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
}
~DepthImage() = default;
};
class Texture : public Image
{
public:
Texture() = default;
Texture(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format = VK_FORMAT_R8G8B8A8_SRGB, bool is_multisampled = false)
{
Init(std::move(pixels), width, height, format, is_multisampled);
}
inline void Init(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format = VK_FORMAT_R8G8B8A8_SRGB, bool is_multisampled = false)
{
Image::Init(ImageType::Color, width, height, format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, is_multisampled);
Image::CreateImageView(VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT);
Image::CreateSampler();
if(pixels)
{
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
GPUBuffer staging_buffer;
std::size_t size = width * height * kvfFormatSize(format);
staging_buffer.Init(BufferType::Staging, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, pixels);
VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
kvfCopyBufferToImage(cmd, Image::Get(), staging_buffer.Get(), staging_buffer.GetOffset(), VK_IMAGE_ASPECT_COLOR_BIT, { width, height, 1 });
RenderCore::Get().vkEndCommandBuffer(cmd);
VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice());
kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence);
kvfDestroyFence(RenderCore::Get().GetDevice(), fence);
staging_buffer.Destroy();
}
if(!pixels)
TransitionLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
else
TransitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
~Texture() override { Destroy(); }
};
class CubeTexture : public Image
{
public:
CubeTexture() = default;
CubeTexture(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format = VK_FORMAT_R8G8B8A8_SRGB)
{
Init(std::move(pixels), width, height, format);
}
void Init(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format = VK_FORMAT_R8G8B8A8_SRGB);
~CubeTexture() override { Destroy(); }
};
}
#endif

View File

@@ -0,0 +1,49 @@
#ifndef __SCOP_VULKAN_MEMORY_BLOCK__
#define __SCOP_VULKAN_MEMORY_BLOCK__
#include <kvf.h>
#include <algorithm>
namespace Scop
{
class MemoryBlock
{
friend class MemoryChunk;
public:
MemoryBlock() = default;
[[nodiscard]] inline bool operator==(const MemoryBlock& rhs) const noexcept
{
return memory == rhs.memory &&
offset == rhs.offset &&
size == rhs.size &&
free == rhs.free &&
map == rhs.map;
}
inline void Swap(MemoryBlock& rhs) noexcept
{
std::swap(memory, rhs.memory);
std::swap(offset, rhs.offset);
std::swap(size, rhs.size);
std::swap(map, rhs.map);
std::swap(free, rhs.free);
}
~MemoryBlock() = default;
public:
VkDeviceMemory memory = VK_NULL_HANDLE;
VkDeviceSize offset = 0;
VkDeviceSize size = 0;
void* map = nullptr; // useless if it's a GPU allocation
private:
bool free = false;
};
constexpr MemoryBlock NULL_MEMORY_BLOCK{};
}
#endif

View File

@@ -0,0 +1,35 @@
#ifndef __SCOP_VULKAN_MEMORY_CHUNK__
#define __SCOP_VULKAN_MEMORY_CHUNK__
#include <vector>
#include <cstdint>
#include <optional>
#include <Renderer/Memory/Block.h>
namespace Scop
{
class MemoryChunk
{
public:
MemoryChunk(VkDevice device, VkPhysicalDevice physical, VkDeviceSize size, std::int32_t memory_type_index);
[[nodiscard]] std::optional<MemoryBlock> Allocate(VkDeviceSize size, VkDeviceSize alignment);
void Deallocate(const MemoryBlock& block);
[[nodiscard]] inline bool Has(const MemoryBlock& block) const noexcept { return block.memory == m_memory; }
[[nodiscard]] inline std::int32_t GetMemoryTypeIndex() const noexcept { return m_memory_type_index; }
~MemoryChunk();
protected:
std::vector<MemoryBlock> m_blocks;
VkDevice m_device = VK_NULL_HANDLE;
VkPhysicalDevice m_physical = VK_NULL_HANDLE;
VkDeviceMemory m_memory = VK_NULL_HANDLE;
void* p_map = nullptr;
VkDeviceSize m_size = 0;
std::int32_t m_memory_type_index;
};
}
#endif

View File

@@ -0,0 +1,35 @@
#ifndef __SCOP_VULKAN_MEMORY_DEVICE_ALLOCATOR__
#define __SCOP_VULKAN_MEMORY_DEVICE_ALLOCATOR__
#include <vector>
#include <memory>
#include <cstdint>
#include <Renderer/Memory/Block.h>
#include <Renderer/Memory/Chunk.h>
namespace Scop
{
class DeviceAllocator
{
public:
DeviceAllocator() = default;
inline void AttachToDevice(VkDevice device, VkPhysicalDevice physical) noexcept { m_device = device; m_physical = physical; }
inline void DetachFromDevice() noexcept { m_chunks.clear(); m_device = VK_NULL_HANDLE; m_physical = VK_NULL_HANDLE; }
[[nodiscard]] inline std::size_t GetAllocationsCount() const noexcept { return m_allocations_count; }
[[nodiscard]] MemoryBlock Allocate(VkDeviceSize size, VkDeviceSize alignment, std::int32_t memory_type_index, bool dedicated_chunk = false);
void Deallocate(const MemoryBlock& block);
~DeviceAllocator() = default;
private:
std::vector<std::unique_ptr<MemoryChunk>> m_chunks;
VkDevice m_device = VK_NULL_HANDLE;
VkPhysicalDevice m_physical = VK_NULL_HANDLE;
std::size_t m_allocations_count = 0;
};
}
#endif

View File

@@ -0,0 +1,67 @@
#ifndef __SCOP_GRAPHICS_PIPELINE__
#define __SCOP_GRAPHICS_PIPELINE__
#include <memory>
#include <vector>
#include <kvf.h>
#include <Renderer/Image.h>
#include <Utils/NonOwningPtr.h>
#include <Renderer/Pipelines/Shader.h>
#include <Renderer/Pipelines/Pipeline.h>
namespace Scop
{
struct GraphicPipelineDescriptor
{
std::shared_ptr<Shader> vertex_shader;
std::shared_ptr<Shader> fragment_shader;
std::vector<NonOwningPtr<Texture>> color_attachments;
NonOwningPtr<DepthImage> depth = nullptr;
NonOwningPtr<class Renderer> renderer = nullptr;
VkCullModeFlagBits culling = VK_CULL_MODE_FRONT_BIT;
VkPolygonMode mode = VK_POLYGON_MODE_FILL;
bool no_vertex_inputs = false;
bool depth_test_equal = false;
bool clear_color_attachments = true;
};
class GraphicPipeline : public Pipeline
{
public:
GraphicPipeline() = default;
void Init(const GraphicPipelineDescriptor& descriptor);
bool BindPipeline(VkCommandBuffer command_buffer, std::size_t framebuffer_index, std::array<float, 4> clear) noexcept;
void EndPipeline(VkCommandBuffer command_buffer) noexcept override;
void Destroy() noexcept;
[[nodiscard]] inline VkPipeline GetPipeline() const override { return m_pipeline; }
[[nodiscard]] inline VkPipelineLayout GetPipelineLayout() const override { return m_pipeline_layout; }
[[nodiscard]] inline VkPipelineBindPoint GetPipelineBindPoint() const override { return VK_PIPELINE_BIND_POINT_GRAPHICS; }
~GraphicPipeline() = default;
private:
void CreateFramebuffers(const std::vector<NonOwningPtr<Texture>>& render_targets, bool clear_attachments);
void TransitionAttachments(VkCommandBuffer cmd = VK_NULL_HANDLE);
// Private override to remove access
bool BindPipeline(VkCommandBuffer) noexcept override { return false; };
private:
std::vector<NonOwningPtr<Texture>> m_attachments;
std::vector<VkFramebuffer> m_framebuffers;
std::vector<VkClearValue> m_clears;
std::shared_ptr<Shader> p_vertex_shader;
std::shared_ptr<Shader> p_fragment_shader;
VkRenderPass m_renderpass = VK_NULL_HANDLE;
VkPipeline m_pipeline = VK_NULL_HANDLE;
VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE;
NonOwningPtr<class Renderer> p_renderer;
NonOwningPtr<DepthImage> p_depth;
};
}
#endif

View File

@@ -0,0 +1,25 @@
#ifndef __SCOP_PIPELINE__
#define __SCOP_PIPELINE__
#include <kvf.h>
#include <Renderer/RenderCore.h>
namespace Scop
{
class Pipeline
{
public:
Pipeline() = default;
inline virtual bool BindPipeline(VkCommandBuffer command_buffer) noexcept { RenderCore::Get().vkCmdBindPipeline(command_buffer, GetPipelineBindPoint(), GetPipeline()); return true; }
inline virtual void EndPipeline([[maybe_unused]] VkCommandBuffer command_buffer) noexcept {}
virtual VkPipeline GetPipeline() const = 0;
virtual VkPipelineLayout GetPipelineLayout() const = 0;
virtual VkPipelineBindPoint GetPipelineBindPoint() const = 0;
virtual ~Pipeline() = default;
};
}
#endif

View File

@@ -0,0 +1,78 @@
#ifndef __SCOP_SHADER__
#define __SCOP_SHADER__
#include <vector>
#include <cstdint>
#include <filesystem>
#include <kvf.h>
namespace Scop
{
struct ShaderSetLayout
{
std::vector<std::pair<int, VkDescriptorType> > binds;
ShaderSetLayout(std::vector<std::pair<int, VkDescriptorType> > b) : binds(std::move(b)) {}
};
struct ShaderPushConstantLayout
{
std::size_t offset;
std::size_t size;
ShaderPushConstantLayout(std::size_t o, std::size_t s) : offset(o), size(s) {}
};
struct ShaderLayout
{
std::vector<std::pair<int, ShaderSetLayout> > set_layouts;
std::vector<ShaderPushConstantLayout> push_constants;
ShaderLayout(std::vector<std::pair<int, ShaderSetLayout> > s, std::vector<ShaderPushConstantLayout> pc) : set_layouts(std::move(s)), push_constants(std::move(pc)) {}
};
enum class ShaderType
{
Vertex,
Fragment,
Compute
};
struct ShaderPipelineLayoutPart
{
std::vector<VkPushConstantRange> push_constants;
std::vector<VkDescriptorSetLayout> set_layouts;
};
class Shader
{
public:
Shader(const std::vector<std::uint32_t>& bytecode, ShaderType type, ShaderLayout layout);
[[nodiscard]] inline const ShaderLayout& GetShaderLayout() const { return m_layout; }
[[nodiscard]] inline const std::vector<std::uint32_t>& GetByteCode() const noexcept { return m_bytecode; }
[[nodiscard]] inline const ShaderPipelineLayoutPart& GetPipelineLayout() const noexcept { return m_pipeline_layout_part; }
[[nodiscard]] inline VkShaderModule GetShaderModule() const noexcept { return m_module; }
[[nodiscard]] inline VkShaderStageFlagBits GetShaderStage() const noexcept { return m_stage; }
void Destroy();
~Shader();
private:
void GeneratePipelineLayout(ShaderLayout layout);
private:
ShaderLayout m_layout;
ShaderPipelineLayoutPart m_pipeline_layout_part;
std::vector<std::uint32_t> m_bytecode;
std::vector<VkDescriptorSetLayout> m_set_layouts;
VkShaderStageFlagBits m_stage;
VkShaderModule m_module = VK_NULL_HANDLE;
};
std::shared_ptr<Shader> LoadShaderFromFile(const std::filesystem::path& filepath, ShaderType type, ShaderLayout layout);
}
#endif

View File

@@ -0,0 +1,76 @@
#ifndef __SCOP_RENDER_CORE__
#define __SCOP_RENDER_CORE__
#include <array>
#include <memory>
#include <cstdint>
#include <optional>
#ifdef DEBUG
#define KVF_ENABLE_VALIDATION_LAYERS
#endif
#include <kvf.h>
#include <Renderer/Memory/DeviceAllocator.h>
namespace Scop
{
constexpr const int MAX_FRAMES_IN_FLIGHT = 2;
constexpr const int DEFAULT_VERTEX_SHADER_ID = 0;
constexpr const int DEFAULT_FRAGMENT_SHADER_ID = 1;
constexpr const int BASIC_FRAGMENT_SHADER_ID = 2;
std::optional<std::uint32_t> FindMemoryType(std::uint32_t type_filter, VkMemoryPropertyFlags properties, bool error = true);
#if defined(DEBUG) && defined(VK_EXT_debug_utils)
#define SCOP_HAS_DEBUG_UTILS_FUNCTIONS
#endif
class RenderCore
{
public:
RenderCore();
[[nodiscard]] inline VkInstance GetInstance() const noexcept { return m_instance; }
[[nodiscard]] inline VkInstance& GetInstanceRef() noexcept { return m_instance; }
[[nodiscard]] inline VkDevice GetDevice() const noexcept { return m_device; }
[[nodiscard]] inline VkPhysicalDevice GetPhysicalDevice() const noexcept { return m_physical_device; }
[[nodiscard]] inline DeviceAllocator& GetAllocator() noexcept { return m_allocator; }
[[nodiscard]] inline std::shared_ptr<class Shader> GetDefaultVertexShader() const { return m_internal_shaders[DEFAULT_VERTEX_SHADER_ID]; }
[[nodiscard]] inline std::shared_ptr<class Shader> GetBasicFragmentShader() const { return m_internal_shaders[BASIC_FRAGMENT_SHADER_ID]; }
[[nodiscard]] inline std::shared_ptr<class Shader> GetDefaultFragmentShader() const { return m_internal_shaders[DEFAULT_FRAGMENT_SHADER_ID]; }
inline void WaitDeviceIdle() const noexcept { vkDeviceWaitIdle(m_device); }
inline static bool IsInit() noexcept { return s_instance != nullptr; }
inline static RenderCore& Get() noexcept { return *s_instance; }
#define SCOP_VULKAN_GLOBAL_FUNCTION(fn) PFN_##fn fn = nullptr;
#define SCOP_VULKAN_INSTANCE_FUNCTION(fn) PFN_##fn fn = nullptr;
#define SCOP_VULKAN_DEVICE_FUNCTION(fn) PFN_##fn fn = nullptr;
#include <Renderer/Vulkan/VulkanDefs.h>
#undef SCOP_VULKAN_GLOBAL_FUNCTION
#undef SCOP_VULKAN_INSTANCE_FUNCTION
#undef SCOP_VULKAN_DEVICE_FUNCTION
~RenderCore();
private:
void LoadKVFGlobalVulkanFunctionPointers() const noexcept;
void LoadKVFInstanceVulkanFunctionPointers() const noexcept;
void LoadKVFDeviceVulkanFunctionPointers() const noexcept;
private:
static RenderCore* s_instance;
std::array<std::shared_ptr<class Shader>, 3> m_internal_shaders;
DeviceAllocator m_allocator;
VkInstance m_instance = VK_NULL_HANDLE;
VkDevice m_device = VK_NULL_HANDLE;
VkPhysicalDevice m_physical_device = VK_NULL_HANDLE;
};
}
#endif

View File

@@ -0,0 +1,31 @@
#ifndef __SCOP_2D_PASS__
#define __SCOP_2D_PASS__
#include <memory>
#include <Renderer/Descriptor.h>
#include <Renderer/Pipelines/Shader.h>
#include <Renderer/Pipelines/Graphics.h>
namespace Scop
{
class Render2DPass
{
public:
Render2DPass() = default;
void Init();
void Pass(class Scene& scene, class Renderer& renderer, class Texture& render_target);
void Destroy();
~Render2DPass() = default;
private:
GraphicPipeline m_pipeline;
std::shared_ptr<DescriptorSet> p_viewer_data_set;
std::shared_ptr<UniformBuffer> p_viewer_data_buffer;
std::shared_ptr<DescriptorSet> p_texture_set;
std::shared_ptr<Shader> p_vertex_shader;
std::shared_ptr<Shader> p_fragment_shader;
};
}
#endif

View File

@@ -0,0 +1,29 @@
#ifndef __SCOP_FINAL_PASS__
#define __SCOP_FINAL_PASS__
#include <memory>
#include <Renderer/Descriptor.h>
#include <Renderer/Pipelines/Shader.h>
#include <Renderer/Pipelines/Graphics.h>
namespace Scop
{
class FinalPass
{
public:
FinalPass() = default;
void Init();
void Pass(class Scene& scene, class Renderer& renderer, class Texture& render_target);
void Destroy();
~FinalPass() = default;
private:
GraphicPipeline m_pipeline;
std::shared_ptr<DescriptorSet> p_set;
std::shared_ptr<Shader> p_vertex_shader;
std::shared_ptr<Shader> p_fragment_shader;
};
}
#endif

View File

@@ -0,0 +1,15 @@
#ifndef __SCOP_FORWARD_PASS__
#define __SCOP_FORWARD_PASS__
namespace Scop
{
class ForwardPass
{
public:
ForwardPass() = default;
void Pass(class Scene& scene, class Renderer& renderer, class Texture& render_target);
~ForwardPass() = default;
};
}
#endif

View File

@@ -0,0 +1,30 @@
#ifndef __SCOP_PASSES__
#define __SCOP_PASSES__
#include <Renderer/Image.h>
#include <Renderer/RenderPasses/SkyboxPass.h>
#include <Renderer/RenderPasses/ForwardPass.h>
#include <Renderer/RenderPasses/FinalPass.h>
#include <Renderer/RenderPasses/2DPass.h>
namespace Scop
{
class RenderPasses
{
public:
RenderPasses() = default;
void Init();
void Pass(class Scene& scene, class Renderer& renderer);
void Destroy();
~RenderPasses() = default;
private:
SkyboxPass m_skybox;
Render2DPass m_2Dpass;
FinalPass m_final;
Texture m_main_render_texture;
ForwardPass m_forward;
};
}
#endif

View File

@@ -0,0 +1,30 @@
#ifndef __SCOP_SKYBOX_PASS__
#define __SCOP_SKYBOX_PASS__
#include <memory>
#include <Renderer/Descriptor.h>
#include <Renderer/Pipelines/Shader.h>
#include <Renderer/Pipelines/Graphics.h>
namespace Scop
{
class SkyboxPass
{
public:
SkyboxPass() = default;
void Init();
void Pass(class Scene& scene, class Renderer& renderer, class Texture& render_target);
void Destroy();
~SkyboxPass() = default;
private:
GraphicPipeline m_pipeline;
std::shared_ptr<DescriptorSet> p_set;
std::shared_ptr<Shader> p_vertex_shader;
std::shared_ptr<Shader> p_fragment_shader;
std::shared_ptr<class Mesh> m_cube;
};
}
#endif

View File

@@ -0,0 +1,55 @@
#ifndef __SCOP_RENDERER__
#define __SCOP_RENDERER__
#include <Platform/Window.h>
#include <Utils/NonOwningPtr.h>
#include <Renderer/Swapchain.h>
#include <Renderer/RenderCore.h>
#include <Renderer/Image.h>
#include <kvf.h>
#include <array>
#include <Core/EventBus.h>
namespace Scop
{
class Renderer
{
public:
Renderer() = default;
void Init(NonOwningPtr<Window> window);
void BeginFrame();
void EndFrame();
[[nodiscard]] inline VkSemaphore GetImageAvailableSemaphore(int index) const noexcept { return m_image_available_semaphores[index]; }
[[nodiscard]] inline VkSemaphore GetRenderFinishedSemaphore(int index) const noexcept { return m_render_finished_semaphores[index]; }
[[nodiscard]] inline VkCommandBuffer GetCommandBuffer(int index) const noexcept { return m_cmd_buffers[index]; }
[[nodiscard]] inline VkCommandBuffer GetActiveCommandBuffer() const noexcept { return m_cmd_buffers[m_current_frame_index]; }
[[nodiscard]] inline std::size_t& GetDrawCallsCounterRef() noexcept { return m_drawcalls; }
[[nodiscard]] inline std::size_t& GetPolygonDrawnCounterRef() noexcept { return m_polygons_drawn; }
[[nodiscard]] inline std::size_t GetCurrentFrameIndex() const noexcept { return m_current_frame_index; }
[[nodiscard]] inline NonOwningPtr<Window> GetWindow() const noexcept { return p_window; }
[[nodiscard]] inline const Swapchain& GetSwapchain() const noexcept { return m_swapchain; }
void Destroy() noexcept;
~Renderer() = default;
private:
Swapchain m_swapchain;
std::array<VkSemaphore, MAX_FRAMES_IN_FLIGHT> m_image_available_semaphores;
std::array<VkSemaphore, MAX_FRAMES_IN_FLIGHT> m_render_finished_semaphores;
std::array<VkCommandBuffer, MAX_FRAMES_IN_FLIGHT> m_cmd_buffers;
std::array<VkFence, MAX_FRAMES_IN_FLIGHT> m_cmd_fences;
NonOwningPtr<Window> p_window;
std::uint32_t m_current_frame_index = 0;
std::size_t m_polygons_drawn = 0;
std::size_t m_drawcalls = 0;
};
}
#endif

View File

@@ -0,0 +1,22 @@
#ifndef __SCOP_SCENES_RENDERER__
#define __SCOP_SCENES_RENDERER__
#include <Renderer/RenderPasses/Passes.h>
namespace Scop
{
class SceneRenderer
{
public:
SceneRenderer() = default;
void Init();
void Render(class Scene& scene, class Renderer& renderer); // TODO : add RTT support
void Destroy();
~SceneRenderer() = default;
private:
RenderPasses m_passes;
};
}
#endif

View File

@@ -0,0 +1,43 @@
#ifndef __SCOP_SWAPCHAIN__
#define __SCOP_SWAPCHAIN__
#include <Utils/NonOwningPtr.h>
#include <Renderer/Image.h>
namespace Scop
{
class Swapchain
{
public:
Swapchain() = default;
void Init(NonOwningPtr<class Window> window);
void AquireFrame(VkSemaphore signal);
void Present(VkSemaphore wait) noexcept;
void Destroy();
[[nodiscard]] inline VkSwapchainKHR Get() const noexcept { return m_swapchain; }
[[nodiscard]] inline VkSurfaceKHR GetSurface() const noexcept { return m_surface; }
[[nodiscard]] inline std::uint32_t GetImagesCount() const noexcept { return m_images_count; }
[[nodiscard]] inline std::uint32_t GetMinImagesCount() const noexcept { return m_min_images_count; }
[[nodiscard]] inline std::uint32_t GetImageIndex() const noexcept { return m_current_image_index; }
[[nodiscard]] inline const std::vector<Image>& GetSwapchainImages() const { return m_swapchain_images; }
~Swapchain() = default;
private:
void CreateSwapchain();
private:
std::vector<Image> m_swapchain_images;
VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
NonOwningPtr<class Window> p_window;
std::uint32_t m_images_count = 0;
std::uint32_t m_min_images_count = 0;
std::uint32_t m_current_image_index = 0;
bool m_resize = false;
};
}
#endif

28
ScopEngine/Runtime/Includes/Renderer/Vertex.h git.filemode.normal_file
View File

@@ -0,0 +1,28 @@
#ifndef __SCOP_VERTEX__
#define __SCOP_VERTEX__
#include <kvf.h>
#include <array>
#include <Maths/Vec4.h>
#include <Maths/Vec2.h>
namespace Scop
{
struct Vertex
{
alignas(16) Vec4f position = Vec4f{ 0.0f, 0.0f, 0.0f, 1.0f };
alignas(16) Vec4f color = Vec4f{ 1.0f, 1.0f, 1.0f, 1.0f };
alignas(16) Vec4f normal = Vec4f{ 0.0f, 0.0f, 0.0f, 1.0f };
alignas(16) Vec2f uv = Vec2f{ 0.0f, 0.0f };
Vertex() = default;
Vertex(Vec4f p, Vec4f c, Vec4f n, Vec2f u) : position(std::move(p)), color(std::move(c)), normal(std::move(n)), uv(std::move(u)) {}
[[nodiscard]] inline static VkVertexInputBindingDescription GetBindingDescription();
[[nodiscard]] inline static std::array<VkVertexInputAttributeDescription, 4> GetAttributeDescriptions();
};
}
#include <Renderer/Vertex.inl>
#endif

View File

@@ -0,0 +1,41 @@
#pragma once
#include <Renderer/Vertex.h>
namespace Scop
{
VkVertexInputBindingDescription Vertex::GetBindingDescription()
{
VkVertexInputBindingDescription binding_description{};
binding_description.binding = 0;
binding_description.stride = sizeof(Vertex);
binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
return binding_description;
}
std::array<VkVertexInputAttributeDescription, 4> Vertex::GetAttributeDescriptions()
{
std::array<VkVertexInputAttributeDescription, 4> attribute_descriptions;
attribute_descriptions[0].binding = 0;
attribute_descriptions[0].location = 0;
attribute_descriptions[0].format = VK_FORMAT_R32G32B32A32_SFLOAT;
attribute_descriptions[0].offset = offsetof(Vertex, position);
attribute_descriptions[1].binding = 0;
attribute_descriptions[1].location = 1;
attribute_descriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
attribute_descriptions[1].offset = offsetof(Vertex, color);
attribute_descriptions[2].binding = 0;
attribute_descriptions[2].location = 2;
attribute_descriptions[2].format = VK_FORMAT_R32G32B32A32_SFLOAT;
attribute_descriptions[2].offset = offsetof(Vertex, normal);
attribute_descriptions[3].binding = 0;
attribute_descriptions[3].location = 3;
attribute_descriptions[3].format = VK_FORMAT_R32G32_SFLOAT;
attribute_descriptions[3].offset = offsetof(Vertex, uv);
return attribute_descriptions;
}
}

View File

@@ -0,0 +1,21 @@
#ifndef __SCOP_VIEWER_DATA__
#define __SCOP_VIEWER_DATA__
#include <Maths/Mat4.h>
#include <Maths/Vec3.h>
namespace Scop
{
struct ViewerData
{
Mat4f projection_matrix;
Mat4f inv_projection_matrix;
Mat4f view_matrix;
Mat4f inv_view_matrix;
Mat4f view_proj_matrix;
Mat4f inv_view_proj_matrix;
alignas(16) Vec3f camera_position;
};
}
#endif

View File

@@ -0,0 +1,127 @@
// No header guard
#ifdef VK_VERSION_1_0
#ifdef SCOP_VULKAN_GLOBAL_FUNCTION
SCOP_VULKAN_GLOBAL_FUNCTION(vkCreateInstance)
SCOP_VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceExtensionProperties)
SCOP_VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceLayerProperties)
SCOP_VULKAN_GLOBAL_FUNCTION(vkGetInstanceProcAddr)
#endif
#ifdef SCOP_VULKAN_INSTANCE_FUNCTION
SCOP_VULKAN_INSTANCE_FUNCTION(vkCreateDevice)
SCOP_VULKAN_INSTANCE_FUNCTION(vkDestroyInstance)
SCOP_VULKAN_INSTANCE_FUNCTION(vkEnumerateDeviceExtensionProperties)
SCOP_VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetDeviceProcAddr)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFormatProperties)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceImageFormatProperties)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceMemoryProperties)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties)
#ifdef DEBUG
#ifdef VK_EXT_debug_utils
SCOP_VULKAN_INSTANCE_FUNCTION(vkSetDebugUtilsObjectNameEXT)
//SCOP_VULKAN_INSTANCE_FUNCTION(vkSetDebugUtilsObjectTagEXT)
#endif
#endif
#endif
#ifdef SCOP_VULKAN_DEVICE_FUNCTION
SCOP_VULKAN_DEVICE_FUNCTION(vkAllocateCommandBuffers)
SCOP_VULKAN_DEVICE_FUNCTION(vkAllocateDescriptorSets)
SCOP_VULKAN_DEVICE_FUNCTION(vkAllocateMemory)
SCOP_VULKAN_DEVICE_FUNCTION(vkBeginCommandBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkBindBufferMemory)
SCOP_VULKAN_DEVICE_FUNCTION(vkBindImageMemory)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdBeginRenderPass)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdBindDescriptorSets)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdBindIndexBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdBindPipeline)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdBindVertexBuffers)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdClearAttachments)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdClearColorImage)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdClearDepthStencilImage)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdCopyBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdCopyBufferToImage)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdCopyImage)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdCopyImageToBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdDraw)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdDrawIndexed)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdEndRenderPass)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdPipelineBarrier)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdPushConstants)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdSetScissor)
SCOP_VULKAN_DEVICE_FUNCTION(vkCmdSetViewport)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateCommandPool)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateDescriptorPool)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateDescriptorSetLayout)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateFence)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateFramebuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateGraphicsPipelines)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateImage)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateImageView)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreatePipelineLayout)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateRenderPass)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateSampler)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateSemaphore)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateShaderModule)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyCommandPool)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyDescriptorPool)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyDescriptorSetLayout)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyDevice)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyFence)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyFramebuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyImage)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyImageView)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyPipeline)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyPipelineLayout)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyRenderPass)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroySampler)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroySemaphore)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroyShaderModule)
SCOP_VULKAN_DEVICE_FUNCTION(vkDeviceWaitIdle)
SCOP_VULKAN_DEVICE_FUNCTION(vkEndCommandBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkFlushMappedMemoryRanges)
SCOP_VULKAN_DEVICE_FUNCTION(vkFreeCommandBuffers)
SCOP_VULKAN_DEVICE_FUNCTION(vkFreeMemory)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetBufferMemoryRequirements)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetDeviceMemoryCommitment)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetDeviceQueue)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetFenceStatus)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetImageMemoryRequirements)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetImageSubresourceLayout)
SCOP_VULKAN_DEVICE_FUNCTION(vkInvalidateMappedMemoryRanges)
SCOP_VULKAN_DEVICE_FUNCTION(vkMapMemory)
SCOP_VULKAN_DEVICE_FUNCTION(vkQueueSubmit)
SCOP_VULKAN_DEVICE_FUNCTION(vkQueueWaitIdle)
SCOP_VULKAN_DEVICE_FUNCTION(vkResetCommandBuffer)
SCOP_VULKAN_DEVICE_FUNCTION(vkResetDescriptorPool)
SCOP_VULKAN_DEVICE_FUNCTION(vkResetEvent)
SCOP_VULKAN_DEVICE_FUNCTION(vkResetFences)
SCOP_VULKAN_DEVICE_FUNCTION(vkUnmapMemory)
SCOP_VULKAN_DEVICE_FUNCTION(vkUpdateDescriptorSets)
SCOP_VULKAN_DEVICE_FUNCTION(vkWaitForFences)
#endif
#endif
#ifdef VK_KHR_swapchain
#ifdef SCOP_VULKAN_DEVICE_FUNCTION
SCOP_VULKAN_DEVICE_FUNCTION(vkAcquireNextImageKHR)
SCOP_VULKAN_DEVICE_FUNCTION(vkCreateSwapchainKHR)
SCOP_VULKAN_DEVICE_FUNCTION(vkDestroySwapchainKHR)
SCOP_VULKAN_DEVICE_FUNCTION(vkGetSwapchainImagesKHR)
SCOP_VULKAN_DEVICE_FUNCTION(vkQueuePresentKHR)
#endif
#endif
#ifdef VK_KHR_surface
#ifdef SCOP_VULKAN_INSTANCE_FUNCTION
SCOP_VULKAN_INSTANCE_FUNCTION(vkDestroySurfaceKHR)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR)
SCOP_VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR)
#endif
#endif

14
ScopEngine/Runtime/Includes/ScopCore.h git.filemode.normal_file
View File

@@ -0,0 +1,14 @@
#ifndef __SCOP_CORE__
#define __SCOP_CORE__
#include <Core/Engine.h>
#include <Core/CLI.h>
#include <Core/EventBase.h>
#include <Core/EventBus.h>
#include <Core/EventListener.h>
#include <Core/Format.h>
#include <Core/Logs.h>
#include <Core/NativeScript.h>
#include <Core/Script.h>
#endif

15
ScopEngine/Runtime/Includes/ScopGraphics.h git.filemode.normal_file
View File

@@ -0,0 +1,15 @@
#ifndef __SCOP_GRAPHICS__
#define __SCOP_GRAPHICS__
#include <Graphics/Actor.h>
#include <Graphics/Material.h>
#include <Graphics/Mesh.h>
#include <Graphics/Model.h>
#include <Graphics/Scene.h>
#include <Graphics/MeshFactory.h>
#include <Graphics/Cameras/Base.h>
#include <Graphics/Cameras/FirstPerson3D.h>
#include <Graphics/Loaders/OBJ.h>
#include <Graphics/Loaders/BMP.h>
#endif

14
ScopEngine/Runtime/Includes/ScopMaths.h git.filemode.normal_file
View File

@@ -0,0 +1,14 @@
#ifndef __SCOP_MATHS__
#define __SCOP_MATHS__
#include <Maths/Vec2.h>
#include <Maths/Vec3.h>
#include <Maths/Vec4.h>
#include <Maths/MathsUtils.h>
#include <Maths/Mat4.h>
#include <Maths/Angles.h>
#include <Maths/Constants.h>
#include <Maths/EulerAngles.h>
#include <Maths/Quaternions.h>
#endif

7
ScopEngine/Runtime/Includes/ScopPlatform.h git.filemode.normal_file
View File

@@ -0,0 +1,7 @@
#ifndef __SCOP_PLATFORM__
#define __SCOP_PLATFORM__
#include <Platform/Inputs.h>
#include <Platform/Window.h>
#endif

13
ScopEngine/Runtime/Includes/ScopRenderer.h git.filemode.normal_file
View File

@@ -0,0 +1,13 @@
#ifndef __SCOP_RENDERER__
#define __SCOP_RENDERER__
#include <Renderer/Pipelines/Graphics.h>
#include <Renderer/Pipelines/Pipeline.h>
#include <Renderer/Pipelines/Shader.h>
#include <Renderer/Image.h>
#include <Renderer/RenderCore.h>
#include <Renderer/Renderer.h>
#include <Renderer/ScenesRenderer.h>
#include <Renderer/Vertex.h>
#endif

9
ScopEngine/Runtime/Includes/ScopUtils.h git.filemode.normal_file
View File

@@ -0,0 +1,9 @@
#ifndef __SCOP_UTILS__
#define __SCOP_UTILS__
#include <Utils/Ansi.h>
#include <Utils/NonCopyable.h>
#include <Utils/NonMovable.h>
#include <Utils/NonOwningPtr.h>
#endif

44
ScopEngine/Runtime/Includes/Utils/Ansi.h git.filemode.normal_file
View File

@@ -0,0 +1,44 @@
#ifndef __SCOPE_UTILS_ANSI__
#define __SCOPE_UTILS_ANSI__
#include <cstdint>
#include <ostream>
namespace Scop
{
enum class Ansi : std::uint32_t
{
red = 31,
green = 32,
blue = 34,
def = 0,
black = 30,
yellow = 33,
magenta = 35,
cyan = 36,
white = 37,
bg_red = 41,
bg_green = 42,
bg_blue = 44,
bg_def = 0,
bg_black = 40,
bg_yellow = 43,
bg_magenta = 45,
bg_cyan = 46,
bg_white = 47,
reset = 0,
bold = 1,
underline = 4,
inverse = 7,
bold_off = 21,
underline_off = 24,
inverse_off = 27
};
}
inline std::ostream& operator<<(std::ostream& os, Scop::Ansi ansi)
{
return os << "\033[1;" << std::to_string(static_cast<std::uint32_t>(ansi)) << "m";
}
#endif

46
ScopEngine/Runtime/Includes/Utils/Buffer.h git.filemode.normal_file
View File

@@ -0,0 +1,46 @@
#ifndef __SCOP_CPU_BUFFER__
#define __SCOP_CPU_BUFFER__
#include <cstdint>
#include <cstring>
#include <memory>
#include <Core/Logs.h>
namespace Scop
{
class CPUBuffer
{
public:
CPUBuffer() {}
CPUBuffer(std::size_t size) try : m_data(new std::uint8_t[size]), m_size(size)
{}
catch(...)
{
FatalError("memory allocation for a CPU buffer failed");
}
[[nodiscard]] inline CPUBuffer Duplicate() const
{
CPUBuffer buffer(m_size);
std::memcpy(buffer.GetData(), m_data.get(), m_size);
return buffer;
}
inline bool Empty() const { return m_size == 0; }
[[nodiscard]] inline std::size_t GetSize() const noexcept { return m_size; }
template<typename T>
[[nodiscard]] inline T* GetDataAs() const { return reinterpret_cast<T*>(m_data.get()); }
[[nodiscard]] inline std::uint8_t* GetData() const { return m_data.get(); }
inline operator bool() const { return (bool)m_data; }
~CPUBuffer() = default;
private:
std::shared_ptr<std::uint8_t[]> m_data;
std::size_t m_size = 0;
};
}
#endif

View File

@@ -0,0 +1,20 @@
#ifndef __SCOPE_UTILS_NON_COPYABLE__
#define __SCOPE_UTILS_NON_COPYABLE__
namespace Scop
{
class NonCopyable
{
protected:
NonCopyable() = default;
virtual ~NonCopyable() = default;
public:
NonCopyable(const NonCopyable&) = delete;
NonCopyable(NonCopyable&&) noexcept = default;
NonCopyable& operator=(const NonCopyable&) = delete;
NonCopyable& operator=(NonCopyable&&) noexcept = default;
};
}
#endif

View File

@@ -0,0 +1,20 @@
#ifndef __SCOPE_UTILS_NON_MOVABLE__
#define __SCOPE_UTILS_NON_MOVABLE__
namespace Scop
{
class NonMovable
{
protected:
NonMovable() = default;
virtual ~NonMovable() = default;
public:
NonMovable(const NonMovable&) = default;
NonMovable(NonMovable&&) noexcept = delete;
NonMovable& operator=(const NonMovable&) = default;
NonMovable& operator=(NonMovable&&) noexcept = delete;
};
}
#endif

View File

@@ -0,0 +1,33 @@
#ifndef __SCOPE_UTILS_NON_OWNING_PTR__
#define __SCOPE_UTILS_NON_OWNING_PTR__
namespace Scop
{
template<typename T>
class NonOwningPtr
{
public:
NonOwningPtr(T* ptr = nullptr);
NonOwningPtr(const NonOwningPtr&) = default;
NonOwningPtr(NonOwningPtr&& ptr) noexcept;
NonOwningPtr& operator=(T* ptr);
NonOwningPtr& operator=(const NonOwningPtr&) = default;
NonOwningPtr& operator=(NonOwningPtr&& ptr) noexcept;
[[nodiscard]] inline operator bool() const noexcept;
[[nodiscard]] inline T* Get() const noexcept;
[[nodiscard]] inline T* operator->() const noexcept;
[[nodiscard]] inline T& operator*() const noexcept;
~NonOwningPtr() = default;
private:
T* m_ptr = nullptr;
};
}
#include <Utils/NonOwningPtr.inl>
#endif

View File

@@ -0,0 +1,53 @@
#pragma once
#include <Utils/NonOwningPtr.h>
namespace Scop
{
template<typename T>
NonOwningPtr<T>::NonOwningPtr(T* ptr) : m_ptr(ptr) {}
template<typename T>
NonOwningPtr<T>::NonOwningPtr(NonOwningPtr<T>&& ptr) noexcept : m_ptr(ptr.m_ptr)
{
ptr.m_ptr = nullptr;
}
template<typename T>
NonOwningPtr<T>& NonOwningPtr<T>::operator=(T* ptr)
{
m_ptr = ptr;
return *this;
}
template<typename T>
NonOwningPtr<T>& NonOwningPtr<T>::operator=(NonOwningPtr&& ptr) noexcept
{
m_ptr = ptr.m_ptr;
ptr.m_ptr = nullptr;
return *this;
}
template<typename T>
NonOwningPtr<T>::operator bool() const noexcept
{
return m_ptr != nullptr;
}
template<typename T>
T* NonOwningPtr<T>::Get() const noexcept
{
return m_ptr;
}
template<typename T>
T* NonOwningPtr<T>::operator->() const noexcept
{
return m_ptr;
}
template<typename T>
T& NonOwningPtr<T>::operator*() const noexcept
{
return *m_ptr;
}
}

124
ScopEngine/Runtime/Sources/Core/Engine.cpp git.filemode.normal_file
View File

@@ -0,0 +1,124 @@
#include <Core/Engine.h>
#include <Renderer/RenderCore.h>
#include <SDL2/SDL.h>
#include <Core/Logs.h>
#include <Core/EventBus.h>
#include <csignal>
namespace Scop
{
namespace Internal
{
struct InterruptEvent : public EventBase
{
Event What() const override { return Event::QuitEventCode; }
};
struct SceneChangedEvent : public EventBase
{
Event What() const override { return Event::SceneHasChangedEventCode; }
};
}
void FatalErrorEventHandle()
{
std::abort();
}
void SignalHandler([[maybe_unused]] int sig)
{
EventBus::Send("__ScopEngine", Internal::InterruptEvent{});
}
ScopEngine* ScopEngine::s_instance = nullptr;
ScopEngine::ScopEngine(int ac, char** av, const std::string& title, std::uint32_t width, std::uint32_t height, std::filesystem::path assets_path)
: m_renderer(), m_window(title, width, height), m_assets_path(std::move(assets_path))
#ifdef DEBUG
,m_imgui(&m_renderer)
#endif
{
s_instance = this;
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
if(event.What() == Event::FatalErrorEventCode)
FatalErrorEventHandle();
if(event.What() == Event::QuitEventCode)
this->Quit();
};
EventBus::RegisterListener({ functor, "__ScopEngine" });
m_cli.Feed(ac, av);
signal(SIGINT, SignalHandler);
SDL_SetHint("SDL_MOUSE_RELATIVE_MODE_WARP", "1");
SDL_SetHint("SDL_MOUSE_RELATIVE_MODE_CENTER", "1");
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) != 0)
FatalError("SDL error: unable to init all subsystems : %", SDL_GetError());
p_renderer_core = std::make_unique<RenderCore>();
m_renderer.Init(&m_window);
m_scene_renderer.Init();
#ifdef DEBUG
m_imgui.Init(m_inputs);
#endif
}
ScopEngine& ScopEngine::Get() noexcept
{
Verify(s_instance != nullptr, "ScopEngine has not been instanciated");
return *s_instance;
}
void ScopEngine::Run()
{
Verify(p_current_scene, "no main scene registered");
float old_timestep = static_cast<float>(SDL_GetTicks64()) / 1000.0f;
p_current_scene->Init(&m_renderer);
while(m_running)
{
float current_timestep = (static_cast<float>(SDL_GetTicks64()) / 1000.0f) - old_timestep;
old_timestep = static_cast<float>(SDL_GetTicks64()) / 1000.0f;
m_inputs.Update();
m_window.FetchWindowInfos();
p_current_scene->Update(m_inputs, current_timestep, static_cast<float>(m_window.GetWidth()) / static_cast<float>(m_window.GetHeight()));
if(m_scene_changed)
{
RenderCore::Get().WaitDeviceIdle();
EventBus::SendBroadcast(Internal::SceneChangedEvent{});
m_scene_changed = false;
continue;
}
m_renderer.BeginFrame();
m_scene_renderer.Render(*p_current_scene, m_renderer);
#ifdef DEBUG
m_imgui.BeginFrame();
m_imgui.DisplayRenderStatistics();
m_imgui.EndFrame();
#endif
m_renderer.EndFrame();
if(m_running)
m_running = !m_inputs.HasRecievedCloseEvent();
}
}
ScopEngine::~ScopEngine()
{
RenderCore::Get().WaitDeviceIdle();
m_main_scene->Destroy();
m_window.Destroy();
#ifdef DEBUG
m_imgui.Destroy();
#endif
m_scene_renderer.Destroy();
m_renderer.Destroy();
p_renderer_core.reset();
SDL_Quit();
Message("Successfully executed !");
s_instance = nullptr;
}
}

24
ScopEngine/Runtime/Sources/Core/EventBus.cpp git.filemode.normal_file
View File

@@ -0,0 +1,24 @@
#include <Core/EventBus.h>
#include <Core/Logs.h>
namespace Scop
{
void EventBus::Send(const std::string& listener_name, const EventBase& event)
{
for(const EventListener& listener : s_listeners)
{
if(listener.GetName() == listener_name)
{
listener.Call(event);
return;
}
}
Warning("Event Bus : listener not found, '%'", listener_name);
}
void EventBus::SendBroadcast(const EventBase& event)
{
for(const EventListener& listener : s_listeners)
listener.Call(event);
}
}

View File

@@ -0,0 +1,8 @@
#include <Core/EventListener.h>
namespace Scop
{
EventListener::EventListener(std::function<void(const EventBase&)> functor, std::string name)
: m_listen_functor(std::move(functor)), m_name(std::move(name))
{}
}

66
ScopEngine/Runtime/Sources/Core/Logs.cpp git.filemode.normal_file
View File

@@ -0,0 +1,66 @@
#include <ctime>
#include <string>
#include <string_view>
#include <Core/Logs.h>
#include <Utils/Ansi.h>
#include <Core/EventBase.h>
#include <Core/EventBus.h>
namespace Scop
{
namespace Internal
{
struct FatalErrorEvent : public EventBase
{
Event What() const override { return Event::FatalErrorEventCode; }
};
}
void Logs::Report(LogType type, std::string message)
{
Report(type, 0, {}, {}, std::move(message));
}
void Logs::Report(LogType type, unsigned int line, std::string_view file, std::string_view function, std::string message)
{
using namespace std::literals;
#ifndef VOX_DEBUG
if(type == LogType::Debug && std::getenv("VOX_DEBUG_LOGS") == nullptr)
return;
#endif
std::string code_infos;
if((type == LogType::Error || type == LogType::FatalError) && !file.empty() && !function.empty())
{
code_infos += "{in file '"s;
code_infos += file;
code_infos += "', line "s + std::to_string(line) + ", in function '"s;
code_infos += function;
code_infos += "'} "s;
}
switch(type)
{
case LogType::Debug: std::cout << Ansi::blue << "[Scop Debug] "; break;
case LogType::Message: std::cout << Ansi::blue << "[Scop Message] "; break;
case LogType::Warning: std::cout << Ansi::magenta << "[Scop Warning] "; break;
case LogType::Error: std::cerr << Ansi::red << "[Scop Error] "; break;
case LogType::FatalError: std::cerr << Ansi::red << "[Scop Fatal Error] "; break;
default: break;
}
std::time_t now = time(0);
std::tm tstruct = *localtime(&now);
char buffer[80];
std::strftime(buffer, sizeof(buffer), "[%X] ", &tstruct);
std::cout << Ansi::yellow << buffer << Ansi::def << code_infos << message << std::endl;
if(type == LogType::FatalError)
{
std::cout << Ansi::bg_red << "Fatal Error: emergency exit" << Ansi::bg_def << std::endl;
EventBus::Send("__ScopEngine", Internal::FatalErrorEvent{});
}
}
}

View File

@@ -0,0 +1,261 @@
#include <Debug/ImGuiRenderer.h>
#include <Renderer/Buffer.h>
#include <Renderer/Image.h>
#include <Platform/Inputs.h>
#include <Core/EventBus.h>
#undef DebugLog
#include <imgui.h>
#include <backends/imgui_impl_sdl2.h>
#include <backends/imgui_impl_vulkan.h>
#include <algorithm>
namespace Scop
{
ImGuiRenderer::ImGuiRenderer(NonOwningPtr<Renderer> renderer) : p_renderer(renderer)
{}
void ImGuiRenderer::Init(Inputs& inputs)
{
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
if(event.What() == Event::ResizeEventCode)
{
kvfDestroyRenderPass(RenderCore::Get().GetDevice(), m_renderpass);
std::vector<VkAttachmentDescription> attachments;
const Image& image = p_renderer->GetSwapchain().GetSwapchainImages()[0];
attachments.push_back(kvfBuildAttachmentDescription(KVF_IMAGE_COLOR, image.GetFormat(), image.GetLayout(), image.GetLayout(), false, VK_SAMPLE_COUNT_1_BIT));
m_renderpass = kvfCreateRenderPass(RenderCore::Get().GetDevice(), attachments.data(), attachments.size(), VK_PIPELINE_BIND_POINT_GRAPHICS);
CreateFramebuffers();
ImGui_ImplVulkan_SetMinImageCount(kvfGetSwapchainMinImagesCount(p_renderer->GetSwapchain().Get()));
}
};
EventBus::RegisterListener({ functor, std::to_string((std::uintptr_t)(void**)this) });
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui::GetIO().IniFilename = nullptr;
SetTheme();
VkDescriptorPoolSize pool_sizes[] = {
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
};
VkDescriptorPoolCreateInfo pool_info{};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes);
pool_info.poolSizeCount = (std::uint32_t)IM_ARRAYSIZE(pool_sizes);
pool_info.pPoolSizes = pool_sizes;
RenderCore::Get().vkCreateDescriptorPool(RenderCore::Get().GetDevice(), &pool_info, nullptr, &m_pool);
// Setup Platform/Renderer bindings
ImGui_ImplVulkan_LoadFunctions([](const char* function_name, void* vulkan_instance) {
return RenderCore::Get().vkGetInstanceProcAddr(*(reinterpret_cast<VkInstance*>(vulkan_instance)), function_name);
}, &RenderCore::Get().GetInstanceRef());
std::vector<VkAttachmentDescription> attachments;
const Image& image = p_renderer->GetSwapchain().GetSwapchainImages()[0];
attachments.push_back(kvfBuildAttachmentDescription(KVF_IMAGE_COLOR, image.GetFormat(), image.GetLayout(), image.GetLayout(), false, VK_SAMPLE_COUNT_1_BIT));
m_renderpass = kvfCreateRenderPass(RenderCore::Get().GetDevice(), attachments.data(), attachments.size(), VK_PIPELINE_BIND_POINT_GRAPHICS);
CreateFramebuffers();
ImGui_ImplSDL2_InitForVulkan(p_renderer->GetWindow()->GetSDLWindow());
ImGui_ImplVulkan_InitInfo init_info{};
init_info.Instance = RenderCore::Get().GetInstance();
init_info.PhysicalDevice = RenderCore::Get().GetPhysicalDevice();
init_info.Device = RenderCore::Get().GetDevice();
init_info.QueueFamily = kvfGetDeviceQueueFamily(RenderCore::Get().GetDevice(), KVF_GRAPHICS_QUEUE);
init_info.Queue = kvfGetDeviceQueue(RenderCore::Get().GetDevice(), KVF_GRAPHICS_QUEUE);
init_info.DescriptorPool = m_pool;
init_info.Allocator = nullptr;
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
init_info.Subpass = 0;
init_info.MinImageCount = kvfGetSwapchainMinImagesCount(p_renderer->GetSwapchain().Get());
init_info.ImageCount = p_renderer->GetSwapchain().GetSwapchainImages().size();
init_info.CheckVkResultFn = [](VkResult result){ kvfCheckVk(result); };
init_info.RenderPass = m_renderpass;
ImGui_ImplVulkan_Init(&init_info);
inputs.AddEventUpdateHook(ImGui_ImplSDL2_ProcessEvent);
}
void ImGuiRenderer::Destroy()
{
RenderCore::Get().WaitDeviceIdle();
ImGui_ImplVulkan_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
for(VkFramebuffer fb : m_framebuffers)
kvfDestroyFramebuffer(RenderCore::Get().GetDevice(), fb);
m_framebuffers.clear();
kvfDestroyRenderPass(RenderCore::Get().GetDevice(), m_renderpass);
RenderCore::Get().vkDestroyDescriptorPool(RenderCore::Get().GetDevice(), m_pool, nullptr);
}
bool ImGuiRenderer::BeginFrame()
{
ImGui_ImplVulkan_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
return true;
}
void ImGuiRenderer::DisplayRenderStatistics()
{
static std::array<std::string, 5> gpu_types_string = {
"Other",
"Integrated",
"Graphics Card",
"Virtual",
"CPU"
};
static std::array<float, 1000> frame_histogram;
float delta = ImGui::GetIO().DeltaTime;
ImGui::SetNextWindowPos(ImVec2{ 20.0f, 20.0f }, ImGuiCond_FirstUseEver);
if(ImGui::Begin("Render Statistics", nullptr, ImGuiWindowFlags_NoResize))
{
VkPhysicalDeviceProperties props;
RenderCore::Get().vkGetPhysicalDeviceProperties(RenderCore::Get().GetPhysicalDevice(), &props);
ImGui::Text("GPU in use:\n%s", props.deviceName);
ImGui::Text("GPU type: %s", gpu_types_string[static_cast<int>(props.deviceType)].c_str());
ImGui::Separator();
ImGui::Text("Frame time %.3fms", delta);
ImGui::PlotLines("##Frames_Histogram", frame_histogram.data(), frame_histogram.size(), 0, nullptr, 0.0f, 1.0f, ImVec2(ImGui::GetContentRegionAvail().x, 40.0f));
ImGui::Text("FPS %.0f", ImGui::GetIO().Framerate);
ImGui::Separator();
ImGui::Text("Swapchain images count %ld", p_renderer->GetSwapchain().GetSwapchainImages().size());
ImGui::Text("Drawcalls %ld", p_renderer->GetDrawCallsCounterRef());
ImGui::Text("Polygon drawn %ld", p_renderer->GetPolygonDrawnCounterRef());
ImGui::Text("Allocations count %ld", RenderCore::Get().GetAllocator().GetAllocationsCount());
ImGui::Text("Buffer count %ld", GPUBuffer::GetBufferCount());
ImGui::Text("Image count %ld", Image::GetImageCount());
ImGui::Text("Window dimensions: %ux%u", p_renderer->GetWindow()->GetWidth(), p_renderer->GetWindow()->GetHeight());
}
ImGui::End();
std::rotate(frame_histogram.begin(), frame_histogram.begin() + 1, frame_histogram.end());
frame_histogram.back() = delta;
}
void ImGuiRenderer::EndFrame()
{
VkFramebuffer fb = m_framebuffers[p_renderer->GetSwapchain().GetImageIndex()];
ImGui::Render();
ImDrawData* draw_data = ImGui::GetDrawData();
if(draw_data->DisplaySize.x >= 0.0f && draw_data->DisplaySize.y >= 0.0f)
{
VkExtent2D fb_extent = kvfGetFramebufferSize(fb);
kvfBeginRenderPass(m_renderpass, p_renderer->GetActiveCommandBuffer(), fb, fb_extent, nullptr, 0);
ImGui_ImplVulkan_RenderDrawData(draw_data, p_renderer->GetActiveCommandBuffer());
RenderCore::Get().vkCmdEndRenderPass(p_renderer->GetActiveCommandBuffer());
}
}
void ImGuiRenderer::CreateFramebuffers()
{
for(VkFramebuffer fb : m_framebuffers)
kvfDestroyFramebuffer(RenderCore::Get().GetDevice(), fb);
m_framebuffers.clear();
std::vector<VkAttachmentDescription> attachments;
std::vector<VkImageView> attachment_views;
const Image& image = p_renderer->GetSwapchain().GetSwapchainImages()[0];
attachments.push_back(kvfBuildAttachmentDescription((kvfIsDepthFormat(image.GetFormat()) ? KVF_IMAGE_DEPTH : KVF_IMAGE_COLOR), image.GetFormat(), image.GetLayout(), image.GetLayout(), false, VK_SAMPLE_COUNT_1_BIT));
attachment_views.push_back(image.GetImageView());
for(const Image& image : p_renderer->GetSwapchain().GetSwapchainImages())
{
attachment_views[0] = image.GetImageView();
m_framebuffers.push_back(kvfCreateFramebuffer(RenderCore::Get().GetDevice(), m_renderpass, attachment_views.data(), attachment_views.size(), { .width = image.GetWidth(), .height = image.GetHeight() }));
}
}
void ImGuiRenderer::SetTheme()
{
ImGuiStyle* style = &ImGui::GetStyle();
ImVec4* colors = style->Colors;
ImGui::StyleColorsDark();
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.10f, 0.10f, 0.10f, 0.50f);
colors[ImGuiCol_ChildBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f);
colors[ImGuiCol_PopupBg] = ImVec4(0.19f, 0.19f, 0.19f, 0.92f);
colors[ImGuiCol_Border] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.05f, 0.05f, 0.05f, 0.24f);
colors[ImGuiCol_FrameBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.19f, 0.19f, 0.19f, 0.54f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f);
colors[ImGuiCol_TitleBg] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.40f, 0.54f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f);
colors[ImGuiCol_CheckMark] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f);
colors[ImGuiCol_Button] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.19f, 0.19f, 0.19f, 0.54f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f);
colors[ImGuiCol_Header] = ImVec4(0.05f, 0.05f, 0.05f, 0.52f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.05f, 0.05f, 0.05f, 0.36f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.20f, 0.22f, 0.23f, 0.33f);
colors[ImGuiCol_Separator] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f);
colors[ImGuiCol_Tab] = ImVec4(0.05f, 0.05f, 0.05f, 0.52f);
colors[ImGuiCol_TabHovered] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
colors[ImGuiCol_TabActive] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.05f, 0.05f, 0.05f, 0.52f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.52f);
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.05f, 0.05f, 0.05f, 0.52f);
colors[ImGuiCol_TableBorderLight] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f);
colors[ImGuiCol_TableRowBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f);
colors[ImGuiCol_DragDropTarget] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f);
colors[ImGuiCol_NavHighlight] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 0.00f, 0.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
style->ChildRounding = 4.0f;
style->FrameBorderSize = 1.0f;
style->FrameRounding = 4.0f;
style->GrabMinSize = 7.0f;
style->PopupRounding = 2.0f;
style->ScrollbarRounding = 12.0f;
style->ScrollbarSize = 13.0f;
style->TabBorderSize = 0.0f;
style->TabRounding = 5.0f;
style->WindowRounding = 0.0f;
style->WindowBorderSize = 1.0f;
style->AntiAliasedLines = true;
style->AntiAliasedFill = true;
style->TabBorderSize = 2.0f;
}
}

29
ScopEngine/Runtime/Sources/Graphics/Actor.cpp git.filemode.normal_file
View File

@@ -0,0 +1,29 @@
#include <Graphics/Actor.h>
#include <Platform/Inputs.h>
namespace Scop
{
Actor::Actor()
{
if(p_script)
p_script->OnInit(this);
}
Actor::Actor(Model model) : m_model(std::move(model))
{
if(p_script)
p_script->OnInit(this);
}
void Actor::Update(NonOwningPtr<Scene> scene, Inputs& input, float delta)
{
if(p_script)
p_script->OnUpdate(scene, this, input, delta);
}
Actor::~Actor()
{
if(p_script)
p_script->OnQuit(this);
}
}

View File

@@ -0,0 +1,82 @@
#include <Graphics/Cameras/FirstPerson3D.h>
#include <Platform/Inputs.h>
namespace Scop
{
FirstPerson3D::FirstPerson3D() : BaseCamera(), m_up(0, 1, 0), m_position(0.0, 0.0, 0.0)
{}
FirstPerson3D::FirstPerson3D(Vec3f position, float fov) : BaseCamera(), m_position(std::move(position)), m_up(0, 1, 0), m_fov(fov)
{}
void FirstPerson3D::Update(class Inputs& input, float aspect, float timestep)
{
UpdateView();
m_target = m_position + m_direction;
m_view = Mat4f::LookAt(m_position, m_target, m_up);
m_proj = Mat4f::Perspective(RadianAnglef(m_fov), aspect, 0.1f, 100'000.f);
if(input.IsKeyPressed(SDL_SCANCODE_F1))
{
m_inputs_blocked = true;
input.ReleaseMouse();
}
if(input.IsKeyPressed(SDL_SCANCODE_F2))
{
m_inputs_blocked = false;
input.GrabMouse();
}
if(m_inputs_blocked)
return;
if(input.IsMouseGrabbed())
{
m_theta -= input.GetXRel() * m_sensivity;
m_phi -= input.GetYRel() * m_sensivity;
}
if(input.IsKeyPressed(SDL_SCANCODE_ESCAPE))
input.ReleaseMouse();
else if(input.IsMouseButtonPressed(SDL_BUTTON_LEFT))
input.GrabMouse();
m_mov = Vec3f(0.0);
if(input.IsKeyPressed(SDL_SCANCODE_W) || input.IsKeyPressed(SDL_SCANCODE_UP))
m_mov -= m_forward;
if(input.IsKeyPressed(SDL_SCANCODE_S) || input.IsKeyPressed(SDL_SCANCODE_DOWN))
m_mov += m_forward;
if(input.IsKeyPressed(SDL_SCANCODE_D) || input.IsKeyPressed(SDL_SCANCODE_RIGHT))
m_mov -= m_left;
if(input.IsKeyPressed(SDL_SCANCODE_A) || input.IsKeyPressed(SDL_SCANCODE_LEFT))
m_mov += m_left;
if(input.IsKeyPressed(SDL_SCANCODE_LSHIFT) || input.IsKeyPressed(SDL_SCANCODE_RSHIFT))
m_mov -= m_up;
if(input.IsKeyPressed(SDL_SCANCODE_SPACE))
m_mov += m_up;
if(input.IsMouseWheelUp())
m_speed_factor *= 1.5f;
if(input.IsMouseWheelDown())
m_speed_factor /= 1.5f;
m_position += m_mov * m_speed * m_speed_factor * timestep;
}
void FirstPerson3D::UpdateView()
{
m_phi = m_phi > 89 ? 89 : m_phi;
m_phi = m_phi < -89 ? -89 : m_phi;
// Spherical coordinate system
m_direction.x = std::cos(m_phi * Pi<float>() / 180) * std::cos(m_theta * Pi<float>() / 180);
m_direction.y = std::sin(m_phi * Pi<float>() / 180);
m_direction.z = std::cos(m_phi * Pi<float>() / 180) * std::sin(-m_theta * Pi<float>() / 180);
m_left = m_up.CrossProduct(m_direction);
m_left.Normalize();
m_forward = m_up.CrossProduct(m_left);
m_forward.Normalize();
}
}

View File

@@ -0,0 +1,45 @@
#include <Graphics/Loaders/BMP.h>
#include <Maths/Vec3.h>
#include <fstream>
namespace Scop
{
CPUBuffer LoadBMPFile(const std::filesystem::path& path, Vec2ui32& dimensions)
{
if(path.extension() != ".bmp")
{
Error("BMP loader : not a BMP file, %", path);
return {};
}
std::ifstream file(path, std::ios::binary);
if(!file.is_open())
{
Error("BMP loader : could not open %", path);
return {};
}
std::int16_t bpp;
file.seekg(18, std::ios_base::beg);
file.read(reinterpret_cast<char*>(&dimensions.x), sizeof(dimensions.x));
file.read(reinterpret_cast<char*>(&dimensions.y), sizeof(dimensions.y));
file.seekg(28, std::ios_base::beg);
file.read(reinterpret_cast<char*>(&bpp), sizeof(bpp));
file.seekg(54, std::ios_base::beg);
if(bpp != 24)
Warning("BMP loader : warning while loadeing %, cannot handle yet different color palette sizes", path);
CPUBuffer buffer{ dimensions.x * dimensions.y * 4 };
for(std::size_t i = 0; i <= buffer.GetSize(); i += 4)
{
Vec3b data{ 0, 0, 0 };
data.x = (file.eof() ? 0x00 : file.get());
data.y = (file.eof() ? 0x00 : file.get());
data.z = (file.eof() ? 0x00 : file.get());
buffer.GetData()[i + 0] = data.z;
buffer.GetData()[i + 1] = data.y;
buffer.GetData()[i + 2] = data.x;
buffer.GetData()[i + 3] = 0xFF;
}
Message("BMP Loader : loaded %", path);
return buffer;
}
}

View File

@@ -0,0 +1,171 @@
#include <Graphics/Loaders/OBJ.h>
#include <Core/Logs.h>
#include <set>
#include <fstream>
#include <algorithm>
namespace Scop
{
std::optional<ObjData> LoadObjFromFile(const std::filesystem::path& path)
{
if(!std::filesystem::exists(path))
{
Error("OBJ loader : OBJ file does not exists; %", path);
return std::nullopt;
}
char line[1024];
std::string op;
std::istringstream line_in;
std::set<std::string> groups;
groups.insert("default");
std::ifstream in(path);
ObjData data;
while(in.good())
{
in.getline(line, 1023);
line_in.clear();
line_in.str(line);
if(!(line_in >> op))
continue;
if(op == "v")
{
Vec3f v;
line_in >> v.x;
line_in >> v.y;
line_in >> v.z;
data.vertex.push_back(std::move(v));
}
else if(op == "vt")
{
Vec2f v;
line_in >> v.x;
line_in >> v.y;
data.tex_coord.push_back(std::move(v));
}
else if(op == "vn")
{
Vec3f v;
line_in >> v.x;
line_in >> v.y;
line_in >> v.z;
data.normal.push_back(std::move(v));
}
else if(op == "vc")
{
Vec4f v;
line_in >> v.x;
line_in >> v.y;
line_in >> v.z;
line_in >> v.w;
data.color.push_back(std::move(v));
}
else if(op == "g")
{
groups.clear();
while(line_in >> groups);
groups.insert("default");
}
else if(op == "f")
{
std::vector<ObjData::FaceVertex> list;
while(line_in >> list);
for(const auto& group : groups)
{
ObjData::FaceList& fl = data.faces[group];
fl.second.push_back(fl.first.size());
fl.first.insert(fl.first.end(), list.begin(), list.end());
}
}
}
for(auto& [_, face] : data.faces)
{
ObjData::FaceList& fl = face;
fl.second.push_back(fl.first.size());
}
Message("OBJ Loader : loaded %", path);
return data;
}
static void TesselateObjData(std::vector<ObjData::FaceVertex>& input, std::vector<std::uint32_t>& input_start) noexcept
{
std::vector<ObjData::FaceVertex> output;
std::vector<std::uint32_t> output_start;
output.reserve(input.size());
output_start.reserve(input_start.size());
for(auto s = input_start.begin(); s != input_start.end() - 1; ++s)
{
const std::uint32_t size = *(s + 1) - *s;
if(size > 3)
{
const ObjData::FaceVertex & start_vertex = input[*s];
for(std::size_t i = 1; i < size - 1; i++)
{
output_start.push_back(output.size());
output.push_back(start_vertex);
output.push_back(input[*s + i]);
output.push_back(input[*s + i + 1]);
}
}
else
{
output_start.push_back(output.size());
output.insert(output.end(), input.begin() + *s, input.begin() + *(s + 1));
}
}
output_start.push_back(output.size());
input.swap(output);
input_start.swap(output_start);
}
void TesselateObjData(ObjData& obj)
{
for(auto& face : obj.faces)
{
ObjData::FaceList& fl = face.second;
TesselateObjData(fl.first, fl.second);
}
Message("OBJ Loader : object data tesselated");
}
ObjModel ConvertObjDataToObjModel(const ObjData& data)
{
std::vector<ObjData::FaceVertex> unique(data.faces.find("default")->second.first);
std::sort(unique.begin(), unique.end());
//unique.erase(std::unique(unique.begin(), unique.end()), unique.end());
ObjModel model;
for(auto& face : unique)
{
model.vertex.push_back(data.vertex[face.v]);
if(!data.tex_coord.empty())
{
const int index = (face.t > -1) ? face.t : face.v;
model.tex_coord.push_back(data.tex_coord[index]);
}
if(!data.normal.empty())
{
const int index = (face.n > -1) ? face.n : face.v;
model.normal.push_back(data.normal[index]);
}
if(!data.color.empty())
{
const int index = face.v;
model.color.push_back(data.color[index]);
}
}
for(auto& [group, faces] : data.faces)
{
std::vector<std::uint32_t>& v = model.faces[group];
v.reserve(faces.first.size());
for(auto& face : faces.first)
v.push_back(std::distance(unique.begin(), std::lower_bound(unique.begin(), unique.end(), face)));
}
return model;
}
}

31
ScopEngine/Runtime/Sources/Graphics/Mesh.cpp git.filemode.normal_file
View File

@@ -0,0 +1,31 @@
#include <Graphics/Mesh.h>
#include <Utils/Buffer.h>
#include <cstring>
namespace Scop
{
void Mesh::Draw(VkCommandBuffer cmd, std::size_t& drawcalls, std::size_t& polygondrawn) const noexcept
{
for(std::size_t i = 0; i < m_sub_meshes.size(); i++)
Draw(cmd, drawcalls, polygondrawn, i);
}
void Mesh::Draw(VkCommandBuffer cmd, std::size_t& drawcalls, std::size_t& polygondrawn, std::size_t submesh_index) const noexcept
{
Verify(submesh_index < m_sub_meshes.size(), "invalid submesh index");
m_sub_meshes[submesh_index].vbo.Bind(cmd);
m_sub_meshes[submesh_index].ibo.Bind(cmd);
RenderCore::Get().vkCmdDrawIndexed(cmd, static_cast<std::uint32_t>(m_sub_meshes[submesh_index].ibo.GetSize() / sizeof(std::uint32_t)), 1, 0, 0, 0);
polygondrawn += m_sub_meshes[submesh_index].triangle_count;
drawcalls++;
}
Mesh::~Mesh()
{
for(auto& mesh : m_sub_meshes)
{
mesh.vbo.Destroy();
mesh.ibo.Destroy();
}
}
}

View File

@@ -0,0 +1,565 @@
#include <Graphics/MeshFactory.h>
#include <Graphics/Mesh.h>
#include <Renderer/Vertex.h>
#include <Maths/Constants.h>
#include <Maths/Quaternions.h>
#include <cmath>
#include <vector>
namespace Scop
{
std::shared_ptr<Mesh> CreateQuad(float x, float y, float width, float height)
{
std::vector<Vertex> data(4);
data[0].position = Vec4f(x, y, 0.0f, 1.0f);
data[0].uv = Vec2f(1.0f, 1.0f);
data[1].position = Vec4f(x + width, y, 0.0f, 1.0f);
data[1].uv = Vec2f(0.0f, 1.0f);
data[2].position = Vec4f(x + width, y + height, 0.0f, 1.0f);
data[2].uv = Vec2f(0.0f, 0.0f);
data[3].position = Vec4f(x, y + height, 0.0f, 1.0f);
data[3].uv = Vec2f(1.0f, 0.0f);
std::vector<std::uint32_t> indices = {
0,
1,
2,
2,
3,
0,
};
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
mesh->AddSubMesh({ std::move(data), std::move(indices) });
return mesh;
}
std::shared_ptr<Mesh> CreateQuad(const Vec2f& position, const Vec2f& size)
{
return CreateQuad(position.x, position.y, size.x, size.y);
}
std::shared_ptr<Mesh> CreateQuad()
{
std::vector<Vertex> data(4);
data[0].position = Vec4f(-1.0f, -1.0f, 0.0f, 1.0f);
data[0].uv = Vec2f(0.0f, 0.0f);
data[0].color = Vec4f(1.0f);
data[1].position = Vec4f(1.0f, -1.0f, 0.0f, 1.0f);
data[1].color = Vec4f(1.0f);
data[1].uv = Vec2f(1.0f, 0.0f);
data[2].position = Vec4f(1.0f, 1.0f, 0.0f, 1.0f);
data[2].color = Vec4f(1.0f);
data[2].uv = Vec2f(1.0f, 1.0f);
data[3].position = Vec4f(-1.0f, 1.0f, 0.0f, 1.0f);
data[3].color = Vec4f(1.0f);
data[3].uv = Vec2f(0.0f, 1.0f);
std::vector<std::uint32_t> indices = {
0,
1,
2,
2,
3,
0,
};
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
mesh->AddSubMesh({ std::move(data), std::move(indices) });
return mesh;
}
std::shared_ptr<Mesh> CreateCube()
{
// v6----- v5
// /| /|
// v1------v0|
// | | | |
// | |v7---|-|v4
// |/ |/
// v2------v3
std::vector<Vertex> data(24);
data[0].position = Vec4f(1.0f, 1.0f, 1.0f, 1.0f);
data[0].color = Vec4f(1.0f);
data[0].normal = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
data[1].position = Vec4f(-1.0f, 1.0f, 1.0f, 1.0f);
data[1].color = Vec4f(1.0f);
data[1].normal = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
data[2].position = Vec4f(-1.0f, -1.0f, 1.0f, 1.0f);
data[2].color = Vec4f(1.0f);
data[2].normal = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
data[3].position = Vec4f(1.0f, -1.0f, 1.0f, 1.0f);
data[3].color = Vec4f(1.0f);
data[3].normal = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
data[4].position = Vec4f(1.0f, 1.0f, 1.0f, 1.0f);
data[4].color = Vec4f(1.0f);
data[4].normal = Vec4f(1.0f, 0.0f, 0.0f, 1.0f);
data[5].position = Vec4f(1.0f, -1.0f, 1.0f, 1.0f);
data[5].color = Vec4f(1.0f);
data[5].normal = Vec4f(1.0f, 0.0f, 0.0f, 1.0f);
data[6].position = Vec4f(1.0f, -1.0f, -1.0f, 1.0f);
data[6].color = Vec4f(1.0f);
data[6].normal = Vec4f(1.0f, 0.0f, 0.0f, 1.0f);
data[7].position = Vec4f(1.0f, 1.0f, -1.0f, 1.0f);
data[7].color = Vec4f(1.0f);
data[7].normal = Vec4f(1.0f, 0.0f, 0.0f, 1.0f);
data[8].position = Vec4f(1.0f, 1.0f, 1.0f, 1.0f);
data[8].color = Vec4f(1.0f);
data[8].normal = Vec4f(0.0f, 1.0f, 0.0f, 1.0f);
data[9].position = Vec4f(1.0f, 1.0f, -1.0f, 1.0f);
data[9].color = Vec4f(1.0f);
data[9].normal = Vec4f(0.0f, 1.0f, 0.0f, 1.0f);
data[10].position = Vec4f(-1.0f, 1.0f, -1.0f, 1.0f);
data[10].color = Vec4f(1.0f);
data[10].normal = Vec4f(0.0f, 1.0f, 0.0f, 1.0f);
data[11].position = Vec4f(-1.0f, 1.0f, 1.0f, 1.0f);
data[11].color = Vec4f(1.0f);
data[11].normal = Vec4f(0.0f, 1.0f, 0.0f, 1.0f);
data[12].position = Vec4f(-1.0f, 1.0f, 1.0f, 1.0f);
data[12].color = Vec4f(1.0f);
data[12].normal = Vec4f(-1.0f, 0.0f, 0.0f, 1.0f);
data[13].position = Vec4f(-1.0f, 1.0f, -1.0f, 1.0f);
data[13].color = Vec4f(1.0f);
data[13].normal = Vec4f(-1.0f, 0.0f, 0.0f, 1.0f);
data[14].position = Vec4f(-1.0f, -1.0f, -1.0f, 1.0f);
data[14].color = Vec4f(1.0f);
data[14].normal = Vec4f(-1.0f, 0.0f, 0.0f, 1.0f);
data[15].position = Vec4f(-1.0f, -1.0f, 1.0f, 1.0f);
data[15].color = Vec4f(1.0f);
data[15].normal = Vec4f(-1.0f, 0.0f, 0.0f, 1.0f);
data[16].position = Vec4f(-1.0f, -1.0f, -1.0f, 1.0f);
data[16].color = Vec4f(1.0f);
data[16].normal = Vec4f(0.0f, -1.0f, 0.0f, 1.0f);
data[17].position = Vec4f(1.0f, -1.0f, -1.0f, 1.0f);
data[17].color = Vec4f(1.0f);
data[17].normal = Vec4f(0.0f, -1.0f, 0.0f, 1.0f);
data[18].position = Vec4f(1.0f, -1.0f, 1.0f, 1.0f);
data[18].color = Vec4f(1.0f);
data[18].normal = Vec4f(0.0f, -1.0f, 0.0f, 1.0f);
data[19].position = Vec4f(-1.0f, -1.0f, 1.0f, 1.0f);
data[19].color = Vec4f(1.0f);
data[19].normal = Vec4f(0.0f, -1.0f, 0.0f, 1.0f);
data[20].position = Vec4f(1.0f, -1.0f, -1.0f, 1.0f);
data[20].color = Vec4f(1.0f);
data[20].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
data[21].position = Vec4f(-1.0f, -1.0f, -1.0f, 1.0f);
data[21].color = Vec4f(1.0f);
data[21].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
data[22].position = Vec4f(-1.0f, 1.0f, -1.0f, 1.0f);
data[22].color = Vec4f(1.0f);
data[22].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
data[23].position = Vec4f(1.0f, 1.0f, -1.0f, 1.0f);
data[23].color = Vec4f(1.0f);
data[23].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
for(int i = 0; i < 6; i++)
{
data[i * 4 + 0].uv = Vec2f(0.0f, 0.0f);
data[i * 4 + 1].uv = Vec2f(1.0f, 0.0f);
data[i * 4 + 2].uv = Vec2f(1.0f, 1.0f);
data[i * 4 + 3].uv = Vec2f(0.0f, 1.0f);
}
std::vector<std::uint32_t> indices = {
0, 1, 2, 0, 2, 3, // Right
4, 5, 6, 4, 6, 7, // Front
8, 9, 10, 8, 10, 11, // Top
12, 13, 14, 12, 14, 15, // Back
16, 17, 18, 16, 18, 19, // Bottom
20, 21, 22, 20, 22, 23 // Left
};
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
mesh->AddSubMesh({ std::move(data), std::move(indices) });
return mesh;
}
std::shared_ptr<Mesh> CreatePyramid()
{
std::vector<Vertex> data(18);
data[0].position = Vec4f(1.0f, 1.0f, -1.0f, 1.0f);
data[0].color = Vec4f(1.0f);
data[0].uv = Vec2f(0.24f, 0.20f);
data[0].normal = Vec4f(0.0f, 0.8948f, 0.4464f, 1.0f);
data[1].position = Vec4f(-1.0f, 1.0f, -1.0f, 1.0f);
data[1].color = Vec4f(1.0f);
data[1].uv = Vec2f(0.24f, 0.81f);
data[1].normal = Vec4f(0.0f, 0.8948f, 0.4464f, 1.0f);
data[2].position = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
data[2].color = Vec4f(1.0f);
data[2].uv = Vec2f(0.95f, 0.50f);
data[2].normal = Vec4f(0.0f, 0.8948f, 0.4464f, 1.0f);
data[3].position = Vec4f(-1.0f, 1.0f, -1.0f, 1.0f);
data[3].color = Vec4f(1.0f);
data[3].uv = Vec2f(0.24f, 0.21f);
data[3].normal = Vec4f(-0.8948f, 0.0f, 0.4464f, 1.0f);
data[4].position = Vec4f(-1.0f, -1.0f, -1.0f, 1.0f);
data[4].color = Vec4f(1.0f);
data[4].uv = Vec2f(0.24f, 0.81f);
data[4].normal = Vec4f(-0.8948f, 0.0f, 0.4464f, 1.0f);
data[5].position = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
data[5].color = Vec4f(1.0f);
data[5].uv = Vec2f(0.95f, 0.50f);
data[5].normal = Vec4f(-0.8948f, 0.0f, 0.4464f, 1.0f);
data[6].position = Vec4f(1.0f, 1.0f, -1.0f, 1.0f);
data[6].color = Vec4f(1.0f);
data[6].uv = Vec2f(0.24f, 0.81f);
data[6].normal = Vec4f(0.8948f, 0.0f, 0.4475f, 1.0f);
data[7].position = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
data[7].color = Vec4f(1.0f);
data[7].uv = Vec2f(0.95f, 0.50f);
data[7].normal = Vec4f(0.8948f, 0.0f, 0.4475f, 1.0f);
data[8].position = Vec4f(1.0f, -1.0f, -1.0f, 1.0f);
data[8].color = Vec4f(1.0f);
data[8].uv = Vec2f(0.24f, 0.21f);
data[8].normal = Vec4f(0.8948f, 0.0f, 0.4475f, 1.0f);
data[9].position = Vec4f(-1.0f, -1.0f, -1.0f, 1.0f);
data[9].color = Vec4f(1.0f);
data[9].uv = Vec2f(0.24f, 0.21f);
data[9].normal = Vec4f(0.0f, -0.8948f, 0.448f, 1.0f);
data[10].position = Vec4f(1.0f, -1.0f, -1.0f, 1.0f);
data[10].color = Vec4f(1.0f);
data[10].uv = Vec2f(0.24f, 0.81f);
data[10].normal = Vec4f(0.0f, -0.8948f, 0.448f, 1.0f);
data[11].position = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
data[11].color = Vec4f(1.0f);
data[11].uv = Vec2f(0.95f, 0.50f);
data[11].normal = Vec4f(0.0f, -0.8948f, 0.448f, 1.0f);
data[12].position = Vec4f(-1.0f, 1.0f, -1.0f, 1.0f);
data[12].color = Vec4f(1.0f);
data[12].uv = Vec2f(0.0f, 0.0f);
data[12].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
data[13].position = Vec4f(1.0f, 1.0f, -1.0f, 1.0f);
data[13].color = Vec4f(1.0f);
data[13].uv = Vec2f(0.0f, 1.0f);
data[13].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
data[14].position = Vec4f(1.0f, -1.0f, -1.0f, 1.0f);
data[14].color = Vec4f(1.0f);
data[14].uv = Vec2f(1.0f, 1.0f);
data[14].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
data[15].position = Vec4f(-1.0f, -1.0f, -1.0f, 1.0f);
data[15].color = Vec4f(1.0f);
data[15].uv = Vec2f(0.96f, 0.0f);
data[15].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
data[16].position = Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
data[16].color = Vec4f(1.0f);
data[16].uv = Vec2f(0.0f, 0.0f);
data[16].normal = Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
data[17].position = Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
data[17].color = Vec4f(1.0f);
data[17].uv = Vec2f(0.0f, 0.0f);
data[17].normal = Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
std::vector<std::uint32_t> indices = {
0, 1, 2,
3, 4, 5,
6, 7, 8,
9, 10, 11,
12, 13, 14,
15, 12, 14
};
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
mesh->AddSubMesh({ std::move(data), std::move(indices) });
return mesh;
}
std::shared_ptr<Mesh> CreateSphere(std::uint32_t x_segments, std::uint32_t y_segments)
{
std::vector<Vertex> data;
float sector_count = static_cast<float>(x_segments);
float stack_count = static_cast<float>(y_segments);
float sector_step = 2 * Pi<float>() / sector_count;
float stack_step = Pi<float>() / stack_count;
float radius = 1.0f;
for(int i = 0; i <= stack_count; ++i)
{
float stack_angle = Pi<float>() / 2 - i * stack_step;
float xy = radius * std::cos(stack_angle);
float z = radius * std::sin(stack_angle);
for(int j = 0; j <= sector_count; ++j)
{
float sector_angle = j * sector_step;
float x = xy * cosf(sector_angle);
float y = xy * sinf(sector_angle);
float s = static_cast<float>(j / sector_count);
float t = static_cast<float>(i / stack_count);
Vertex vertex;
vertex.position = Vec4f(x, y, z, 1.0f);
vertex.uv = Vec2f(s, t);
vertex.normal = vertex.position.Normalize();
data.emplace_back(vertex);
}
}
std::vector<std::uint32_t> indices;
std::uint32_t k1, k2;
for(uint32_t i = 0; i < stack_count; ++i)
{
k1 = i * (static_cast<std::uint32_t>(sector_count) + 1U);
k2 = k1 + static_cast<std::uint32_t>(sector_count) + 1U;
for(std::uint32_t j = 0; j < sector_count; ++j, ++k1, ++k2)
{
if(i != 0)
{
indices.push_back(k1);
indices.push_back(k2);
indices.push_back(k1 + 1);
}
if(i != (stack_count - 1))
{
indices.push_back(k1 + 1);
indices.push_back(k2);
indices.push_back(k2 + 1);
}
}
}
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
mesh->AddSubMesh({ std::move(data), std::move(indices) });
return mesh;
}
std::shared_ptr<Mesh> CreatePlane(float width, float height, const Vec3f& normal)
{
Vec3 vec = normal * 90.0f;
std::vector<Vertex> data(4);
data[0].position = Vec4f(-width * 0.5f, -1.0f, -height * 0.5f);
data[0].normal = normal;
data[0].uv = Vec2f(0.0f, 0.0f);
data[1].position = Vec4f(-width * 0.5f, -1.0f, height * 0.5f);
data[1].normal = normal;
data[1].uv = Vec2f(0.0f, 1.0f);
data[2].position = Vec4f(width * 0.5f, 1.0f, height * 0.5f);
data[2].normal = normal;
data[2].uv = Vec2f(1.0f, 1.0f);
data[3].position = Vec4f(width * 0.5f, 1.0f, -height * 0.5f);
data[3].normal = normal;
data[3].uv = Vec2f(1.0f, 0.0f);
std::vector<std::uint32_t> indices = { 0, 1, 2, 2, 3, 0 };
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
mesh->AddSubMesh({ std::move(data), std::move(indices) });
return mesh;
}
std::shared_ptr<Mesh> CreateCapsule(float radius, float mid_height, int radial_segments, int rings)
{
int i, j, prevrow, thisrow, point;
float x, y, z, u, v, w;
float onethird = 1.0f / 3.0f;
float twothirds = 2.0f / 3.0f;
std::vector<Vertex> data;
std::vector<std::uint32_t> indices;
point = 0;
/* top hemisphere */
thisrow = 0;
prevrow = 0;
for(j = 0; j <= (rings + 1); j++)
{
v = float(j);
v /= (rings + 1);
w = std::sin(0.5f * Pi<float>() * v);
y = radius * std::cos(0.5f * Pi<float>() * v);
for(i = 0; i <= radial_segments; i++)
{
u = float(i);
u /= radial_segments;
x = std::sin(u * (Pi<float>() * 2.0f));
z = std::cos(u * (Pi<float>() * 2.0f));
Vec3 p = Vec3f(x * radius * w, y, z * radius * w);
Vertex vertex;
vertex.position = Vec4f(p + Vec3f(0.0f, 0.5f * mid_height, 0.0f), 1.0f);
vertex.normal = p.Normalize();
vertex.uv = Vec2f(u, onethird * v);
data.emplace_back(vertex);
point++;
if(i > 0 && j > 0)
{
indices.push_back(thisrow + i - 1);
indices.push_back(prevrow + i);
indices.push_back(prevrow + i - 1);
indices.push_back(thisrow + i - 1);
indices.push_back(thisrow + i);
indices.push_back(prevrow + i);
};
};
prevrow = thisrow;
thisrow = point;
};
/* cylinder */
thisrow = point;
prevrow = 0;
for(j = 0; j <= (rings + 1); j++)
{
v = float(j);
v /= (rings + 1);
y = mid_height * v;
y = (mid_height * 0.5f) - y;
for(i = 0; i <= radial_segments; i++)
{
u = float(i);
u /= radial_segments;
x = std::sin(u * (Pi<float>() * 2.0f));
z = std::cos(u * (Pi<float>() * 2.0f));
Vec3 p = Vec3f(x * radius, y, z * radius);
Vertex vertex;
vertex.position = p;
// vertex.normal = Vec4f(x, z, 0.0f);
vertex.normal = Vec4f(x, 0.0f, z);
// vertex.uv = Vec2f(u, onethird + (v * onethird));
vertex.uv = Vec2f(u, v * 0.5f);
data.emplace_back(vertex);
point++;
if(i > 0 && j > 0)
{
indices.push_back(thisrow + i - 1);
indices.push_back(prevrow + i);
indices.push_back(prevrow + i - 1);
indices.push_back(thisrow + i - 1);
indices.push_back(thisrow + i);
indices.push_back(prevrow + i);
};
};
prevrow = thisrow;
thisrow = point;
};
/* bottom hemisphere */
thisrow = point;
prevrow = 0;
for(j = 0; j <= (rings + 1); j++)
{
v = float(j);
v /= (rings + 1);
v += 1.0f;
w = std::sin(0.5f * Pi<float>() * v);
y = radius * std::cos(0.5f * Pi<float>() * v);
for(i = 0; i <= radial_segments; i++)
{
float u2 = float(i);
u2 /= radial_segments;
x = std::sin(u2 * (Pi<float>() * 2.0f));
z = std::cos(u2 * (Pi<float>() * 2.0f));
Vec3 p = Vec3f(x * radius * w, y, z * radius * w);
Vertex vertex;
vertex.position = p + Vec3f(0.0f, -0.5f * mid_height, 0.0f);
vertex.normal = p.Normalize();
vertex.uv = Vec2f(u2, twothirds + ((v - 1.0f) * onethird));
data.emplace_back(vertex);
point++;
if(i > 0 && j > 0)
{
indices.push_back(thisrow + i - 1);
indices.push_back(prevrow + i);
indices.push_back(prevrow + i - 1);
indices.push_back(thisrow + i - 1);
indices.push_back(thisrow + i);
indices.push_back(prevrow + i);
};
};
prevrow = thisrow;
thisrow = point;
}
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
mesh->AddSubMesh({ std::move(data), std::move(indices) });
return mesh;
}
}

154
ScopEngine/Runtime/Sources/Graphics/Model.cpp git.filemode.normal_file
View File

@@ -0,0 +1,154 @@
#include <Graphics/Model.h>
#include <Graphics/Loaders/OBJ.h>
#include <Renderer/Pipelines/Graphics.h>
#include <Maths/Angles.h>
#include <unordered_map>
namespace Scop
{
Model::Model(std::shared_ptr<Mesh> mesh) : p_mesh(mesh)
{
if(p_mesh)
m_materials.resize(p_mesh->GetSubMeshCount() + 1);
CPUBuffer default_pixels{ kvfFormatSize(VK_FORMAT_R8G8B8A8_SRGB) };
default_pixels.GetDataAs<std::uint32_t>()[0] = 0xFFFFFFFF;
std::shared_ptr<Texture> default_texture = std::make_shared<Texture>(std::move(default_pixels), 1, 1, VK_FORMAT_R8G8B8A8_SRGB);
MaterialTextures textures;
textures.albedo = default_texture;
m_materials.back() = std::make_shared<Material>(textures);
}
void Model::Draw(VkCommandBuffer cmd, const DescriptorSet& matrices_set, const GraphicPipeline& pipeline, DescriptorSet& set, std::size_t& drawcalls, std::size_t& polygondrawn, std::size_t frame_index) const
{
if(!p_mesh)
return;
for(std::size_t i = 0; i < GetSubMeshCount(); i++)
{
std::shared_ptr<Material> material;
if(!m_materials[i])
material = m_materials.back();
else
material = m_materials[i];
if(!material->IsSetInit())
material->UpdateDescriptorSet(set);
material->Bind(frame_index, cmd);
std::array<VkDescriptorSet, 2> sets = { matrices_set.GetSet(frame_index), material->GetSet(frame_index) };
RenderCore::Get().vkCmdBindDescriptorSets(cmd, pipeline.GetPipelineBindPoint(), pipeline.GetPipelineLayout(), 0, sets.size(), sets.data(), 0, nullptr);
p_mesh->Draw(cmd, drawcalls, polygondrawn, i);
}
}
RadianAnglef GetAngleBetweenVectors(const Vec3f& a, const Vec3f& b) noexcept
{
float cosine_theta = (a.DotProduct(b)) / (a.GetLength() * b.GetLength());
RadianAnglef angle(std::acos(cosine_theta));
return angle;
}
Model LoadModelFromObjFile(std::filesystem::path path) noexcept
{
auto obj_data = LoadObjFromFile(path);
if(!obj_data)
return { nullptr };
TesselateObjData(*obj_data);
ObjModel obj_model = ConvertObjDataToObjModel(*obj_data);
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
float min_x = std::numeric_limits<float>::max(), max_x = std::numeric_limits<float>::lowest();
float min_y = std::numeric_limits<float>::max(), max_y = std::numeric_limits<float>::lowest();
float min_z = std::numeric_limits<float>::max(), max_z = std::numeric_limits<float>::lowest();
bool needs_to_generate_normals = obj_model.normal.empty();
std::unordered_map<std::string, std::vector<Vec3f>> generated_normals;
if(needs_to_generate_normals)
{
for(auto& [group, faces] : obj_model.faces)
{
generated_normals[group] = std::vector<Vec3f>(faces.size(), Vec3f{});
for(std::size_t i = 0; i < faces.size(); i += 3)
{
Vec3f vec_a{ obj_model.vertex[faces[i + 1]] - obj_model.vertex[faces[i]] };
Vec3f vec_b{ obj_model.vertex[faces[i + 2]] - obj_model.vertex[faces[i]] };
Vec3f normal = vec_a.CrossProduct(vec_b).Normalize();
generated_normals[group][i + 0] = normal;
generated_normals[group][i + 1] = normal;
generated_normals[group][i + 2] = normal;
}
}
}
for(auto& [group, faces] : obj_model.faces)
{
std::vector<Vertex> vertices;
std::vector<std::uint32_t> indices;
for(std::size_t i = 0; i < faces.size(); i++)
{
min_x = std::min(obj_model.vertex[faces[i]].x, min_x);
min_y = std::min(obj_model.vertex[faces[i]].y, min_y);
min_z = std::min(obj_model.vertex[faces[i]].z, min_z);
max_x = std::max(obj_model.vertex[faces[i]].x, max_x);
max_y = std::max(obj_model.vertex[faces[i]].y, max_y);
max_z = std::max(obj_model.vertex[faces[i]].z, max_z);
Vec4f color{};
switch(i % 10)
{
case 0: color = Vec4f{ 1.0f, 0.0f, 1.0f, 1.0f }; break;
case 1: color = Vec4f{ 1.0f, 1.0f, 0.0f, 1.0f }; break;
case 2: color = Vec4f{ 1.0f, 0.5f, 0.0f, 1.0f }; break;
case 3: color = Vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }; break;
case 4: color = Vec4f{ 0.2f, 0.0f, 0.8f, 1.0f }; break;
case 5: color = Vec4f{ 0.0f, 1.0f, 1.0f, 1.0f }; break;
case 6: color = Vec4f{ 0.0f, 1.0f, 0.0f, 1.0f }; break;
case 7: color = Vec4f{ 0.0f, 0.0f, 1.0f, 1.0f }; break;
case 8: color = Vec4f{ 0.3f, 0.0f, 0.4f, 1.0f }; break;
default: color = Vec4f{ 1.0f, 1.0f, 1.0f, 1.0f }; break;
}
Vec3f normal{};
if(needs_to_generate_normals)
{
normal = generated_normals[group][i];
for(std::size_t j = 0; j < faces.size(); j++)
{
if(faces[j] == faces[i] && i != j)
{
RadianAnglef angle = GetAngleBetweenVectors(generated_normals[group][i], generated_normals[group][j]);
if(angle.ToDegrees() < 89.0f)
normal += generated_normals[group][j];
}
}
}
else
normal = obj_model.normal[faces[i]];
Vertex v(
Vec4f{
obj_model.vertex[faces[i]],
1.0f
},
color,
Vec4f{
normal.Normalize(),
1.0f
},
(obj_model.tex_coord.empty() ?
Vec2f{ (obj_model.vertex[faces[i]].x - min_x) / (max_x - min_x), 1.0f - ((obj_model.vertex[faces[i]].y - min_y) / (max_y - min_y)) }
:
obj_model.tex_coord[faces[i]]
)
);
indices.push_back(vertices.size());
vertices.push_back(std::move(v));
}
mesh->AddSubMesh({ vertices, indices });
}
Model model(mesh);
model.m_center = Vec3f{ (min_x + max_x) / 2.0f, (min_y + max_y) / 2.0f, (min_z + max_z) / 2.0f };
return model;
}
}

121
ScopEngine/Runtime/Sources/Graphics/Scene.cpp git.filemode.normal_file
View File

@@ -0,0 +1,121 @@
#include <Graphics/Scene.h>
#include <Renderer/Renderer.h>
#include <Renderer/RenderCore.h>
#include <Platform/Inputs.h>
#include <Core/Logs.h>
#include <Renderer/ViewerData.h>
#include <Core/EventBus.h>
#include <Core/Engine.h>
#include <cstring>
namespace Scop
{
Scene::Scene(std::string_view name, SceneDescriptor desc)
: m_name(name), m_descriptor(std::move(desc)), p_parent(nullptr)
{
}
Scene::Scene(std::string_view name, SceneDescriptor desc, NonOwningPtr<Scene> parent)
: m_name(name), m_descriptor(std::move(desc)), p_parent(parent)
{
}
Actor& Scene::CreateActor(Model model) noexcept
{
std::shared_ptr<Actor> actor = std::make_shared<Actor>(std::move(model));
m_actors.push_back(actor);
return *actor;
}
Actor& Scene::CreateActor(std::string_view name, Model model)
{
std::shared_ptr<Actor> actor = std::make_shared<Actor>(std::move(model));
m_actors.push_back(actor);
return *actor;
}
Sprite& Scene::CreateSprite(std::shared_ptr<Texture> texture) noexcept
{
std::shared_ptr<Sprite> sprite = std::make_shared<Sprite>(texture);
m_sprites.push_back(sprite);
return *sprite;
}
Sprite& Scene::CreateSprite(std::string_view name, std::shared_ptr<Texture> texture)
{
std::shared_ptr<Sprite> sprite = std::make_shared<Sprite>(texture);
m_sprites.push_back(sprite);
return *sprite;
}
void Scene::SwitchToChild(std::string_view name) const noexcept
{
auto it = std::find_if(m_scene_children.begin(), m_scene_children.end(), [name](const Scene& scene){ return name == scene.GetName(); });
if(it == m_scene_children.end())
{
Error("Cannot switch to scene '%', scene not found in children of '%'", name, m_name);
return;
}
ScopEngine::Get().SwitchToScene(const_cast<Scene*>(&(*it)));
}
void Scene::SwitchToParent() const noexcept
{
ScopEngine::Get().SwitchToScene(p_parent);
}
void Scene::Init(NonOwningPtr<Renderer> renderer)
{
std::function<void(const EventBase&)> functor = [this, renderer](const EventBase& event)
{
if(event.What() == Event::ResizeEventCode)
{
m_depth.Destroy();
m_depth.Init(renderer->GetSwapchain().GetSwapchainImages().back().GetWidth(), renderer->GetSwapchain().GetSwapchainImages().back().GetHeight());
}
if(event.What() == Event::ResizeEventCode || event.What() == Event::SceneHasChangedEventCode)
m_pipeline.Destroy(); // Ugly but f*ck off
};
EventBus::RegisterListener({ functor, m_name + std::to_string(reinterpret_cast<std::uintptr_t>(this)) });
auto vertex_shader = RenderCore::Get().GetDefaultVertexShader();
m_depth.Init(renderer->GetSwapchain().GetSwapchainImages().back().GetWidth(), renderer->GetSwapchain().GetSwapchainImages().back().GetHeight());
m_forward.matrices_buffer = std::make_shared<UniformBuffer>();
m_forward.matrices_buffer->Init(sizeof(ViewerData));
m_forward.matrices_set = std::make_shared<DescriptorSet>(vertex_shader->GetShaderLayout().set_layouts[0].second, vertex_shader->GetPipelineLayout().set_layouts[0], ShaderType::Vertex);
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
m_forward.matrices_set->SetUniformBuffer(i, 0, m_forward.matrices_buffer->Get(i));
m_forward.matrices_set->Update(i);
}
m_forward.albedo_set = std::make_shared<DescriptorSet>(m_descriptor.fragment_shader->GetShaderLayout().set_layouts[0].second, m_descriptor.fragment_shader->GetPipelineLayout().set_layouts[0], ShaderType::Fragment);
for(auto& child : m_scene_children)
child.Init(renderer);
}
void Scene::Update(Inputs& input, float timestep, float aspect)
{
for(auto actor : m_actors)
actor->Update(this, input, timestep);
for(auto sprite : m_sprites)
sprite->Update(this, input, timestep);
if(m_descriptor.camera)
m_descriptor.camera->Update(input, aspect, timestep);
}
void Scene::Destroy()
{
p_skybox.reset();
m_depth.Destroy();
m_actors.clear();
m_sprites.clear();
m_pipeline.Destroy();
m_descriptor.fragment_shader.reset();
m_forward.matrices_buffer->Destroy();
for(auto& child : m_scene_children)
child.Destroy();
}
}

View File

@@ -0,0 +1,29 @@
#include <Graphics/Sprite.h>
#include <Core/Script.h>
#include <Renderer/Image.h>
#include <Graphics/MeshFactory.h>
#include <Core/Logs.h>
namespace Scop
{
Sprite::Sprite(std::shared_ptr<Texture> texture)
{
Verify((bool)texture, "Sprite: invalid texture");
p_mesh = CreateQuad(0, 0, texture->GetWidth(), texture->GetHeight());
p_texture = texture;
if(p_script)
p_script->OnInit(this);
}
void Sprite::Update(NonOwningPtr<Scene> scene, Inputs& input, float delta)
{
if(p_script)
p_script->OnUpdate(scene, this, input, delta);
}
Sprite::~Sprite()
{
if(p_script)
p_script->OnQuit(this);
}
}

View File

@@ -0,0 +1,59 @@
#include <Platform/Inputs.h>
#include <Core/EventBus.h>
namespace Scop
{
namespace Internal
{
struct ResizeEventSwapchain : public EventBase
{
Event What() const override { return Event::ResizeEventCode; }
};
}
Inputs::Inputs() : m_keys(SDL_GetKeyboardState(&m_keys_count))
{}
void Inputs::Update()
{
SDL_GetRelativeMouseState(&m_x_rel, &m_y_rel);
std::uint8_t mouse_state = SDL_GetMouseState(&m_x, &m_y);
m_mouse[0] = SDL_BUTTON(mouse_state) & SDL_BUTTON_LMASK;
m_mouse[1] = SDL_BUTTON(mouse_state) & SDL_BUTTON_MMASK;
m_mouse[2] = SDL_BUTTON(mouse_state) & SDL_BUTTON_RMASK;
m_mouse[3] = SDL_BUTTON(mouse_state) & SDL_BUTTON_X1MASK;
m_mouse[4] = SDL_BUTTON(mouse_state) & SDL_BUTTON_X2MASK;
m_is_mouse_wheel_up = false;
m_is_mouse_wheel_down = false;
while(SDL_PollEvent(&m_event))
{
for(auto& hook : m_hooks)
hook(&m_event);
switch(m_event.type)
{
case SDL_MOUSEWHEEL:
{
if(m_event.wheel.y > 0) // scroll up
m_is_mouse_wheel_up = true;
else if(m_event.wheel.y < 0) // scroll down
m_is_mouse_wheel_down = true;
break;
}
case SDL_WINDOWEVENT:
{
switch(m_event.window.event)
{
case SDL_WINDOWEVENT_CLOSE: m_has_recieved_close_event = true; break;
case SDL_WINDOWEVENT_SIZE_CHANGED: EventBus::Send("__ScopSwapchain", Internal::ResizeEventSwapchain{}); break;
default: break;
}
break;
}
default: break;
}
}
}
}

View File

@@ -0,0 +1,73 @@
#include <Platform/Window.h>
#include <SDL2/SDL_vulkan.h>
#include <Core/Logs.h>
namespace Scop
{
Window::Window(const std::string& title, std::uint32_t width, std::uint32_t height, bool hidden) : m_title(title), m_height(height), m_width(width)
{
std::uint32_t flags = SDL_WINDOW_VULKAN;
if(hidden)
flags |= SDL_WINDOW_HIDDEN;
else
flags |= SDL_WINDOW_SHOWN;
if(width == 0 && height == 0)
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
else
flags |= SDL_WINDOW_RESIZABLE;
p_window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, flags);
if(!p_window)
FatalError("Unable to open a new window, %", SDL_GetError());
}
void Window::FetchWindowInfos() noexcept
{
Vec2i size;
SDL_GetWindowSize(p_window, &size.x, &size.y);
m_width = size.x;
m_height = size.y;
}
VkSurfaceKHR Window::CreateVulkanSurface(VkInstance instance) const noexcept
{
VkSurfaceKHR surface;
if(!SDL_Vulkan_CreateSurface(p_window, instance, &surface))
FatalError("SDL: could not create a Vulkan surface; %", SDL_GetError());
return surface;
}
std::vector<const char*> Window::GetRequiredVulkanInstanceExtentions() const noexcept
{
std::uint32_t count;
if(!SDL_Vulkan_GetInstanceExtensions(p_window, &count, nullptr))
FatalError("SDL Manager: could not retrieve Vulkan instance extensions");
std::vector<const char*> extensions(count);
if(!SDL_Vulkan_GetInstanceExtensions(p_window, &count, extensions.data()))
FatalError("SDL Manager: could not retrieve Vulkan instance extensions");
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
return extensions;
}
Vec2ui Window::GetVulkanDrawableSize() const noexcept
{
Vec2i extent;
SDL_Vulkan_GetDrawableSize(p_window, &extent.x, &extent.y);
return Vec2ui{ extent };
}
void Window::Destroy() noexcept
{
if(p_window != nullptr)
{
SDL_DestroyWindow(p_window);
p_window = nullptr;
}
}
Window::~Window()
{
Destroy();
}
}

184
ScopEngine/Runtime/Sources/Renderer/Buffer.cpp git.filemode.normal_file
View File

@@ -0,0 +1,184 @@
#include <Renderer/RenderCore.h>
#include <Core/Logs.h>
#include <Renderer/Buffer.h>
namespace Scop
{
void GPUBuffer::Init(BufferType type, VkDeviceSize size, VkBufferUsageFlags usage, CPUBuffer data)
{
if(type == BufferType::Constant)
{
if(data.Empty())
{
Warning("Vulkan: trying to create constant buffer without data (constant buffers cannot be modified after creation)");
return;
}
m_usage = usage | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
m_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
else if(type == BufferType::HighDynamic)
{
m_usage = usage;
m_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
else // LowDynamic or Staging
{
m_usage = usage | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
m_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
if(type == BufferType::Staging && data.Empty())
Warning("Vulkan: trying to create staging buffer without data (wtf?)");
CreateBuffer(size, m_usage, m_flags, false);
if(!data.Empty())
{
if(m_memory.map != nullptr)
std::memcpy(m_memory.map, data.GetData(), data.GetSize());
}
if(type == BufferType::Constant || type == BufferType::LowDynamic)
PushToGPU();
}
void GPUBuffer::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, bool use_raw_size)
{
auto device = RenderCore::Get().GetDevice();
m_buffer = kvfCreateBuffer(device, usage, size);
VkMemoryRequirements mem_requirements;
RenderCore::Get().vkGetBufferMemoryRequirements(device, m_buffer, &mem_requirements);
if(use_raw_size)
{
m_memory = RenderCore::Get().GetAllocator().Allocate(size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties));
Message("test % - %", size, m_memory.size);
}
else
m_memory = RenderCore::Get().GetAllocator().Allocate(mem_requirements.size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties));
RenderCore::Get().vkBindBufferMemory(device, m_buffer, m_memory.memory, m_memory.offset);
Message("Vulkan: created buffer");
s_buffer_count++;
}
bool GPUBuffer::CopyFrom(const GPUBuffer& buffer) noexcept
{
if(!(m_usage & VK_BUFFER_USAGE_TRANSFER_DST_BIT))
{
Error("Vulkan: buffer cannot be the destination of a copy because it does not have the correct usage flag");
return false;
}
if(!(buffer.m_usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT))
{
Error("Vulkan: buffer cannot be the source of a copy because it does not have the correct usage flag");
return false;
}
VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
kvfCopyBufferToBuffer(cmd, m_buffer, buffer.Get(), buffer.GetSize());
kvfEndCommandBuffer(cmd);
VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice());
kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence);
kvfWaitForFence(RenderCore::Get().GetDevice(), fence);
kvfDestroyFence(RenderCore::Get().GetDevice(), fence);
return true;
}
void GPUBuffer::PushToGPU() noexcept
{
GPUBuffer new_buffer;
new_buffer.m_usage = (this->m_usage & 0xFFFFFFFC) | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
new_buffer.m_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
new_buffer.CreateBuffer(m_memory.size, new_buffer.m_usage, new_buffer.m_flags, true);
if(new_buffer.CopyFrom(*this))
Swap(new_buffer);
new_buffer.Destroy();
Message("Vulkan: pushed buffer to GPU memory");
}
void GPUBuffer::Destroy() noexcept
{
if(m_buffer == VK_NULL_HANDLE)
return;
RenderCore::Get().vkDestroyBuffer(RenderCore::Get().GetDevice(), m_buffer, nullptr);
RenderCore::Get().GetAllocator().Deallocate(m_memory);
m_buffer = VK_NULL_HANDLE;
m_memory = NULL_MEMORY_BLOCK;
Message("Vulkan: destroyed buffer");
s_buffer_count--;
}
void GPUBuffer::Swap(GPUBuffer& buffer) noexcept
{
std::swap(m_buffer, buffer.m_buffer);
m_memory.Swap(buffer.m_memory);
std::swap(m_usage, buffer.m_usage);
std::swap(m_flags, buffer.m_flags);
}
void VertexBuffer::SetData(CPUBuffer data)
{
if(data.GetSize() > m_memory.size)
{
Error("Vulkan: trying to store to much data in a vertex buffer (% bytes in % bytes)", data.GetSize(), m_memory.size);
return;
}
if(data.Empty())
{
Warning("Vulkan: cannot set empty data in a vertex buffer");
return;
}
GPUBuffer staging;
staging.Init(BufferType::Staging, data.GetSize(), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, data);
CopyFrom(staging);
staging.Destroy();
}
void IndexBuffer::SetData(CPUBuffer data)
{
if(data.GetSize() > m_memory.size)
{
Error("Vulkan: trying to store to much data in an index buffer (% bytes in % bytes)", data.GetSize(), m_memory.size);
return;
}
if(data.Empty())
{
Warning("Vulkan: cannot set empty data in an index buffer");
return;
}
GPUBuffer staging;
staging.Init(BufferType::Staging, data.GetSize(), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, data);
CopyFrom(staging);
staging.Destroy();
}
void UniformBuffer::Init(std::uint32_t size)
{
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
m_buffers[i].Init(BufferType::HighDynamic, size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, {});
m_maps[i] = m_buffers[i].GetMap();
if(m_maps[i] == nullptr)
FatalError("Vulkan: unable to map a uniform buffer");
}
}
void UniformBuffer::SetData(CPUBuffer data, std::size_t frame_index)
{
if(data.GetSize() != m_buffers[frame_index].GetSize())
{
Error("Vulkan: invalid data size to update to a uniform buffer, % != %", data.GetSize(), m_buffers[frame_index].GetSize());
return;
}
if(m_maps[frame_index] != nullptr)
std::memcpy(m_maps[frame_index], data.GetData(), data.GetSize());
}
void UniformBuffer::Destroy() noexcept
{
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
m_buffers[i].Destroy();
}
}

View File

@@ -0,0 +1,167 @@
#include <kvf.h>
#include <algorithm>
#include <Core/Logs.h>
#include <Renderer/Image.h>
#include <Renderer/Enums.h>
#include <Renderer/Buffer.h>
#include <Renderer/Descriptor.h>
#include <Renderer/RenderCore.h>
namespace Scop
{
void TransitionImageToCorrectLayout(Image& image, VkCommandBuffer cmd)
{
if(!image.IsInit())
return;
if(image.GetType() == ImageType::Color || image.GetType() == ImageType::Cube)
image.TransitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, cmd);
else if(image.GetType() == ImageType::Depth)
image.TransitionLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, cmd);
else
Error("Vulkan: cannot transition descriptor image layout, unkown image type");
}
DescriptorSet::DescriptorSet(const ShaderSetLayout& layout, VkDescriptorSetLayout vklayout, ShaderType shader_type)
: m_set_layout(vklayout)
{
for(auto& [binding, type] : layout.binds)
{
m_descriptors.emplace_back();
m_descriptors.back().type = type;
m_descriptors.back().shader_type = shader_type;
m_descriptors.back().binding = binding;
}
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
m_set[i] = kvfAllocateDescriptorSet(RenderCore::Get().GetDevice(), vklayout);
}
DescriptorSet::DescriptorSet(VkDescriptorSetLayout layout, const std::vector<Descriptor>& descriptors)
: m_set_layout(layout), m_descriptors(descriptors)
{
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
m_set[i] = kvfAllocateDescriptorSet(RenderCore::Get().GetDevice(), layout);
}
void DescriptorSet::SetImage(std::size_t i, std::uint32_t binding, class Image& image)
{
Verify(m_set[i] != VK_NULL_HANDLE, "invalid descriptor");
auto it = std::find_if(m_descriptors.begin(), m_descriptors.end(), [=](Descriptor descriptor)
{
return binding == descriptor.binding;
});
if(it == m_descriptors.end())
{
Warning("Vulkan: cannot update descriptor set image; invalid binding");
return;
}
if(it->type != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
{
Error("Vulkan: trying to bind an image to the wrong descriptor");
return;
}
it->image_ptr = &image;
}
void DescriptorSet::SetStorageBuffer(std::size_t i, std::uint32_t binding, class GPUBuffer& buffer)
{
Verify(m_set[i] != VK_NULL_HANDLE, "invalid descriptor");
auto it = std::find_if(m_descriptors.begin(), m_descriptors.end(), [=](Descriptor descriptor)
{
return binding == descriptor.binding;
});
if(it == m_descriptors.end())
{
Warning("Vulkan: cannot update descriptor set buffer; invalid binding");
return;
}
if(it->type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
{
Error("Vulkan: trying to bind a buffer to the wrong descriptor");
return;
}
it->storage_buffer_ptr = &buffer;
}
void DescriptorSet::SetUniformBuffer(std::size_t i, std::uint32_t binding, class GPUBuffer& buffer)
{
Verify(m_set[i] != VK_NULL_HANDLE, "invalid descriptor");
auto it = std::find_if(m_descriptors.begin(), m_descriptors.end(), [=](Descriptor descriptor)
{
return binding == descriptor.binding;
});
if(it == m_descriptors.end())
{
Warning("Vulkan: cannot update descriptor set buffer; invalid binding");
return;
}
if(it->type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
{
Error("Vulkan: trying to bind a buffer to the wrong descriptor");
return;
}
it->uniform_buffer_ptr = &buffer;
}
void DescriptorSet::Update(std::size_t i, VkCommandBuffer cmd) noexcept
{
Verify(m_set[i] != VK_NULL_HANDLE, "invalid descriptor");
std::size_t image_count = 0;
std::size_t buffer_count = 0;
for(auto& descriptor : m_descriptors)
{
if(descriptor.image_ptr)
image_count++;
else if(descriptor.uniform_buffer_ptr || descriptor.storage_buffer_ptr)
buffer_count++;
else
FatalError("unknown descriptor data");
}
std::vector<VkWriteDescriptorSet> writes(m_descriptors.size());
std::vector<VkDescriptorBufferInfo> buffer_infos(buffer_count);
std::vector<VkDescriptorImageInfo> image_infos(image_count);
std::size_t buffer_index = 0;
std::size_t image_index = 0;
std::size_t write_index = 0;
for(auto& descriptor : m_descriptors)
{
if(descriptor.image_ptr)
{
TransitionImageToCorrectLayout(*descriptor.image_ptr, cmd);
VkDescriptorImageInfo info{};
info.sampler = descriptor.image_ptr->GetSampler();
info.imageLayout = descriptor.image_ptr->GetLayout();
info.imageView = descriptor.image_ptr->GetImageView();
image_infos[image_index] = std::move(info);
writes[write_index] = kvfWriteImageToDescriptorSet(RenderCore::Get().GetDevice(), m_set[i], &image_infos[image_index], descriptor.binding);
image_index++;
}
else if(descriptor.uniform_buffer_ptr)
{
VkDescriptorBufferInfo info{};
info.buffer = descriptor.uniform_buffer_ptr->Get();
info.offset = descriptor.uniform_buffer_ptr->GetOffset();
info.range = VK_WHOLE_SIZE;
buffer_infos[buffer_index] = std::move(info);
writes[write_index] = kvfWriteUniformBufferToDescriptorSet(RenderCore::Get().GetDevice(), m_set[i], &buffer_infos[buffer_index], descriptor.binding);
buffer_index++;
}
else if(descriptor.storage_buffer_ptr)
{
VkDescriptorBufferInfo info{};
info.buffer = descriptor.storage_buffer_ptr->Get();
info.offset = descriptor.storage_buffer_ptr->GetOffset();
info.range = VK_WHOLE_SIZE;
buffer_infos[buffer_index] = std::move(info);
writes[write_index] = kvfWriteStorageBufferToDescriptorSet(RenderCore::Get().GetDevice(), m_set[i], &buffer_infos[buffer_index], descriptor.binding);
buffer_index++;
}
write_index++;
}
RenderCore::Get().vkUpdateDescriptorSets(RenderCore::Get().GetDevice(), writes.size(), writes.data(), 0, nullptr);
}
}

245
ScopEngine/Runtime/Sources/Renderer/Image.cpp git.filemode.normal_file
View File

@@ -0,0 +1,245 @@
#include <Renderer/Image.h>
#include <Renderer/RenderCore.h>
#include <Core/Logs.h>
namespace Scop
{
void Image::Init(ImageType type, std::uint32_t width, std::uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, bool is_multisampled)
{
m_type = type;
m_width = width;
m_height = height;
m_format = format;
m_tiling = tiling;
m_is_multisampled = is_multisampled;
KvfImageType kvf_type = KVF_IMAGE_OTHER;
switch(m_type)
{
case ImageType::Color: kvf_type = KVF_IMAGE_COLOR; break;
case ImageType::Depth: kvf_type = KVF_IMAGE_DEPTH; break;
case ImageType::Cube: kvf_type = KVF_IMAGE_CUBE; break;
default: break;
}
if(m_is_multisampled)
{
VkImageCreateInfo image_info{};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.imageType = VK_IMAGE_TYPE_2D;
image_info.extent.width = width;
image_info.extent.height = height;
image_info.extent.depth = 1;
image_info.mipLevels = 1;
image_info.arrayLayers = (m_type == ImageType::Cube ? 6 : 1);
image_info.format = format;
image_info.tiling = tiling;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.usage = usage;
image_info.samples = VK_SAMPLE_COUNT_4_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
kvfCheckVk(RenderCore::Get().vkCreateImage(RenderCore::Get().GetDevice(), &image_info, nullptr, &m_image));
}
else
m_image = kvfCreateImage(RenderCore::Get().GetDevice(), width, height, format, tiling, usage, kvf_type);
VkMemoryRequirements mem_requirements;
RenderCore::Get().vkGetImageMemoryRequirements(RenderCore::Get().GetDevice(), m_image, &mem_requirements);
m_memory = RenderCore::Get().GetAllocator().Allocate(mem_requirements.size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties), true);
RenderCore::Get().vkBindImageMemory(RenderCore::Get().GetDevice(), m_image, m_memory.memory, 0);
Message("Vulkan: image created");
s_image_count++;
}
void Image::CreateImageView(VkImageViewType type, VkImageAspectFlags aspect_flags, int layer_count) noexcept
{
m_image_view = kvfCreateImageView(RenderCore::Get().GetDevice(), m_image, m_format, type, aspect_flags, layer_count);
}
void Image::CreateSampler() noexcept
{
m_sampler = kvfCreateSampler(RenderCore::Get().GetDevice(), VK_FILTER_NEAREST, VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_MIPMAP_MODE_NEAREST);
}
void Image::TransitionLayout(VkImageLayout new_layout, VkCommandBuffer cmd)
{
if(new_layout == m_layout)
return;
bool is_single_time_cmd_buffer = (cmd == VK_NULL_HANDLE);
if(is_single_time_cmd_buffer)
cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
KvfImageType kvf_type = KVF_IMAGE_OTHER;
switch(m_type)
{
case ImageType::Color: kvf_type = KVF_IMAGE_COLOR; break;
case ImageType::Depth: kvf_type = KVF_IMAGE_DEPTH; break;
case ImageType::Cube: kvf_type = KVF_IMAGE_CUBE; break;
default: break;
}
kvfTransitionImageLayout(RenderCore::Get().GetDevice(), m_image, kvf_type, cmd, m_format, m_layout, new_layout, is_single_time_cmd_buffer);
m_layout = new_layout;
}
void Image::Clear(VkCommandBuffer cmd, Vec4f color)
{
VkImageSubresourceRange subresource_range{};
subresource_range.baseMipLevel = 0;
subresource_range.layerCount = (m_type == ImageType::Cube ? 6 : 1);
subresource_range.levelCount = 1;
subresource_range.baseArrayLayer = 0;
if(m_type == ImageType::Color || m_type == ImageType::Cube)
{
VkImageLayout old_layout = m_layout;
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd);
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
VkClearColorValue clear_color = VkClearColorValue({ { color.x, color.y, color.z, color.w } });
RenderCore::Get().vkCmdClearColorImage(cmd, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
TransitionLayout(old_layout, cmd);
}
else if(m_type == ImageType::Depth)
{
VkClearDepthStencilValue clear_depth_stencil = { 1.0f, 1 };
subresource_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd);
RenderCore::Get().vkCmdClearDepthStencilImage(cmd, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_depth_stencil, 1, &subresource_range);
TransitionLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, cmd);
}
}
void Image::DestroySampler() noexcept
{
if(m_sampler != VK_NULL_HANDLE)
kvfDestroySampler(RenderCore::Get().GetDevice(), m_sampler);
m_sampler = VK_NULL_HANDLE;
}
void Image::DestroyImageView() noexcept
{
if(m_image_view != VK_NULL_HANDLE)
kvfDestroyImageView(RenderCore::Get().GetDevice(), m_image_view);
m_image_view = VK_NULL_HANDLE;
}
void Image::Destroy() noexcept
{
DestroySampler();
DestroyImageView();
if(m_image != VK_NULL_HANDLE)
{
RenderCore::Get().GetAllocator().Deallocate(m_memory);
m_memory = NULL_MEMORY_BLOCK;
kvfDestroyImage(RenderCore::Get().GetDevice(), m_image);
}
Message("Vulkan: image destroyed");
m_image = VK_NULL_HANDLE;
s_image_count--;
}
void CubeTexture::Init(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format)
{
if(!pixels)
FatalError("Vulkan: a cubemap cannot be created without pixels data");
std::array<std::vector<std::uint8_t>, 6> texture_data;
std::uint32_t face_width = width / 4;
std::uint32_t face_height = height / 3;
std::size_t size = 0;
for(std::uint32_t cy = 0, face = 0; cy < 3; cy++)
{
for(std::uint32_t cx = 0; cx < 4; cx++)
{
if(cx == 0 || cx == 2 || cx == 3)
{
if(cy != 1)
continue;
}
texture_data[face] = std::vector<std::uint8_t>(face_width * face_height * sizeof(std::uint32_t));
size += sizeof(std::uint32_t) * width * height;
for(std::uint32_t y = 0; y < face_height; y++)
{
std::uint32_t offset = y;
std::uint32_t yp = cy * face_height + offset;
for(std::uint32_t x = 0; x < face_width; x++)
{
offset = x;
std::uint32_t xp = cx * face_width + offset;
texture_data[face][(x + y * face_width) * sizeof(std::uint32_t) + 0] = pixels.GetData()[(xp + yp * width) * sizeof(std::uint32_t) + 0];
texture_data[face][(x + y * face_width) * sizeof(std::uint32_t) + 1] = pixels.GetData()[(xp + yp * width) * sizeof(std::uint32_t) + 1];
texture_data[face][(x + y * face_width) * sizeof(std::uint32_t) + 2] = pixels.GetData()[(xp + yp * width) * sizeof(std::uint32_t) + 2];
texture_data[face][(x + y * face_width) * sizeof(std::uint32_t) + 3] = pixels.GetData()[(xp + yp * width) * sizeof(std::uint32_t) + 3];
}
}
face++;
}
}
CPUBuffer complete_data(size);
std::uint32_t pointer_offset = 0;
const std::uint32_t face_order[6] = { 3, 1, 0, 5, 2, 4 };
for(std::uint32_t face : face_order)
{
std::size_t current_size = face_width * face_height * sizeof(std::uint32_t);
std::memcpy(complete_data.GetData() + pointer_offset, texture_data[face].data(), current_size);
pointer_offset += current_size;
}
Image::Init(ImageType::Cube, face_width, face_height, format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
Image::CreateImageView(VK_IMAGE_VIEW_TYPE_CUBE, VK_IMAGE_ASPECT_COLOR_BIT, 6);
Image::CreateSampler();
GPUBuffer staging_buffer;
staging_buffer.Init(BufferType::Staging, complete_data.GetSize(), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, complete_data);
std::vector<VkBufferImageCopy> buffer_copy_regions;
std::uint32_t offset = 0;
for(std::uint32_t face = 0; face < 6; face++)
{
VkBufferImageCopy buffer_copy_region{};
buffer_copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
buffer_copy_region.imageSubresource.mipLevel = 0;
buffer_copy_region.imageSubresource.baseArrayLayer = face;
buffer_copy_region.imageSubresource.layerCount = 1;
buffer_copy_region.imageExtent.width = face_width;
buffer_copy_region.imageExtent.height = face_height;
buffer_copy_region.imageExtent.depth = 1;
buffer_copy_region.bufferOffset = offset;
buffer_copy_regions.push_back(buffer_copy_region);
offset += face_width * face_height * kvfFormatSize(format);
}
VkImageSubresourceRange subresource_range{};
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource_range.baseMipLevel = 0;
subresource_range.levelCount = 1;
subresource_range.layerCount = 6;
auto device = RenderCore::Get().GetDevice();
VkCommandBuffer cmd = kvfCreateCommandBuffer(device);
kvfBeginCommandBuffer(cmd, 0);
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd);
RenderCore::Get().vkCmdCopyBufferToImage(cmd, staging_buffer.Get(), Image::Get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, buffer_copy_regions.size(), buffer_copy_regions.data());
TransitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, cmd);
kvfEndCommandBuffer(cmd);
VkFence fence = kvfCreateFence(device);
kvfSubmitSingleTimeCommandBuffer(device, cmd, KVF_GRAPHICS_QUEUE, fence);
kvfWaitForFence(device, fence);
kvfDestroyFence(device, fence);
staging_buffer.Destroy();
}
}

Some files were not shown because too many files have changed in this diff Show More