diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt index c96d31166..2b87c96a0 100644 --- a/Editor/CMakeLists.txt +++ b/Editor/CMakeLists.txt @@ -57,7 +57,6 @@ endforeach () qt_add_qml_module( Editor NO_CACHEGEN - NO_GENERATE_QMLDIR URI editor QML_FILES ${QML_SOURCES} RESOURCES ${RESOURCES} diff --git a/Editor/Include/Editor/Core.h b/Editor/Include/Editor/Core.h deleted file mode 100644 index 1149f9f54..000000000 --- a/Editor/Include/Editor/Core.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// Created by johnk on 2024/6/23. -// - -#pragma once - -#include -#include - -namespace Editor { - class Core { - public: - static Core& Get(); - - ~Core(); - - void Initialize(int argc, char** argv); - void Cleanup(); - bool ProjectHasSet() const; - Runtime::Engine* GetEngine() const; - - private: - Core(); - - void ParseCommandLineArgs(int argc, char** argv) const; - void InitializeRuntime(); - - Runtime::Engine* engine; - }; -} diff --git a/Editor/Include/Editor/EditorEngine.h b/Editor/Include/Editor/EditorEngine.h index bf29e0903..5a677557d 100644 --- a/Editor/Include/Editor/EditorEngine.h +++ b/Editor/Include/Editor/EditorEngine.h @@ -18,4 +18,6 @@ namespace Editor { explicit EditorEngine(const Runtime::EngineInitParams& inParams); }; + + EditorEngine& GetEditorEngine(); } diff --git a/Editor/Include/Editor/Widget/Launcher.h b/Editor/Include/Editor/Widget/ProjectHub.h similarity index 68% rename from Editor/Include/Editor/Widget/Launcher.h rename to Editor/Include/Editor/Widget/ProjectHub.h index fc8335d05..61e6cdc2b 100644 --- a/Editor/Include/Editor/Widget/Launcher.h +++ b/Editor/Include/Editor/Widget/ProjectHub.h @@ -7,10 +7,10 @@ #include namespace Editor { - class Launcher final : public QmlWidget { + class ProjectHub final : public QmlWidget { Q_OBJECT public: - Launcher(); + ProjectHub(); }; } diff --git a/Editor/Qml/EButton.qml b/Editor/Qml/EButton.qml index dbbd673c9..e1bf262fb 100644 --- a/Editor/Qml/EButton.qml +++ b/Editor/Qml/EButton.qml @@ -2,7 +2,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic import QtQuick.Layouts -import QtQuick.Effects Item { enum Style { @@ -29,12 +28,12 @@ Item { property int shape: EButton.Shape.Rect signal clicked() - id: 'root' + id: root implicitWidth: btnWidget.implicitWidth implicitHeight: btnWidget.implicitHeight Button { - id: 'btnWidget' + id: btnWidget enabled: !root.disabled onClicked: root.clicked() leftPadding: 10 @@ -75,7 +74,7 @@ Item { return focus ? ETheme.primaryFocusColor : (hovered ? ETheme.primaryHoverColor : ETheme.primaryColor); } - color: getBackgroundColor(root.style, parent.down, parent.hovered, root.disabled) + color: getBackgroundColor(root.style, btnWidget.down, btnWidget.hovered, root.disabled) radius: 5 + root.shape * 8 } } diff --git a/Editor/Qml/EIcon.qml b/Editor/Qml/EIcon.qml index 619f866a5..d14da8cf6 100644 --- a/Editor/Qml/EIcon.qml +++ b/Editor/Qml/EIcon.qml @@ -1,18 +1,16 @@ import QtQuick -import QtQuick.Controls -import QtQuick.Controls.Basic import QtQuick.Effects Item { property string name: '' property int size: ETheme.iconFontSize - id: 'root' + id: root implicitWidth: imageWidget.implicitWidth implicitHeight: imageWidget.implicitHeight Image { - id: 'imageWidget' + id: imageWidget source: root.name === '' ? '' : 'Resource/Icon/%1.svg'.arg(root.name) sourceSize.width: root.size sourceSize.height: root.size diff --git a/Editor/Qml/ELauncher.qml b/Editor/Qml/EProjectHub.qml similarity index 70% rename from Editor/Qml/ELauncher.qml rename to Editor/Qml/EProjectHub.qml index 118f38887..1a9782112 100644 --- a/Editor/Qml/ELauncher.qml +++ b/Editor/Qml/EProjectHub.qml @@ -1,5 +1,4 @@ import QtQuick -import QtQuick.Controls Rectangle { color: ETheme.bgColor diff --git a/Editor/Qml/EText.qml b/Editor/Qml/EText.qml index 289e0fbbd..7d311b5c5 100644 --- a/Editor/Qml/EText.qml +++ b/Editor/Qml/EText.qml @@ -1,5 +1,4 @@ import QtQuick -import QtQuick.Controls Item { enum Style { @@ -15,7 +14,7 @@ Item { property string href: '' property int style: EText.Style.Content - id: 'root' + id: root implicitWidth: textWidget.implicitWidth implicitHeight: textWidget.implicitHeight @@ -40,7 +39,7 @@ Item { } } - id: 'textWidget' + id: textWidget text: getText(root.text, root.href, root.style) textFormat: root.style === EText.Style.Link ? Text.RichText : Text.PlainText; font.italic: root.style === EText.Style.Italic diff --git a/Editor/Qml/EWidgetSamples.qml b/Editor/Qml/EWidgetSamples.qml index 806edc943..9baf9057b 100644 --- a/Editor/Qml/EWidgetSamples.qml +++ b/Editor/Qml/EWidgetSamples.qml @@ -6,7 +6,7 @@ Rectangle { color: ETheme.bgColor ScrollView { - id: 'scrollview' + id: scrollview anchors.fill: parent anchors.margins: 20 diff --git a/Editor/Src/Core.cpp b/Editor/Src/Core.cpp deleted file mode 100644 index ab23d06bc..000000000 --- a/Editor/Src/Core.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// Created by johnk on 2024/6/23. -// - -#include -#include - -namespace Editor { - static ::Core::CmdlineArgValue caRhiType( - "rhiType", "-rhi", RHI::GetPlatformDefaultRHIAbbrString(), - "rhi abbr string, can be 'dx12' or 'vulkan'"); - - static ::Core::CmdlineArgValue caProjectFile( - "projectFile", "-project", "", - "project file path"); -} - -namespace Editor { - Core& Core::Get() - { - static Core instance; - return instance; - } - - Core::Core() - : engine(nullptr) - { - } - - Core::~Core() = default; - - void Core::Initialize(int argc, char** argv) - { - ParseCommandLineArgs(argc, argv); - InitializeRuntime(); - } - - void Core::Cleanup() // NOLINT - { - Runtime::EngineHolder::Unload(); - engine = nullptr; - ::Core::ModuleManager::Get().Unload("Runtime"); - } - - Runtime::Engine* Core::GetEngine() const - { - return engine; - } - - bool Core::ProjectHasSet() const // NOLINT - { - return !caProjectFile.GetValue().empty(); - } - - void Core::ParseCommandLineArgs(int argc, char** argv) const // NOLINT - { - ::Core::Cli::Get().Parse(argc, argv); - } - - void Core::InitializeRuntime() - { - Runtime::EngineInitParams params {}; - params.logToFile = true; - params.projectFile = caProjectFile.GetValue(); - params.rhiType = caRhiType.GetValue(); - - Runtime::EngineHolder::Load("Editor", params); - engine = &Runtime::EngineHolder::Get(); - } -} diff --git a/Editor/Src/EditorEngine.cpp b/Editor/Src/EditorEngine.cpp index e8d4e657c..1a019a114 100644 --- a/Editor/Src/EditorEngine.cpp +++ b/Editor/Src/EditorEngine.cpp @@ -16,4 +16,9 @@ namespace Editor { : Engine(inParams) { } + + EditorEngine& GetEditorEngine() + { + return static_cast(Runtime::EngineHolder::Get()); + } } diff --git a/Editor/Src/Main.cpp b/Editor/Src/Main.cpp index f5569fa59..b29755c3c 100644 --- a/Editor/Src/Main.cpp +++ b/Editor/Src/Main.cpp @@ -5,9 +5,8 @@ #include #include -#include #include -#include +#include #include #if BUILD_CONFIG_DEBUG @@ -16,14 +15,42 @@ static ::Core::CmdlineArgValue caRunSample( "Whether to run widget samples instead of editor"); #endif -int main(int argc, char* argv[]) +static ::Core::CmdlineArgValue caRhiType( + "rhiType", "-rhi", RHI::GetPlatformDefaultRHIAbbrString(), + "rhi abbr string, can be 'dx12' or 'vulkan'"); + +static ::Core::CmdlineArgValue caProjectRoot( + "projectRoot", "-project", "", + "project root path"); + +static void InitializePreQtApp(int argc, char** argv) { - Editor::Core::Get().Initialize(argc, argv); + Core::Cli::Get().Parse(argc, argv); - QApplication qtApplication(argc, argv); + Runtime::EngineInitParams params {}; + params.logToFile = true; + params.gameRoot = caProjectRoot.GetValue(); + params.rhiType = caRhiType.GetValue(); + + Runtime::EngineHolder::Load("Editor", params); +} + +static void InitializePostQtApp() +{ + Editor::QmlEngine::Get().Start(); +} + +static void Cleanup() +{ + Editor::QmlEngine::Get().Stop(); + Runtime::EngineHolder::Unload(); +} - auto& qmlEngine = Editor::QmlEngine::Get(); - qmlEngine.Start(); +int main(int argc, char* argv[]) +{ + InitializePreQtApp(argc, argv); + QApplication qtApplication(argc, argv); + InitializePostQtApp(); Common::UniquePtr mainWidget; #if BUILD_CONFIG_DEBUG @@ -31,17 +58,15 @@ int main(int argc, char* argv[]) mainWidget = new Editor::WidgetSamples(); } else #endif - if (!Editor::Core::Get().ProjectHasSet()) { // NOLINT - mainWidget = new Editor::Launcher(); + if (caProjectRoot.GetValue().empty()) { // NOLINT + mainWidget = new Editor::ProjectHub(); } else { // TODO editor main } mainWidget->show(); const int execRes = QApplication::exec(); - qmlEngine.Stop(); - mainWidget = nullptr; - Editor::Core::Get().Cleanup(); + Cleanup(); return execRes; } diff --git a/Editor/Src/Widget/Launcher.cpp b/Editor/Src/Widget/ProjectHub.cpp similarity index 52% rename from Editor/Src/Widget/Launcher.cpp rename to Editor/Src/Widget/ProjectHub.cpp index 581518fa3..1fa2c85a9 100644 --- a/Editor/Src/Widget/Launcher.cpp +++ b/Editor/Src/Widget/ProjectHub.cpp @@ -2,12 +2,12 @@ // Created by johnk on 2024/6/23. // -#include -#include // NOLINT +#include +#include // NOLINT namespace Editor { - Launcher::Launcher() - : QmlWidget("ELauncher.qml") + ProjectHub::ProjectHub() + : QmlWidget("EProjectHub.qml") { setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint); setFixedSize(1024, 768); diff --git a/Engine/Source/CMakeLists.txt b/Engine/Source/CMakeLists.txt index 5cd23a698..7973c3153 100644 --- a/Engine/Source/CMakeLists.txt +++ b/Engine/Source/CMakeLists.txt @@ -3,14 +3,16 @@ if (${BUILD_TEST}) endif() add_subdirectory(Common) -add_subdirectory(RHI) -add_subdirectory(Mirror) add_subdirectory(Core) -add_subdirectory(Render) -add_subdirectory(Runtime) +add_subdirectory(Mirror) +add_subdirectory(RHI) add_subdirectory(RHI-Dummy) add_subdirectory(RHI-Vulkan) if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") add_subdirectory(RHI-DirectX12) endif() + +add_subdirectory(Render) +add_subdirectory(Runtime) +add_subdirectory(Launch) diff --git a/Engine/Source/Common/Include/Common/Delegate.h b/Engine/Source/Common/Include/Common/Delegate.h index ed48b6cdb..02d585656 100644 --- a/Engine/Source/Common/Include/Common/Delegate.h +++ b/Engine/Source/Common/Include/Common/Delegate.h @@ -8,6 +8,7 @@ #include #include +#include #include #define IMPL_INDEX_TO_STD_PLACEHOLDER(I) \ @@ -34,6 +35,9 @@ namespace Common { template class Delegate { public: + NonCopyable(Delegate) + NonMovable(Delegate) + Delegate(); template CallbackHandle BindStatic(); diff --git a/Engine/Source/Common/Include/Common/FileSystem.h b/Engine/Source/Common/Include/Common/FileSystem.h index 7f39864cc..1954ba4d5 100644 --- a/Engine/Source/Common/Include/Common/FileSystem.h +++ b/Engine/Source/Common/Include/Common/FileSystem.h @@ -44,6 +44,7 @@ namespace Common { Path operator+(const Path& inPath) const; Path operator+(const std::string& inPath) const; Path operator+(const char* inPath) const; + bool operator==(const Path& inPath) const; private: std::filesystem::path path; diff --git a/Engine/Source/Common/Include/Common/Serialization.h b/Engine/Source/Common/Include/Common/Serialization.h index 46001577d..23332af9f 100644 --- a/Engine/Source/Common/Include/Common/Serialization.h +++ b/Engine/Source/Common/Include/Common/Serialization.h @@ -15,14 +15,18 @@ #include #include #include -#include #include +#include +#include +#include +#include #include #include #include #include +#include namespace Common { class BinarySerializeStream { @@ -121,6 +125,7 @@ namespace Common { NonCopyable(MemoryDeserializeStream) explicit MemoryDeserializeStream(const std::vector& inBytes, size_t pointerBegin = 0); ~MemoryDeserializeStream() override; + void Seek(int64_t offset) override; std::endian Endian() override; size_t Loc() override; @@ -144,7 +149,9 @@ namespace Common { template struct FieldSerializer; template size_t Serialize(BinarySerializeStream& inStream, const T& inValue); - template std::pair Deserialize(BinaryDeserializeStream& inStream, T& inValue); + template std::pair Deserialize(BinaryDeserializeStream& inStream, T& outValue); + template void SerializeToFile(const std::string& inFile, const T& inValue); + template bool DeserializeFromFile(const std::string& inFile, T& outValue); template struct JsonSerializer {}; template concept JsonSerializable = requires( @@ -158,6 +165,8 @@ namespace Common { template void JsonSerialize(rapidjson::Value& outJsonValue, rapidjson::Document::AllocatorType& inAllocator, const T& inValue); template void JsonDeserialize(const rapidjson::Value& inJsonValue, T& outValue); + template void JsonSerializeToFile(const std::string& inFile, const T& inValue, bool inPretty = true); + template void JsonDeserializeFromFile(const std::string& inFile, T& outValue); } #define IMPL_BASIC_TYPE_SERIALIZER(typeName) \ @@ -224,9 +233,9 @@ namespace Common { template BinaryFileSerializeStream::BinaryFileSerializeStream(const std::string& inFileName) { - if (const auto parent_path = std::filesystem::path(inFileName).parent_path(); - !std::filesystem::exists(parent_path)) { - std::filesystem::create_directories(parent_path); + if (const auto parentPath = Common::Path(inFileName).Parent(); + !parentPath.Exists()) { + parentPath.MakeDir(); } file = std::ofstream(inFileName, std::ios::binary); } @@ -420,16 +429,31 @@ namespace Common { } template - std::pair Deserialize(BinaryDeserializeStream& inStream, T& inValue) + std::pair Deserialize(BinaryDeserializeStream& inStream, T& outValue) { if constexpr (Serializable) { - return FieldSerializer::Deserialize(inStream, inValue); + return FieldSerializer::Deserialize(inStream, outValue); } else { QuickFailWithReason("your type is not support serialization"); return { false, 0 }; } } + template + void SerializeToFile(const std::string& inFile, const T& inValue) + { + BinaryFileSerializeStream stream(inFile); + Serialize(stream, inValue); + } + + template + bool DeserializeFromFile(const std::string& inFile, T& outValue) + { + BinaryFileDeserializeStream stream(inFile); + const auto result = Deserialize(stream, outValue); + return result.first; + } + template void JsonSerialize(rapidjson::Value& outJsonValue, rapidjson::Document::AllocatorType& inAllocator, const T& inValue) { @@ -450,6 +474,43 @@ namespace Common { } } + template void JsonSerializeToFile(const std::string& inFile, const T& inValue, bool inPretty) + { + Common::Path parentPath = Common::Path(inFile).Parent(); + if (!parentPath.Exists()) { + parentPath.MakeDir(); + } + + rapidjson::Document document; + JsonSerialize(document, document.GetAllocator(), inValue); + + char buffer[65536]; + std::FILE* file = fopen(inFile.c_str(), "wb"); + rapidjson::FileWriteStream stream(file, buffer, sizeof(buffer)); + + if (inPretty) { + rapidjson::PrettyWriter writer(stream); + document.Accept(writer); + } else { + rapidjson::Writer writer(stream); + document.Accept(writer); + } + (void) fclose(file); + } + + template void JsonDeserializeFromFile(const std::string& inFile, T& outValue) + { + char buffer[65536]; + std::FILE* file = fopen(inFile.c_str(), "rb"); + rapidjson::FileReadStream stream(file, buffer, sizeof(buffer)); + + rapidjson::Document document; + document.ParseStream(stream); + + JsonDeserialize(document, outValue); + (void) fclose(file); + } + template struct FieldSerializer { struct Header { diff --git a/Engine/Source/Common/Src/FileSystem.cpp b/Engine/Source/Common/Src/FileSystem.cpp index 5d8c7a72c..01f511470 100644 --- a/Engine/Source/Common/Src/FileSystem.cpp +++ b/Engine/Source/Common/Src/FileSystem.cpp @@ -75,6 +75,11 @@ namespace Common { return { String() + inPath, false }; } + bool Path::operator==(const Path& inPath) const + { + return path == inPath.path; + } + Path Path::Parent() const { return { path.parent_path(), false }; diff --git a/Engine/Source/Common/Test/SerializationTest.cpp b/Engine/Source/Common/Test/SerializationTest.cpp index 32bd78190..f4e7a9428 100644 --- a/Engine/Source/Common/Test/SerializationTest.cpp +++ b/Engine/Source/Common/Test/SerializationTest.cpp @@ -11,13 +11,11 @@ using namespace Common; TEST(SerializationTest, FileStreamTest) { - static std::filesystem::path fileName = "../Test/Generated/Common/SerializationTest.FileStreamTest.bin"; - std::filesystem::create_directories(fileName.parent_path()); - + static Common::Path fileName = "../Test/Generated/Common/SerializationTest.FileStreamTest.bin"; { const uint32_t value = 5; // NOLINT - BinaryFileSerializeStream stream(fileName.string()); + BinaryFileSerializeStream stream(fileName.String()); stream.Seek(3); stream.Write(value); } @@ -25,7 +23,7 @@ TEST(SerializationTest, FileStreamTest) { uint32_t value; - BinaryFileDeserializeStream stream(fileName.string()); + BinaryFileDeserializeStream stream(fileName.String()); stream.Seek(3); stream.Read(value); ASSERT_EQ(value, 5); @@ -34,9 +32,6 @@ TEST(SerializationTest, FileStreamTest) TEST(SerializationTest, ByteStreamTest) { - static std::filesystem::path fileName = "../Test/Generated/Common/SerializationTest.ByteStreamTest.bin"; - std::filesystem::create_directories(fileName.parent_path()); - std::vector memory; { const uint32_t value = 5; // NOLINT @@ -83,7 +78,38 @@ TEST(SerializationTest, TypedSerializationTest) PerformTypedSerializationTest>({ 1, true, 2 }); } -TEST(SerializationTest, JsonSerializeTest) +TEST(SerializationTest, TypedSerializationWithFileTest) +{ + static std::string fileName = "../Test/Generated/Common/SerializationTest.TypedSerializationWithFileTest.bin"; + + PerformTypeSerializationWithFileTest(fileName, false); + PerformTypeSerializationWithFileTest(fileName,true); + PerformTypeSerializationWithFileTest(fileName, -1); + PerformTypeSerializationWithFileTest(fileName, 1); + PerformTypeSerializationWithFileTest(fileName, -2); + PerformTypeSerializationWithFileTest(fileName, 2); + PerformTypeSerializationWithFileTest(fileName, -3); + PerformTypeSerializationWithFileTest(fileName, 3); + PerformTypeSerializationWithFileTest(fileName, -4); + PerformTypeSerializationWithFileTest(fileName, 4); + PerformTypeSerializationWithFileTest(fileName, 5.0f); + PerformTypeSerializationWithFileTest(fileName, 6.0); + PerformTypeSerializationWithFileTest(fileName, "hello"); + PerformTypeSerializationWithFileTest(fileName, L"hello"); + PerformTypeSerializationWithFileTest>(fileName, {}); + PerformTypeSerializationWithFileTest>(fileName, 15); + PerformTypeSerializationWithFileTest>(fileName, { 1, false }); + PerformTypeSerializationWithFileTest>(fileName, { 1, 2, 3 }); + PerformTypeSerializationWithFileTest>(fileName, { 1, 2, 3 }); + PerformTypeSerializationWithFileTest>(fileName, { 1, 2, 3 }); + PerformTypeSerializationWithFileTest>(fileName, { 1, 2, 3 }); + PerformTypeSerializationWithFileTest>(fileName, { 1, 2, 3 }); + PerformTypeSerializationWithFileTest>(fileName, { { 1, false }, { 2, true } }); + PerformTypeSerializationWithFileTest>(fileName, { { 1, false }, { 2, true } }); + PerformTypeSerializationWithFileTest>(fileName, { 1, true, 2 }); +} + +TEST(SerializationTest, JsonSerializationTest) { PerformJsonSerializationTest(false, "false"); PerformJsonSerializationTest(true, "true"); @@ -112,3 +138,35 @@ TEST(SerializationTest, JsonSerializeTest) PerformJsonSerializationTest>({ { "1", 1 }, { "2", 2 } }, R"([{"key":"1","value":1},{"key":"2","value":2}])"); PerformJsonSerializationTest>({ 1, true, 2 }, R"({"0":1,"1":true,"2":2})"); } + +TEST(SerializationTest, JsonSerializationWithFileTest) +{ + static std::string fileName = "../Test/Generated/Common/SerializationTest.JsonSerializationWithFileTest.bin"; + + PerformJsonSerializationWithFileTest(fileName, false); + PerformJsonSerializationWithFileTest(fileName, true); + PerformJsonSerializationWithFileTest(fileName, -1); + PerformJsonSerializationWithFileTest(fileName, 1); + PerformJsonSerializationWithFileTest(fileName, -2); + PerformJsonSerializationWithFileTest(fileName, 2); + PerformJsonSerializationWithFileTest(fileName, -3); + PerformJsonSerializationWithFileTest(fileName, 3); + PerformJsonSerializationWithFileTest(fileName, -4); + PerformJsonSerializationWithFileTest(fileName, 4); + PerformJsonSerializationWithFileTest(fileName, 5.0f); + PerformJsonSerializationWithFileTest(fileName, 6.0); + PerformJsonSerializationWithFileTest(fileName, "hello"); + PerformJsonSerializationWithFileTest(fileName, L"hello"); + PerformJsonSerializationWithFileTest>(fileName, {}); + PerformJsonSerializationWithFileTest>(fileName, 15); + PerformJsonSerializationWithFileTest>(fileName, { 1, false }); + PerformJsonSerializationWithFileTest>(fileName, { 1, 2, 3 }); + PerformJsonSerializationWithFileTest>(fileName, { 1, 2, 3 }); + PerformJsonSerializationWithFileTest>(fileName, { 1, 2, 3 }); + PerformJsonSerializationWithFileTest>(fileName, { 1, 2, 3 }); + PerformJsonSerializationWithFileTest>(fileName, { 1, 2, 3 }); + PerformJsonSerializationWithFileTest>(fileName, { { 1, false }, { 2, true } }); + PerformJsonSerializationWithFileTest>(fileName, { { 1, false }, { 2, true } }); + PerformJsonSerializationWithFileTest>(fileName, { { "1", 1 }, { "2", 2 } }); + PerformJsonSerializationWithFileTest>(fileName, { 1, true, 2 }); +} diff --git a/Engine/Source/Common/Test/SerializationTest.h b/Engine/Source/Common/Test/SerializationTest.h index 6c86be48f..b15c5aacb 100644 --- a/Engine/Source/Common/Test/SerializationTest.h +++ b/Engine/Source/Common/Test/SerializationTest.h @@ -4,13 +4,12 @@ #pragma once -#include - #include #include #include #include +#include inline std::string FltToJson(float value) { @@ -23,16 +22,6 @@ inline std::string FltToJson(float value) return std::string(buffer.GetString(), buffer.GetSize()); // NOLINT } -inline std::string GetEndianString(std::endian e) -{ - static std::unordered_map map = { - { std::endian::big, "std::endian::big" }, - { std::endian::little, "std::endian::little" }, - { std::endian::native, "std::endian::native" } - }; - return map.at(e); -} - template void PerformTypedSerializationTestWithStream( const std::function()>& createSerializeStream, @@ -55,12 +44,11 @@ void PerformTypedSerializationTestWithStream( template void PerformTypedSerializationTestWithEndian(const T& inValue) { - static std::filesystem::path fileName = "../Test/Generated/Common/SerializationTest.TypedSerializationTest"; - std::filesystem::create_directories(fileName.parent_path()); + static Common::Path fileName = "../Test/Generated/Common/SerializationTest.TypedSerializationTest"; PerformTypedSerializationTestWithStream( - []() -> Common::UniquePtr { return {new Common::BinaryFileSerializeStream(fileName.string()) }; }, - []() -> Common::UniquePtr { return {new Common::BinaryFileDeserializeStream(fileName.string()) }; }, + []() -> Common::UniquePtr { return {new Common::BinaryFileSerializeStream(fileName.String()) }; }, + []() -> Common::UniquePtr { return {new Common::BinaryFileDeserializeStream(fileName.String()) }; }, inValue); std::vector buffer; @@ -78,16 +66,23 @@ void PerformTypedSerializationTest(const T& inValue) PerformTypedSerializationTestWithEndian(inValue); } +template +void PerformTypeSerializationWithFileTest(const std::string& inFile, const T& inValue) +{ + Common::SerializeToFile(inFile, inValue); + + T value; + Common::DeserializeFromFile(inFile, value); + ASSERT_EQ(inValue, value); +} + template void PerformJsonSerializationTest(const T& inValue, const std::string& inExceptJson) { std::string json; { rapidjson::Document document; - - rapidjson::Value jsonValue; - Common::JsonSerialize(jsonValue, document.GetAllocator(), inValue); - document.CopyFrom(jsonValue, document.GetAllocator()); + Common::JsonSerialize(document, document.GetAllocator(), inValue); rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); @@ -103,11 +98,18 @@ void PerformJsonSerializationTest(const T& inValue, const std::string& inExceptJ rapidjson::Document document; document.Parse(json.c_str()); - rapidjson::Value jsonValue; - jsonValue.CopyFrom(document, document.GetAllocator()); - T value; - Common::JsonDeserialize(jsonValue, value); + Common::JsonDeserialize(document, value); ASSERT_EQ(inValue, value); } } + +template +void PerformJsonSerializationWithFileTest(const std::string& inFile, const T& inValue) +{ + Common::JsonSerializeToFile(inFile, inValue); + + T value; + Common::JsonDeserializeFromFile(inFile, value); + ASSERT_EQ(inValue, value); +} diff --git a/Engine/Source/Core/Include/Core/Console.h b/Engine/Source/Core/Include/Core/Console.h index 68622c362..1a6fe1f74 100644 --- a/Engine/Source/Core/Include/Core/Console.h +++ b/Engine/Source/Core/Include/Core/Console.h @@ -12,8 +12,10 @@ #include namespace Core { - enum class CSFlagBits { - // TODO + enum class CSFlagBits : uint8_t { + configOverridable = 0x1, + settingsOverridable = 0x2, + max = 0x4 }; using CSFlags = Common::Flags; DECLARE_FLAG_BITS_OP(CSFlags, CSFlagBits) @@ -23,6 +25,9 @@ namespace Core { class CORE_API ConsoleSetting { public: + NonCopyable(ConsoleSetting) + NonMovable(ConsoleSetting) + virtual ~ConsoleSetting(); const std::string& Name() const; @@ -73,6 +78,9 @@ namespace Core { template class ConsoleSettingValue final : public ConsoleSetting { public: + NonCopyable(ConsoleSettingValue) + NonMovable(ConsoleSettingValue) + ConsoleSettingValue(const std::string& inName, const std::string& inDescription, const T& inDefaultValue, const CSFlags& inFlags = CSFlags::null); ~ConsoleSettingValue() override; @@ -120,6 +128,7 @@ namespace Core { // 0: game/gameWorker // 1: render/renderWorker T value[2]; + bool dirty; }; class CORE_API Console { @@ -132,7 +141,7 @@ namespace Core { ConsoleSetting& GetSetting(const std::string& inName) const; template ConsoleSettingValue* FindSettingValue(const std::string& inName) const; template ConsoleSettingValue& GetSettingValue(const std::string& inName) const; - void PerformRenderThreadCopy() const; + void PerformRenderThreadSettingsCopy() const; private: friend class ConsoleSetting; @@ -140,6 +149,7 @@ namespace Core { Console(); void RegisterConsoleSetting(ConsoleSetting& inSetting); + void UnregisterConsoleSetting(ConsoleSetting& inSetting); std::unordered_map settings; }; @@ -149,6 +159,7 @@ namespace Core { template ConsoleSettingValue::ConsoleSettingValue(const std::string& inName, const std::string& inDescription, const T& inDefaultValue, const CSFlags& inFlags) : ConsoleSetting(inName, inDescription, inFlags) + , dirty(false) { value[0] = inDefaultValue; value[1] = inDefaultValue; @@ -187,12 +198,17 @@ namespace Core { void ConsoleSettingValue::Set(const T& inValue) { value[0] = inValue; + dirty = true; } template void ConsoleSettingValue::PerformRenderThreadCopy() { + if (!dirty) { + return; + } value[1] = value[0]; + dirty = false; } template diff --git a/Engine/Source/Core/Include/Core/Paths.h b/Engine/Source/Core/Include/Core/Paths.h index 850ec487b..f012f0f58 100644 --- a/Engine/Source/Core/Include/Core/Paths.h +++ b/Engine/Source/Core/Include/Core/Paths.h @@ -15,10 +15,10 @@ namespace Core { Paths() = delete; static void SetExecutableDir(const Common::Path& inPath); - static void SetCurrentProjectFile(const Common::Path& inFile); + static void SetGameRoot(const Common::Path& inPath); - static bool HasSetProjectFile(); static bool HasSetExecutableDir(); + static bool HasSetGameRoot(); static Common::Path WorkingDir(); static Common::Path ExecutablePath(); static Common::Path EngineRootDir(); @@ -26,18 +26,19 @@ namespace Core { static Common::Path EngineShaderDir(); static Common::Path EngineAssetDir(); static Common::Path EngineBinDir(); + static Common::Path EngineConfigDir(); static Common::Path EngineCacheDir(); static Common::Path EngineLogDir(); static Common::Path EnginePluginDir(); static Common::Path EnginePluginAssetDir(const std::string& pluginName); - static Common::Path ProjectFile(); - static Common::Path ProjectRootDir(); - static Common::Path ProjectAssetDir(); - static Common::Path ProjectBinDir(); - static Common::Path ProjectCacheDir(); - static Common::Path ProjectLogDir(); - static Common::Path ProjectPluginDir(); - static Common::Path ProjectPluginAssetDir(const std::string& pluginName); + static Common::Path GameRootDir(); + static Common::Path GameAssetDir(); + static Common::Path GameBinDir(); + static Common::Path GameConfigDir(); + static Common::Path GameCacheDir(); + static Common::Path GameLogDir(); + static Common::Path GamePluginDir(); + static Common::Path GamePluginAssetDir(const std::string& pluginName); static Common::Path EngineCMakeSourceDir(); static Common::Path EngineCMakeBinaryDir(); @@ -48,6 +49,6 @@ namespace Core { private: static Common::Path executablePath; static Common::Path workingDir; - static Common::Path currentProjectFile; + static Common::Path gameRoot; }; } diff --git a/Engine/Source/Core/Include/Core/Uri.h b/Engine/Source/Core/Include/Core/Uri.h index b5255b1a8..d58a44a16 100644 --- a/Engine/Source/Core/Include/Core/Uri.h +++ b/Engine/Source/Core/Include/Core/Uri.h @@ -8,7 +8,8 @@ #include namespace Core { - enum class UriProtocol { + enum class UriProtocol : uint8_t { + file, asset, max }; @@ -16,32 +17,38 @@ namespace Core { class CORE_API Uri { public: Uri(); - explicit Uri(std::string inValue); - Uri(const Uri& other); - Uri(Uri&& other) noexcept; - ~Uri(); - Uri& operator=(const Uri& other); - Uri& operator=(const std::string& inValue); - Uri& operator=(Uri&& other) noexcept; - bool operator==(const Uri& rhs) const; + Uri(std::string inValue); // NOLINT const std::string& Str() const; - UriProtocol Protocal() const; + UriProtocol Protocol() const; std::string Content() const; bool Empty() const; + bool operator==(const Uri& rhs) const; private: std::string value; }; + class CORE_API FileUriParser { + public: + explicit FileUriParser(const Uri& inUri); + bool IsEngineFile() const; + bool IsGameFile() const; + bool IsRegularFile() const; + Common::Path Parse() const; + + private: + std::string content; + }; + class CORE_API AssetUriParser { public: explicit AssetUriParser(const Uri& inUri); bool IsEngineAsset() const; - bool IsProjectAsset() const; + bool IsGameAsset() const; bool IsEnginePluginAsset() const; - bool IsProjectPluginAsset() const; - Common::Path AbsoluteFilePath() const; + bool IsGamePluginAsset() const; + Common::Path Parse() const; #if BUILD_TEST bool IsEngineTestAsset() const; diff --git a/Engine/Source/Core/Src/Console.cpp b/Engine/Source/Core/Src/Console.cpp index 94a402bfc..e6a017fa4 100644 --- a/Engine/Source/Core/Src/Console.cpp +++ b/Engine/Source/Core/Src/Console.cpp @@ -15,7 +15,10 @@ namespace Core { Console::Get().RegisterConsoleSetting(*this); } - ConsoleSetting::~ConsoleSetting() = default; + ConsoleSetting::~ConsoleSetting() + { + Console::Get().UnregisterConsoleSetting(*this); + } const std::string& ConsoleSetting::Name() const { @@ -56,7 +59,7 @@ namespace Core { return *settings.at(inName); } - void Console::PerformRenderThreadCopy() const + void Console::PerformRenderThreadSettingsCopy() const { for (const auto& setting : settings | std::views::values) { setting->PerformRenderThreadCopy(); @@ -69,4 +72,9 @@ namespace Core { { settings.emplace(inSetting.Name(), &inSetting); } + + void Console::UnregisterConsoleSetting(ConsoleSetting& inSetting) + { + settings.erase(inSetting.Name()); + } } diff --git a/Engine/Source/Core/Src/Module.cpp b/Engine/Source/Core/Src/Module.cpp index 8585a55bd..3720c2800 100644 --- a/Engine/Source/Core/Src/Module.cpp +++ b/Engine/Source/Core/Src/Module.cpp @@ -131,18 +131,16 @@ namespace Core { std::optional ModuleManager::SearchModule(const std::string& moduleName) { - std::vector searchPaths = { - Paths::WorkingDir(), - }; - + std::vector searchPaths; + searchPaths.reserve(5); + searchPaths.emplace_back(Paths::WorkingDir()); if (Paths::HasSetExecutableDir()) { searchPaths.emplace_back(Paths::EngineBinDir()); searchPaths.emplace_back(Paths::EnginePluginDir()); } - - if (Paths::HasSetProjectFile()) { - searchPaths.emplace_back(Paths::ProjectBinDir()); - searchPaths.emplace_back(Paths::ProjectPluginDir()); + if (Paths::HasSetGameRoot()) { + searchPaths.emplace_back(Paths::GameBinDir()); + searchPaths.emplace_back(Paths::GamePluginDir()); } for (const auto& searchPath : searchPaths) { diff --git a/Engine/Source/Core/Src/Paths.cpp b/Engine/Source/Core/Src/Paths.cpp index 6b81740be..8bd794025 100644 --- a/Engine/Source/Core/Src/Paths.cpp +++ b/Engine/Source/Core/Src/Paths.cpp @@ -8,26 +8,26 @@ namespace Core { Common::Path Paths::executablePath = Common::Path(); Common::Path Paths::workingDir = Common::Path(); - Common::Path Paths::currentProjectFile = Common::Path(); + Common::Path Paths::gameRoot = Common::Path(); void Paths::SetExecutableDir(const Common::Path& inPath) { executablePath = inPath; } - void Paths::SetCurrentProjectFile(const Common::Path& inFile) + void Paths::SetGameRoot(const Common::Path& inPath) { - currentProjectFile = inFile; + gameRoot = inPath; } - bool Paths::HasSetProjectFile() + bool Paths::HasSetExecutableDir() { - return !currentProjectFile.Empty(); + return !executablePath.Empty(); } - bool Paths::HasSetExecutableDir() + bool Paths::HasSetGameRoot() { - return !executablePath.Empty(); + return !gameRoot.Empty(); } Common::Path Paths::WorkingDir() @@ -47,15 +47,7 @@ namespace Core { Common::Path Paths::EngineRootDir() { -#if BUILD_TEST - if (HasSetExecutableDir()) { - return ExecutablePath().Parent().Parent(); - } return WorkingDir().Parent(); -#else - Assert(HasSetExecutableDir()); - return ExecutableDir().Parent().Parent(); -#endif } Common::Path Paths::EngineResDir() @@ -78,6 +70,11 @@ namespace Core { return EngineRootDir() / "Binaries"; } + Common::Path Paths::EngineConfigDir() + { + return EngineRootDir() / "Config"; + } + Common::Path Paths::EngineCacheDir() { return EngineRootDir() / "Cache"; @@ -98,44 +95,44 @@ namespace Core { return EnginePluginDir() / pluginName / "Asset"; } - Common::Path Paths::ProjectFile() + Common::Path Paths::GameRootDir() { - return currentProjectFile; + return gameRoot; } - Common::Path Paths::ProjectRootDir() + Common::Path Paths::GameAssetDir() { - return currentProjectFile.Parent(); + return GameRootDir() / "Asset"; } - Common::Path Paths::ProjectAssetDir() + Common::Path Paths::GameBinDir() { - return ProjectRootDir() / "Asset"; + return GameRootDir() / "Binaries"; } - Common::Path Paths::ProjectBinDir() + Common::Path Paths::GameConfigDir() { - return ProjectRootDir() / "Binaries"; + return GameRootDir() / "Config"; } - Common::Path Paths::ProjectCacheDir() + Common::Path Paths::GameCacheDir() { - return ProjectRootDir() / "Cache"; + return GameRootDir() / "Cache"; } - Common::Path Paths::ProjectLogDir() + Common::Path Paths::GameLogDir() { - return ProjectCacheDir() / "Log"; + return GameCacheDir() / "Log"; } - Common::Path Paths::ProjectPluginDir() + Common::Path Paths::GamePluginDir() { - return ProjectRootDir() / "Plugin"; + return GameRootDir() / "Plugin"; } - Common::Path Paths::ProjectPluginAssetDir(const std::string& pluginName) + Common::Path Paths::GamePluginAssetDir(const std::string& pluginName) { - return ProjectPluginDir() / pluginName / "Asset"; + return GamePluginDir() / pluginName / "Asset"; } Common::Path Paths::EngineCMakeSourceDir() diff --git a/Engine/Source/Core/Src/Uri.cpp b/Engine/Source/Core/Src/Uri.cpp index ab8a55c28..9ed6ed011 100644 --- a/Engine/Source/Core/Src/Uri.cpp +++ b/Engine/Source/Core/Src/Uri.cpp @@ -16,34 +16,33 @@ namespace Core { { } - Uri::Uri(const Uri& other) // NOLINT - : value(other.value) + const std::string& Uri::Str() const { + return value; } - Uri::Uri(Uri&& other) noexcept // NOLINT - : value(std::move(other.value)) + UriProtocol Uri::Protocol() const // NOLINT { - } - - Uri::~Uri() = default; + static const std::unordered_map protocolMap = { + { "file", UriProtocol::file }, + { "asset", UriProtocol::asset } + }; - Uri& Uri::operator=(const Uri& other) // NOLINT - { - value = other.value; - return *this; + const auto splits = Common::StringUtils::Split(value, "://"); + if (splits.size() != 2) { + return UriProtocol::max; + } + return protocolMap.at(splits[0]); } - Uri& Uri::operator=(const std::string& inValue) + std::string Uri::Content() const { - value = inValue; - return *this; + return Common::StringUtils::AfterFirst(value, "://"); } - Uri& Uri::operator=(Uri&& other) noexcept // NOLINT + bool Uri::Empty() const { - value = std::move(other.value); - return *this; + return value.empty(); } bool Uri::operator==(const Uri& rhs) const @@ -51,38 +50,44 @@ namespace Core { return value == rhs.value; } - const std::string& Uri::Str() const + FileUriParser::FileUriParser(const Uri& inUri) + : content(inUri.Content()) { - return value; + Assert(inUri.Protocol() == UriProtocol::file); } - UriProtocol Uri::Protocal() const // NOLINT + bool FileUriParser::IsEngineFile() const { - static const std::unordered_map protocolMap = { - { "asset", UriProtocol::asset } - }; + return Common::StringUtils::RegexMatch(content, R"(Engine/.*)"); + } - const auto splits = Common::StringUtils::Split(value, "://"); - if (splits.size() != 2) { - return UriProtocol::max; - } - return protocolMap.at(splits[0]); + bool FileUriParser::IsGameFile() const + { + return Common::StringUtils::RegexMatch(content, R"(Game/.*)"); } - std::string Uri::Content() const + bool FileUriParser::IsRegularFile() const { - return Common::StringUtils::AfterFirst(value, "://"); + return !IsEngineFile() && !IsGameFile(); } - bool Uri::Empty() const + Common::Path FileUriParser::Parse() const { - return value.empty(); + Common::Path path; + if (IsEngineFile()) { + path = Paths::EngineRootDir() / Common::StringUtils::AfterFirst(content, "Engine/"); + } else if (IsGameFile()) { + path = Paths::GameRootDir() / Common::StringUtils::AfterFirst(content, "Game/"); + } else { + path = content; + } + return path; } AssetUriParser::AssetUriParser(const Uri& inUri) : content(inUri.Content()) { - Assert(inUri.Protocal() == UriProtocol::asset); + Assert(inUri.Protocol() == UriProtocol::asset); } bool AssetUriParser::IsEngineAsset() const @@ -90,9 +95,9 @@ namespace Core { return Common::StringUtils::RegexMatch(content, R"(Engine/.*)"); } - bool AssetUriParser::IsProjectAsset() const + bool AssetUriParser::IsGameAsset() const { - return Common::StringUtils::RegexMatch(content, R"(Project/.*)"); + return Common::StringUtils::RegexMatch(content, R"(Game/.*)"); } bool AssetUriParser::IsEnginePluginAsset() const @@ -100,12 +105,12 @@ namespace Core { return Common::StringUtils::RegexMatch(content, R"(Engine/Plugin/.*)"); } - bool AssetUriParser::IsProjectPluginAsset() const + bool AssetUriParser::IsGamePluginAsset() const { - return Common::StringUtils::RegexMatch(content, R"(Project/Plugin/.*)"); + return Common::StringUtils::RegexMatch(content, R"(Game/Plugin/.*)"); } - Common::Path AssetUriParser::AbsoluteFilePath() const + Common::Path AssetUriParser::Parse() const { Common::Path path; #if BUILD_TEST @@ -117,18 +122,17 @@ namespace Core { #endif const std::string pathWithPluginName = Common::StringUtils::AfterFirst(content, "Engine/Plugin/"); path = Paths::EnginePluginAssetDir(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); - } else if (IsProjectPluginAsset()) { - const std::string pathWithPluginName = Common::StringUtils::AfterFirst(content, "Project/Plugin/"); - path = Paths::ProjectPluginAssetDir(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); + } else if (IsGamePluginAsset()) { + const std::string pathWithPluginName = Common::StringUtils::AfterFirst(content, "Game/Plugin/"); + path = Paths::GamePluginAssetDir(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); } else if (IsEngineAsset()) { path = Paths::EngineAssetDir() / Common::StringUtils::AfterFirst(content, "Engine/"); - } else if (IsProjectAsset()) { - path = Paths::ProjectAssetDir() / Common::StringUtils::AfterFirst(content, "Project/"); + } else if (IsGameAsset()) { + path = Paths::GameAssetDir() / Common::StringUtils::AfterFirst(content, "Game/"); } else { AssertWithReason(false, "bad asset uri"); } - path = path + ".expa"; - return path.Absolute(); + return path + ".expa"; } #if BUILD_TEST diff --git a/Engine/Source/Core/Test/UriTest.cpp b/Engine/Source/Core/Test/UriTest.cpp new file mode 100644 index 000000000..461519470 --- /dev/null +++ b/Engine/Source/Core/Test/UriTest.cpp @@ -0,0 +1,50 @@ +// +// Created by johnk on 2025/2/21. +// + +#include +#include + +TEST(UriTest, BasicTest) +{ + const Core::Uri u0("file://Engine/Test/a.txt"); + ASSERT_EQ(u0.Str(), "file://Engine/Test/a.txt"); + ASSERT_EQ(u0.Protocol(), Core::UriProtocol::file); + ASSERT_EQ(u0.Content(), "Engine/Test/a.txt"); +} + +TEST(UriTest, FileProtocolTest) +{ + const Core::Uri u0("file://Engine/Test/a.txt"); + const Core::FileUriParser p0(u0); + ASSERT_EQ(p0.Parse(), Core::Paths::EngineRootDir() / "Test" / "a.txt"); + + const Core::Uri u1("file://Game/Test/a.txt"); + const Core::FileUriParser p1(u1); + ASSERT_EQ(p1.Parse(), Core::Paths::GameRootDir() / "Test" / "a.txt"); + + const Core::Uri u2("file://a.txt"); + const Core::FileUriParser p2(u2); + ASSERT_EQ(p2.Parse(), Common::Path("a.txt")); +} + +TEST(UriTest, AssetProtocolTest) +{ + Core::Paths::SetGameRoot("/Game"); + + const Core::Uri u0("asset://Engine/Map/Main"); + const Core::AssetUriParser p0(u0); + ASSERT_EQ(p0.Parse(), Core::Paths::EngineAssetDir() / "Map" / "Main.expa"); + + const Core::Uri u1("asset://Game/Map/Main"); + const Core::AssetUriParser p1(u1); + ASSERT_EQ(p1.Parse(), Core::Paths::GameAssetDir() / "Map" / "Main.expa"); + + const Core::Uri u2("asset://Engine/Plugin/Particle/Sample"); + const Core::AssetUriParser p2(u2); + ASSERT_EQ(p2.Parse(), Core::Paths::EnginePluginAssetDir("Particle") / "Sample.expa"); + + const Core::Uri u3("asset://Game/Plugin/Store/Bootstrap"); + const Core::AssetUriParser p3(u3); + ASSERT_EQ(p3.Parse(), Core::Paths::GamePluginAssetDir("Store") / "Bootstrap.expa"); +} diff --git a/Engine/Source/Launch/CMakeLists.txt b/Engine/Source/Launch/CMakeLists.txt new file mode 100644 index 000000000..d1be13294 --- /dev/null +++ b/Engine/Source/Launch/CMakeLists.txt @@ -0,0 +1,8 @@ +file(GLOB_RECURSE SOURCES Src/*.cpp) +AddLibrary( + NAME Launch + TYPE STATIC + SRC ${SOURCES} + PRIVATE_INC Include + LIB glfw Runtime +) \ No newline at end of file diff --git a/Engine/Source/Launch/Include/Launch/GameClient.h b/Engine/Source/Launch/Include/Launch/GameClient.h new file mode 100644 index 000000000..e2310b521 --- /dev/null +++ b/Engine/Source/Launch/Include/Launch/GameClient.h @@ -0,0 +1,25 @@ +// +// Created by johnk on 2025/2/19. +// + +#pragma once + +#include +#include + +namespace Launch { + class GameViewport; + + class GameClient : public Runtime::Client { + public: + explicit GameClient(GameViewport& inViewport); + ~GameClient() override; + + Runtime::Viewport& GetViewport() override; + Runtime::World& GetWorld() override; + + private: + GameViewport& viewport; + Runtime::World world; + }; +} diff --git a/Engine/Source/Launch/Include/Launch/GameViewport.h b/Engine/Source/Launch/Include/Launch/GameViewport.h new file mode 100644 index 000000000..cc8b471d6 --- /dev/null +++ b/Engine/Source/Launch/Include/Launch/GameViewport.h @@ -0,0 +1,25 @@ +// +// Created by johnk on 2025/2/19. +// + +#pragma once + +#include + +namespace Launch { + class GameViewport : public Runtime::Viewport { + public: + GameViewport(); + ~GameViewport() override; + + Runtime::Client& GetClient() override; + Runtime::PresentInfo GetNextPresentInfo() override; + size_t GetWidth() override; + size_t GetHeight() override; + + private: + // TODO glfw window + // TODO rhi swap chain + // TODO game client + }; +} diff --git a/Engine/Source/Launch/Src/GameClient.cpp b/Engine/Source/Launch/Src/GameClient.cpp new file mode 100644 index 000000000..248e6b678 --- /dev/null +++ b/Engine/Source/Launch/Src/GameClient.cpp @@ -0,0 +1,26 @@ +// +// Created by johnk on 2025/2/19. +// + +#include +#include + +namespace Launch { + GameClient::GameClient(GameViewport& inViewport) + : viewport(inViewport) + , world("GameWorld", this) + { + } + + GameClient::~GameClient() = default; + + Runtime::Viewport& GameClient::GetViewport() + { + return viewport; + } + + Runtime::World& GameClient::GetWorld() + { + return world; + } +} diff --git a/Engine/Source/Launch/Src/GameViewport.cpp b/Engine/Source/Launch/Src/GameViewport.cpp new file mode 100644 index 000000000..4db293cde --- /dev/null +++ b/Engine/Source/Launch/Src/GameViewport.cpp @@ -0,0 +1,41 @@ +// +// Created by johnk on 2025/2/19. +// + +#include + +namespace Launch { + GameViewport::GameViewport() + { + // TODO + } + + GameViewport::~GameViewport() + { + // TODO + } + + Runtime::Client& GameViewport::GetClient() + { + // TODO + return *static_cast(nullptr); + } + + Runtime::PresentInfo GameViewport::GetNextPresentInfo() + { + // TODO + return Runtime::PresentInfo(); + } + + size_t GameViewport::GetWidth() + { + // TODO + return 0; + } + + size_t GameViewport::GetHeight() + { + // TODO + return 0; + } +} diff --git a/Engine/Source/Launch/Src/Main.cpp b/Engine/Source/Launch/Src/Main.cpp new file mode 100644 index 000000000..bdbf61526 --- /dev/null +++ b/Engine/Source/Launch/Src/Main.cpp @@ -0,0 +1,14 @@ +// +// Created by johnk on 2025/2/19. +// + +int main(int argc, char* argv[]) +{ + // TODO parse command line arguments + // TODO load runtime module + // TODO load engine + // TODO create game viewport + // TODO load startup level to world + // TODO main loop tick + return 0; +} diff --git a/Engine/Source/Mirror/Include/Mirror/Mirror.h b/Engine/Source/Mirror/Include/Mirror/Mirror.h index a7451b19e..ac0665eb9 100644 --- a/Engine/Source/Mirror/Include/Mirror/Mirror.h +++ b/Engine/Source/Mirror/Include/Mirror/Mirror.h @@ -430,9 +430,13 @@ namespace Mirror { std::string GetAllMeta() const; bool HasMeta(const std::string& key) const; bool GetMetaBool(const std::string& key) const; + bool GetMetaBoolOr(const std::string& key, bool defaultValue) const; int32_t GetMetaInt32(const std::string& key) const; + int32_t GetMetaInt32Or(const std::string& key, int32_t defaultValue) const; int64_t GetMetaInt64(const std::string& key) const; + int64_t GetMetaInt64Or(const std::string& key, int64_t defaultValue) const; float GetMetaFloat(const std::string& key) const; + float GetMetaFloatOr(const std::string& key, float defaultValue) const; protected: explicit ReflNode(Id inId); @@ -811,6 +815,7 @@ namespace Mirror { const MemberFunction* FindMemberFunction(const Id& inId) const; const MemberFunction& GetMemberFunction(const Id& inId) const; Any GetDefaultObject() const; + bool IsTransient() const; Any ConstructDyn(const ArgumentList& arguments) const; Any NewDyn(const ArgumentList& arguments) const; @@ -1657,6 +1662,7 @@ namespace Common { // NOLINT static size_t SerializeDyn(BinarySerializeStream& stream, const Mirror::Class& clazz, const Mirror::Argument& obj) { + Assert(!clazz.IsTransient()); const auto& className = clazz.GetName(); const auto* baseClass = clazz.GetBaseClass(); const auto& memberVariables = clazz.GetMemberVariables(); @@ -1708,6 +1714,7 @@ namespace Common { // NOLINT static size_t DeserializeDyn(BinaryDeserializeStream& stream, const Mirror::Class& clazz, const Mirror::Argument& obj) { + Assert(!clazz.IsTransient()); const auto& className = clazz.GetName(); const auto* baseClass = clazz.GetBaseClass(); const auto defaultObject = clazz.GetDefaultObject(); diff --git a/Engine/Source/Mirror/Src/Mirror.cpp b/Engine/Source/Mirror/Src/Mirror.cpp index 1debbab1b..ace19a66e 100644 --- a/Engine/Source/Mirror/Src/Mirror.cpp +++ b/Engine/Source/Mirror/Src/Mirror.cpp @@ -923,21 +923,41 @@ namespace Mirror { return Assert(false), false; } + bool ReflNode::GetMetaBoolOr(const std::string& key, bool defaultValue) const + { + return HasMeta(key) ? GetMetaBool(key) : defaultValue; + } + int32_t ReflNode::GetMetaInt32(const std::string& key) const { return std::atoi(GetMeta(key).c_str()); } + int32_t ReflNode::GetMetaInt32Or(const std::string& key, int32_t defaultValue) const + { + return HasMeta(key) ? GetMetaInt32(key) : defaultValue; + } + int64_t ReflNode::GetMetaInt64(const std::string& key) const { return std::atoll(GetMeta(key).c_str()); } + int64_t ReflNode::GetMetaInt64Or(const std::string& key, int64_t defaultValue) const + { + return HasMeta(key) ? GetMetaInt64(key) : defaultValue; + } + float ReflNode::GetMetaFloat(const std::string& key) const { return std::atof(GetMeta(key).c_str()); } + float ReflNode::GetMetaFloatOr(const std::string& key, float defaultValue) const + { + return HasMeta(key) ? GetMetaFloat(key) : defaultValue; + } + Variable::Variable(ConstructParams&& params) : ReflNode(std::move(params.id)) , owner(std::move(params.owner)) @@ -1239,7 +1259,7 @@ namespace Mirror { bool MemberVariable::IsTransient() const { - return HasMeta("transient") && GetMetaBool("transient"); + return GetMetaBoolOr("transient", false); } MemberFunction::MemberFunction(ConstructParams&& params) @@ -1793,6 +1813,11 @@ namespace Mirror { return {}; } + bool Class::IsTransient() const + { + return GetMetaBoolOr("transient", false); + } + EnumValue::EnumValue(ConstructParams&& inParams) : ReflNode(std::move(inParams.id)) , owner(std::move(inParams.owner)) diff --git a/Engine/Source/Mirror/Test/SerializationTest.cpp b/Engine/Source/Mirror/Test/SerializationTest.cpp index 530c8b2ef..9e35eeecc 100644 --- a/Engine/Source/Mirror/Test/SerializationTest.cpp +++ b/Engine/Source/Mirror/Test/SerializationTest.cpp @@ -2,13 +2,12 @@ // Created by johnk on 2023/4/23. // -#include - #include #include #include #include +#include #include using namespace Mirror; @@ -34,16 +33,15 @@ int SerializationTestStruct3::f() const // NOLINT } template -void PerformSerializationTest(const std::filesystem::path& fileName, const T& object) +void PerformSerializationTest(const Common::Path& fileName, const T& object) { - std::filesystem::create_directories(fileName.parent_path()); { - Common::BinaryFileSerializeStream stream(fileName.string()); + Common::BinaryFileSerializeStream stream(fileName.String()); Serialize(stream, object); } { - Common::BinaryFileDeserializeStream stream(fileName.string()); + Common::BinaryFileDeserializeStream stream(fileName.String()); T restored; Deserialize(stream, restored); @@ -57,10 +55,7 @@ void PerformJsonSerializationTest(const T& inValue, const std::string& inExceptJ std::string json; { rapidjson::Document document; - - rapidjson::Value jsonValue; - Common::JsonSerialize(jsonValue, document.GetAllocator(), inValue); - document.CopyFrom(jsonValue, document.GetAllocator()); + Common::JsonSerialize(document, document.GetAllocator(), inValue); rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); @@ -76,22 +71,17 @@ void PerformJsonSerializationTest(const T& inValue, const std::string& inExceptJ rapidjson::Document document; document.Parse(json.c_str()); - rapidjson::Value jsonValue; - jsonValue.CopyFrom(document, document.GetAllocator()); - T value; - Common::JsonDeserialize(jsonValue, value); + Common::JsonDeserialize(document, value); ASSERT_EQ(inValue, value); } } TEST(SerializationTest, VariableFileTest) { - static std::filesystem::path fileName = "../Test/Generated/Mirror/SerializationTest.VariableFileSerializationTest.bin"; - std::filesystem::create_directories(fileName.parent_path()); - + static Common::Path fileName = "../Test/Generated/Mirror/SerializationTest.VariableFileSerializationTest.bin"; { - Common::BinaryFileSerializeStream stream(fileName.string()); + Common::BinaryFileSerializeStream stream(fileName.String()); ga = 1; gb = 2.0f; @@ -108,7 +98,7 @@ TEST(SerializationTest, VariableFileTest) gb = 5.0f; gc = "6"; - Common::BinaryFileDeserializeStream stream(fileName.string()); + Common::BinaryFileDeserializeStream stream(fileName.String()); const auto& globalScope = Mirror::GlobalScope::Get(); globalScope.GetVariable("ga").GetDyn().Deserialize(stream); @@ -158,7 +148,7 @@ TEST(SerializationTest, EnumSerializationTest) TEST(SerializationTest, ReflNodeSerializationTest) { - static std::filesystem::path fileName = "../Test/Generated/Mirror/SerializationTest.MetaTypeSerializationTest.bin"; + static Common::Path fileName = "../Test/Generated/Mirror/SerializationTest.MetaTypeSerializationTest.bin"; const auto& globalScope = GlobalScope::Get(); PerformSerializationTest(fileName, nullptr); diff --git a/Engine/Source/Render/Include/Render/RenderThread.h b/Engine/Source/Render/Include/Render/RenderThread.h index 114992bae..fd8725d23 100644 --- a/Engine/Source/Render/Include/Render/RenderThread.h +++ b/Engine/Source/Render/Include/Render/RenderThread.h @@ -18,7 +18,7 @@ namespace Render { void Start(); void Stop(); void Flush() const; - template auto EmplaceTask(F&& inTask, Args&&... inArgs); + template auto EmplaceTask(F&& inTask); private: RenderThread(); @@ -34,8 +34,8 @@ namespace Render { void Start(); void Stop(); - template auto EmplaceTask(F&& inTask, Args&&... inArgs); - template void ExecuteTasks(size_t inTaskNum, F&& inTask, Args&&... inArgs); + template auto EmplaceTask(F&& inTask); + template void ExecuteTasks(size_t inTaskNum, F&& inTask); private: RenderWorkerThreads(); @@ -45,33 +45,33 @@ namespace Render { } namespace Render { - template - auto RenderThread::EmplaceTask(F&& inTask, Args&&... inArgs) + template + auto RenderThread::EmplaceTask(F&& inTask) { Assert(thread != nullptr); - return thread->EmplaceTask(std::forward(inTask), std::forward(inArgs)...); + return thread->EmplaceTask(std::forward(inTask)); } - template - auto RenderWorkerThreads::EmplaceTask(F&& inTask, Args&&... inArgs) + template + auto RenderWorkerThreads::EmplaceTask(F&& inTask) { - using RetType = std::invoke_result_t; + using RetType = std::invoke_result_t; Assert(threads != nullptr); - auto packedTask = std::bind(std::forward(inTask), std::forward(inArgs)...); + auto packedTask = std::bind(std::forward(inTask)); return threads->EmplaceTask([packedTask]() -> RetType { - Core::ThreadContext::SetTag(Core::ThreadTag::renderWorker); + Core::ScopedThreadTag tag(Core::ThreadTag::renderWorker); return packedTask(); }); } - template - void RenderWorkerThreads::ExecuteTasks(size_t inTaskNum, F&& inTask, Args&&... inArgs) + template + void RenderWorkerThreads::ExecuteTasks(size_t inTaskNum, F&& inTask) { Assert(threads != nullptr); - auto packedTask = std::bind(std::forward(inTask), std::placeholders::_1, std::forward(inArgs)...); + auto packedTask = std::bind(std::forward(inTask), std::placeholders::_1); threads->ExecuteTasks(inTaskNum, [packedTask](size_t inIndex) -> void { - Core::ThreadContext::SetTag(Core::ThreadTag::renderWorker); + Core::ScopedThreadTag tag(Core::ThreadTag::renderWorker); packedTask(inIndex); }); } diff --git a/Engine/Source/Render/Include/Render/Scene.h b/Engine/Source/Render/Include/Render/Scene.h index 49ee4e098..04b0a9909 100644 --- a/Engine/Source/Render/Include/Render/Scene.h +++ b/Engine/Source/Render/Include/Render/Scene.h @@ -24,6 +24,7 @@ namespace Render { NonMovable(Scene) LightSPH AddLight(); + void UpdateLight(const LightSPH& inHandle); void RemoveLight(const LightSPH& inHandle); private: diff --git a/Engine/Source/Render/Include/Render/Shader.h b/Engine/Source/Render/Include/Render/Shader.h index b991d53ca..fcf5bced2 100644 --- a/Engine/Source/Render/Include/Render/Shader.h +++ b/Engine/Source/Render/Include/Render/Shader.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include namespace Render { class Shader {}; @@ -371,18 +371,10 @@ namespace Render { template void GlobalShaderType::ReadCode() { - static std::unordered_map pathMap = { - { "/Engine/Shader", Core::Paths::EngineShaderDir().String() } - }; - const std::string sourceFile = Shader::sourceFile; - for (const auto& [mapFrom, mapTo] : pathMap) { - if (sourceFile.starts_with(mapFrom)) { - code = Common::FileUtils::ReadTextFile(Common::StringUtils::Replace(sourceFile, mapFrom, mapTo)); - return; - } - } - code = Common::FileUtils::ReadTextFile(sourceFile); + const Core::Uri uri(std::format("file://{}", sourceFile)); + const Core::FileUriParser parser(uri); + code = Common::FileUtils::ReadTextFile(parser.Parse().String()); } template diff --git a/Engine/Source/Render/Include/Render/View.h b/Engine/Source/Render/Include/Render/View.h index 14ca88670..acff73c6e 100644 --- a/Engine/Source/Render/Include/Render/View.h +++ b/Engine/Source/Render/Include/Render/View.h @@ -25,6 +25,5 @@ namespace Render { Common::FMat4x4 projectionMatrix; Common::FRect viewport; // TODO viewOrigin, etc... - // TODO render resource (uniform buffer) }; } diff --git a/Engine/Source/Render/Src/Scene.cpp b/Engine/Source/Render/Src/Scene.cpp index 768c1335b..4b1d6842d 100644 --- a/Engine/Source/Render/Src/Scene.cpp +++ b/Engine/Source/Render/Src/Scene.cpp @@ -14,6 +14,8 @@ namespace Render { return lights.Emplace(); } + void Scene::UpdateLight(const LightSPH& inHandle) {} + void Scene::RemoveLight(const LightSPH& inHandle) { lights.Erase(inHandle); diff --git a/Engine/Source/Render/Test/ShaderTest.cpp b/Engine/Source/Render/Test/ShaderTest.cpp index 5ad642e93..bdec663c5 100644 --- a/Engine/Source/Render/Test/ShaderTest.cpp +++ b/Engine/Source/Render/Test/ShaderTest.cpp @@ -10,7 +10,7 @@ class TestGlobalShaderVS : public Render::GlobalShader { public: ShaderInfo( "TestGlobalShader", - "/Engine/Shader/Test/TestGlobalShader.esl", + "Engine/Shader/Test/TestGlobalShader.esl", "VSMain", RHI::ShaderStageBits::sVertex); diff --git a/Engine/Source/Runtime/Include/Runtime/Asset.h b/Engine/Source/Runtime/Include/Runtime/Asset/Asset.h similarity index 98% rename from Engine/Source/Runtime/Include/Runtime/Asset.h rename to Engine/Source/Runtime/Include/Runtime/Asset/Asset.h index dacf7c808..fe4b5fca9 100644 --- a/Engine/Source/Runtime/Include/Runtime/Asset.h +++ b/Engine/Source/Runtime/Include/Runtime/Asset/Asset.h @@ -555,9 +555,8 @@ namespace Runtime { return; } - Core::AssetUriParser parser(assetRef.Uri()); - auto pathString = parser.AbsoluteFilePath().String(); - Common::BinaryFileSerializeStream stream(pathString); + const Core::AssetUriParser parser(assetRef.Uri()); + Common::BinaryFileSerializeStream stream(parser.Parse().Absolute().String()); Mirror::Any ref = std::ref(*assetRef.Get()); ref.Serialize(stream); @@ -572,9 +571,8 @@ namespace Runtime { template A> AssetPtr AssetManager::LoadInternal(const Core::Uri& uri) { - Core::AssetUriParser parser(uri); - auto pathString = parser.AbsoluteFilePath().String(); - Common::BinaryFileDeserializeStream stream(pathString); + const Core::AssetUriParser parser(uri); + Common::BinaryFileDeserializeStream stream(parser.Parse().Absolute().String()); AssetPtr result = Common::SharedPtr(new A()); Mirror::Any ref = std::ref(*result.Get()); diff --git a/Engine/Source/Runtime/Include/Runtime/Asset/Level.h b/Engine/Source/Runtime/Include/Runtime/Asset/Level.h new file mode 100644 index 000000000..c908f2904 --- /dev/null +++ b/Engine/Source/Runtime/Include/Runtime/Asset/Level.h @@ -0,0 +1,20 @@ +// +// Created by johnk on 2025/2/19. +// + +#pragma once + +#include +#include +#include +#include + +namespace Runtime { + struct RUNTIME_API EClass() Level : public Asset { + EClassBody(Level) + + explicit Level(Core::Uri inUri); + + EProperty() ECArchive archive; + }; +} diff --git a/Engine/Source/Runtime/Include/Runtime/Client.h b/Engine/Source/Runtime/Include/Runtime/Client.h new file mode 100644 index 000000000..1c821cf63 --- /dev/null +++ b/Engine/Source/Runtime/Include/Runtime/Client.h @@ -0,0 +1,22 @@ +// +// Created by johnk on 2025/2/18. +// + +#pragma once + +#include + +namespace Runtime { + class World; + + class Client { + public: + virtual ~Client(); + + virtual World& GetWorld() = 0; + virtual Viewport& GetViewport() = 0; + + protected: + Client(); + }; +} diff --git a/Engine/Source/Runtime/Include/Runtime/Component/Transform.h b/Engine/Source/Runtime/Include/Runtime/Component/Transform.h index 17312532c..053df2192 100644 --- a/Engine/Source/Runtime/Include/Runtime/Component/Transform.h +++ b/Engine/Source/Runtime/Include/Runtime/Component/Transform.h @@ -40,7 +40,7 @@ namespace Runtime { EProperty() Entity nextBro; }; - class HierarchyUtils { + class HierarchyOps { public: using TraverseFunc = std::function; diff --git a/Engine/Source/Runtime/Include/Runtime/Component/View.h b/Engine/Source/Runtime/Include/Runtime/Component/View.h index 6f4e0c858..9a3f57f91 100644 --- a/Engine/Source/Runtime/Include/Runtime/Component/View.h +++ b/Engine/Source/Runtime/Include/Runtime/Component/View.h @@ -10,14 +10,6 @@ #include namespace Runtime { - constexpr uint8_t maxPlayerCount = 4; - - struct RUNTIME_API EClass(transient) GViewSource final { - EClassBody(GViewSource) - - Common::InplaceVector playerCameras; - }; - struct RUNTIME_API EClass(transient) Camera final { EClassBody(Camera) diff --git a/Engine/Source/Runtime/Include/Runtime/ECS.h b/Engine/Source/Runtime/Include/Runtime/ECS.h index 4da239072..1c23cc4eb 100644 --- a/Engine/Source/Runtime/Include/Runtime/ECS.h +++ b/Engine/Source/Runtime/Include/Runtime/ECS.h @@ -24,6 +24,8 @@ namespace Runtime { using SystemClass = const Mirror::Class*; class ECRegistry; + class Client; + class SystemSetupContext; template concept ECRegistryOrConst = std::is_same_v, ECRegistry>; @@ -32,7 +34,7 @@ namespace Runtime { public: EPolyClassBody(System) - explicit System(ECRegistry& inRegistry); + explicit System(ECRegistry& inRegistry, const SystemSetupContext&); virtual ~System(); virtual void Tick(float inDeltaTimeSeconds); @@ -58,7 +60,7 @@ namespace Runtime::Internal { Mirror::Any Get(ElemPtr inElem) const; CompClass Class() const; size_t Offset() const; - size_t Size() const; + size_t MemorySize() const; private: using MoveConstructFunc = Mirror::Any(ElemPtr, size_t, const Mirror::Any&); @@ -86,7 +88,7 @@ namespace Runtime::Internal { ElemPtr GetElem(Entity inEntity) const; Mirror::Any GetComp(Entity inEntity, CompClass inCompClass); Mirror::Any GetComp(Entity inEntity, CompClass inCompClass) const; - size_t Size() const; + size_t Count() const; auto All() const; const std::vector& GetRttiVec() const; ArchetypeId Id() const; @@ -106,7 +108,7 @@ namespace Runtime::Internal { ElemPtr ElemAt(size_t inIndex) const; ArchetypeId id; - size_t size; + size_t count; size_t elemSize; std::vector rttiVec; std::unordered_map rttiMap; @@ -122,9 +124,10 @@ namespace Runtime::Internal { EntityPool(); - size_t Size() const; + size_t Count() const; bool Valid(Entity inEntity) const; Entity Allocate(); + void Allocate(Entity inEntity); void Free(Entity inEntity); void Clear(); void Each(const EntityTraverseFunc& inFunc) const; @@ -134,7 +137,7 @@ namespace Runtime::Internal { ConstIter End() const; private: - size_t counter; + uint32_t counter; std::set free; std::set allocated; std::unordered_map archetypeMap; @@ -143,7 +146,7 @@ namespace Runtime::Internal { class SystemFactory { public: explicit SystemFactory(SystemClass inClass); - Common::UniquePtr Build(ECRegistry& inRegistry) const; + Common::UniquePtr Build(ECRegistry& inRegistry, const SystemSetupContext& inSetupContext) const; std::unordered_map GetArguments(); const std::unordered_map& GetArguments() const; SystemClass GetClass() const; @@ -243,7 +246,7 @@ namespace Runtime { NonMovable(BasicView) template void Each(F&& inFunc) const; - size_t Size() const; + size_t Count() const; ConstIter Begin() const; ConstIter End() const; ConstIter begin() const; @@ -284,7 +287,7 @@ namespace Runtime { NonMovable(BasicRuntimeView) template void Each(F&& inFunc) const; - size_t Size() const; + size_t Count() const; ConstIter Begin() const; ConstIter End() const; ConstIter begin() const; @@ -322,7 +325,7 @@ namespace Runtime { Observer& ObConstructedDyn(CompClass inClass); Observer& ObUpdatedDyn(CompClass inClass); Observer& ObRemoved(CompClass inClass); - size_t Size() const; + size_t Count() const; void Each(const EntityTraverseFunc& inFunc) const; void EachThenClear(const EntityTraverseFunc& inFunc); void Clear(); @@ -353,9 +356,9 @@ namespace Runtime { NonCopyable(EventsObserver) NonMovable(EventsObserver) - size_t ConstructedSize() const; - size_t UpdatedSize() const; - size_t RemovedSize() const; + size_t ConstructedCount() const; + size_t UpdatedCount() const; + size_t RemovedCount() const; void EachConstructed(const EntityTraverseFunc& inFunc) const; void EachUpdated(const EntityTraverseFunc& inFunc) const; void EachRemoved(const EntityTraverseFunc& inFunc) const; @@ -385,9 +388,9 @@ namespace Runtime { NonCopyable(EventsObserverDyn) NonMovable(EventsObserverDyn) - size_t ConstructedSize() const; - size_t UpdatedSize() const; - size_t RemovedSize() const; + size_t ConstructedCount() const; + size_t UpdatedCount() const; + size_t RemovedCount() const; void EachConstructed(const EntityTraverseFunc& inFunc) const; void EachUpdated(const EntityTraverseFunc& inFunc) const; void EachRemoved(const EntityTraverseFunc& inFunc) const; @@ -405,9 +408,24 @@ namespace Runtime { Observer removedObserver; }; + struct RUNTIME_API EClass() EntityArchive { + EClassBody(EntityArchive) + + EProperty() std::unordered_map> comps; + }; + + struct RUNTIME_API EClass() ECArchive { + EClassBody(ECArchive) + + EProperty() std::unordered_map entities; + EProperty() std::unordered_map> globalComps; + }; + class RUNTIME_API ECRegistry { public: using EntityTraverseFunc = Internal::EntityPool::EntityTraverseFunc; + using CompTraverseFunc = std::function; + using GCompTraverseFunc = std::function; using DynUpdateFunc = std::function; using ConstIter = Internal::EntityPool::ConstIter; using CompEvent = Common::Delegate; @@ -433,20 +451,20 @@ namespace Runtime { ECRegistry& operator=(const ECRegistry& inOther); ECRegistry& operator=(ECRegistry&& inOther) noexcept; - void ResetTransients(); - // entity - // TODO create with hint Entity Create(); + void Create(Entity inEntity); void Destroy(Entity inEntity); bool Valid(Entity inEntity) const; - size_t Size() const; + size_t Count() const; void Clear(); void Each(const EntityTraverseFunc& inFunc) const; ConstIter Begin() const; ConstIter End() const; ConstIter begin() const; ConstIter end() const; + void CompEach(Entity inEntity, const CompTraverseFunc& inFunc) const; + size_t CompCount(Entity inEntity) const; // component static template C& Emplace(Entity inEntity, Args&&... inArgs); @@ -507,17 +525,23 @@ namespace Runtime { Mirror::Any GGetDyn(GCompClass inClass); Mirror::Any GGetDyn(GCompClass inClass) const; GCompEvents& GEventsDyn(GCompClass inClass); + void GCompEach(const GCompTraverseFunc& inFunc) const; + size_t GCompCount() const; + // comp observer Observer Observer(); + // serialization + void Save(ECArchive& outArchive) const; + void Load(const ECArchive& inArchive); + + // utils + void CheckEventsUnbound() const; + private: template friend class BasicView; template friend class BasicRuntimeView; - template void NotifyConstructed(Entity inEntity); - template void NotifyRemove(Entity inEntity); - template void GNotifyConstructed(); - template void GNotifyRemove(); void NotifyConstructedDyn(CompClass inClass, Entity inEntity); void NotifyRemoveDyn(CompClass inClass, Entity inEntity); void GNotifyConstructedDyn(GCompClass inClass); @@ -526,7 +550,7 @@ namespace Runtime { Internal::EntityPool entities; std::unordered_map globalComps; std::unordered_map archetypes; - // transients + // transients, not copy or move std::unordered_map compEvents; std::unordered_map globalCompEvents; }; @@ -611,9 +635,15 @@ namespace Runtime { std::vector systemGraph; }; + struct SystemSetupContext { + SystemSetupContext(); + + Client* client; + }; + class SystemGraphExecutor { public: - explicit SystemGraphExecutor(ECRegistry& inEcRegistry, const SystemGraph& inSystemGraph); + explicit SystemGraphExecutor(ECRegistry& inEcRegistry, const SystemGraph& inSystemGraph, const SystemSetupContext& inSetupContext); ~SystemGraphExecutor(); NonCopyable(SystemGraphExecutor) @@ -727,7 +757,7 @@ namespace Runtime { } template - size_t BasicView, C...>::Size() const + size_t BasicView, C...>::Count() const { return result.size(); } @@ -776,7 +806,7 @@ namespace Runtime { continue; } - result.reserve(result.size() + archetype.Size()); + result.reserve(result.size() + archetype.Count()); for (auto entity : archetype.All()) { result.emplace_back(entity, inRegistry.template Get>(entity)...); } @@ -801,7 +831,7 @@ namespace Runtime { } template - size_t BasicRuntimeView::Size() const + size_t BasicRuntimeView::Count() const { return resultEntities.size(); } @@ -862,8 +892,8 @@ namespace Runtime { continue; } - resultEntities.reserve(result.size() + archetype.Size()); - result.reserve(result.size() + archetype.Size()); + resultEntities.reserve(result.size() + archetype.Count()); + result.reserve(result.size() + archetype.Count()); for (const auto entity : archetype.All()) { std::vector comps; comps.reserve(includes.size()); @@ -928,21 +958,21 @@ namespace Runtime { EventsObserver::~EventsObserver() = default; template - size_t EventsObserver::ConstructedSize() const + size_t EventsObserver::ConstructedCount() const { - return constructedObserver.Size(); + return constructedObserver.Count(); } template - size_t EventsObserver::UpdatedSize() const + size_t EventsObserver::UpdatedCount() const { - return updatedObserver.Size(); + return updatedObserver.Count(); } template - size_t EventsObserver::RemovedSize() const + size_t EventsObserver::RemovedCount() const { - return removedObserver.Size(); + return removedObserver.Count(); } template @@ -1118,18 +1148,6 @@ namespace Runtime { NotifyUpdatedDyn(Internal::GetClass(), inEntity); } - template - void ECRegistry::NotifyConstructed(Entity inEntity) - { - NotifyConstructedDyn(Internal::GetClass(), inEntity); - } - - template - void ECRegistry::NotifyRemove(Entity inEntity) - { - NotifyRemoveDyn(Internal::GetClass(), inEntity); - } - template G& ECRegistry::GEmplace(Args&&... inArgs) { @@ -1199,18 +1217,6 @@ namespace Runtime { GNotifyUpdatedDyn(Internal::GetClass()); } - template - void ECRegistry::GNotifyConstructed() - { - GNotifyConstructedDyn(Internal::GetClass()); - } - - template - void ECRegistry::GNotifyRemove() - { - GNotifyRemoveDyn(Internal::GetClass()); - } - template Internal::SystemFactory& SystemGroup::EmplaceSystem() { diff --git a/Engine/Source/Runtime/Include/Runtime/Engine.h b/Engine/Source/Runtime/Include/Runtime/Engine.h index 80fa5c3a9..ae235f469 100644 --- a/Engine/Source/Runtime/Include/Runtime/Engine.h +++ b/Engine/Source/Runtime/Include/Runtime/Engine.h @@ -8,15 +8,15 @@ #include #include -#include #include +#include namespace Runtime { class World; struct EngineInitParams { bool logToFile; - std::string projectFile; + std::string gameRoot; std::string rhiType; }; @@ -29,17 +29,18 @@ namespace Runtime { void MountWorld(World* inWorld); void UnmountWorld(World* inWorld); Render::RenderModule& GetRenderModule() const; - void Tick(float inDeltaTimeSeconds) const; - Common::UniquePtr CreateWorld(const std::string& inName = "") const; + void Tick(float inDeltaTimeSeconds); protected: explicit Engine(const EngineInitParams& inParams); - void AttachLogFile(); + void AttachLogFile() const; void InitRender(const std::string& inRhiTypeStr); std::unordered_set worlds; Render::RenderModule* renderModule; + std::future lastFrameRenderThreadFence; + std::future last2FrameRenderThreadFence; }; class RUNTIME_API MinEngine final : public Engine { diff --git a/Engine/Source/Runtime/Include/Runtime/Settings/Registry.h b/Engine/Source/Runtime/Include/Runtime/Settings/Registry.h new file mode 100644 index 000000000..7bfda33a3 --- /dev/null +++ b/Engine/Source/Runtime/Include/Runtime/Settings/Registry.h @@ -0,0 +1,67 @@ +// +// Created by johnk on 2025/2/21. +// + +#pragma once + +#include +#include +#include + +namespace Runtime { + using SettingsClass = const Mirror::Class*; + + class RUNTIME_API SettingsRegistry { + public: + EClassBody(SettingsRegistry) + + EFunc() static SettingsRegistry& Get(); + + ~SettingsRegistry(); + + template void RegisterSettings(); + template T& GetSettings(); + template void SaveSettings(); + template void LoadSettings(); + + EFunc() void RegisterSettingsDyn(SettingsClass inClass); + EFunc() Mirror::Any GetSettingsDyn(SettingsClass inClass); + EFunc() void SaveSettingsDyn(SettingsClass inClass); + EFunc() void LoadSettingsDyn(SettingsClass inClass); + EFunc() void SaveAllSettings(); + EFunc() void LoadAllSettings(); + + private: + SettingsRegistry(); + + void RegisterInternalSettings(); + + std::unordered_map settingsMap; + }; +} + +namespace Runtime { + template + void SettingsRegistry::RegisterSettings() + { + RegisterSettingsDyn(&Mirror::Class::Get()); + } + + template + T& SettingsRegistry::GetSettings() + { + return GetSettingsDyn(&Mirror::Class::Get()).template As(); + } + + template + void SettingsRegistry::SaveSettings() + { + SaveSettingsDyn(&Mirror::Class::Get()); + } + + template + void SettingsRegistry::LoadSettings() + { + LoadSettingsDyn(&Mirror::Class::Get()); + } +} diff --git a/Engine/Source/Runtime/Include/Runtime/System/Scene.h b/Engine/Source/Runtime/Include/Runtime/System/Scene.h index bc87448c6..3dff2593a 100644 --- a/Engine/Source/Runtime/Include/Runtime/System/Scene.h +++ b/Engine/Source/Runtime/Include/Runtime/System/Scene.h @@ -16,7 +16,7 @@ namespace Runtime { EPolyClassBody(SceneSystem) public: - explicit SceneSystem(ECRegistry& inRegistry); + explicit SceneSystem(ECRegistry& inRegistry, const SystemSetupContext& inContext); ~SceneSystem() override; NonCopyable(SceneSystem) @@ -42,8 +42,13 @@ namespace Runtime { SPMap lightSceneProxies; }; } - namespace Runtime { + template + void SceneSystem::FillLightSceneProxy(const Render::LightSPH& inHandle, const L& inLight, const WorldTransform* inTransform) + { + Unimplement(); + } + template <> inline void SceneSystem::FillLightSceneProxy(const Render::LightSPH& inHandle, const DirectionalLight& inLight, const WorldTransform* inTransform) { @@ -79,7 +84,7 @@ namespace Runtime { void SceneSystem::EmplaceLightSceneProxy(Entity inEntity) { lightSceneProxies.emplace(inEntity, scene->AddLight()); - UpdateLightSceneProxy(inEntity); + FillLightSceneProxy(lightSceneProxies.at(inEntity), registry.Get(inEntity), registry.Find(inEntity)); } template @@ -87,6 +92,7 @@ namespace Runtime { { const Render::LightSPH& handle = lightSceneProxies.at(inEntity); FillLightSceneProxy(handle, registry.Get(inEntity), registry.Find(inEntity)); + scene->UpdateLight(handle); } template diff --git a/Engine/Source/Runtime/Include/Runtime/System/Transform.h b/Engine/Source/Runtime/Include/Runtime/System/Transform.h index 48d963e27..3601fa1ec 100644 --- a/Engine/Source/Runtime/Include/Runtime/System/Transform.h +++ b/Engine/Source/Runtime/Include/Runtime/System/Transform.h @@ -14,7 +14,7 @@ namespace Runtime { EPolyClassBody(TransformSystem) public: - explicit TransformSystem(ECRegistry& inRegistry); + explicit TransformSystem(ECRegistry& inRegistry, const SystemSetupContext& inContext); ~TransformSystem() override; NonCopyable(TransformSystem) diff --git a/Engine/Source/Runtime/Include/Runtime/Viewport.h b/Engine/Source/Runtime/Include/Runtime/Viewport.h new file mode 100644 index 000000000..82e638ebc --- /dev/null +++ b/Engine/Source/Runtime/Include/Runtime/Viewport.h @@ -0,0 +1,36 @@ +// +// Created by johnk on 2025/2/18. +// + +#pragma once + +#include +#include + +namespace Runtime { + class Client; + + struct PresentInfo { + PresentInfo(); + + RHI::Texture* backBuffer; + RHI::Semaphore* imageReadySemaphore; + RHI::Semaphore* renderFinishedSemaphore; + }; + + class Viewport { + public: + virtual ~Viewport(); + + virtual Client& GetClient() = 0; + virtual PresentInfo GetNextPresentInfo() = 0; + virtual size_t GetWidth() = 0; + virtual size_t GetHeight() = 0; + + // TODO mouse keyboard inputs etc. + // TODO resolution change etc. + + protected: + Viewport(); + }; +} diff --git a/Engine/Source/Runtime/Include/Runtime/World.h b/Engine/Source/Runtime/Include/Runtime/World.h index 785431cce..5538da960 100644 --- a/Engine/Source/Runtime/Include/Runtime/World.h +++ b/Engine/Source/Runtime/Include/Runtime/World.h @@ -9,6 +9,8 @@ #include #include +#include +#include namespace Runtime { enum class PlayStatus : uint8_t { @@ -24,8 +26,8 @@ namespace Runtime { NonMovable(World) ~World(); + World(std::string inName, Client* inClient); void SetSystemGraph(const SystemGraph& inSystemGraph); - void Reset(); PlayStatus PlayStatus() const; bool Stopped() const; bool Playing() const; @@ -34,15 +36,17 @@ namespace Runtime { void Resume(); void Pause(); void Stop(); + void LoadFrom(AssetPtr inLevel); + void SaveTo(AssetPtr inLevel); private: friend class Engine; - explicit World(const std::string& inName = ""); void Tick(float inDeltaTimeSeconds); std::string name; Runtime::PlayStatus playStatus; + SystemSetupContext systemSetupContext; ECRegistry ecRegistry; SystemGraph systemGraph; std::optional executor; diff --git a/Engine/Source/Runtime/Src/Asset.cpp b/Engine/Source/Runtime/Src/Asset/Asset.cpp similarity index 93% rename from Engine/Source/Runtime/Src/Asset.cpp rename to Engine/Source/Runtime/Src/Asset/Asset.cpp index 8c77ab8e7..01fc0c404 100644 --- a/Engine/Source/Runtime/Src/Asset.cpp +++ b/Engine/Source/Runtime/Src/Asset/Asset.cpp @@ -2,7 +2,7 @@ // Created by johnk on 2023/10/10. // -#include +#include namespace Runtime { Asset::Asset() = default; diff --git a/Engine/Source/Runtime/Src/Asset/Level.cpp b/Engine/Source/Runtime/Src/Asset/Level.cpp new file mode 100644 index 000000000..eca3f9564 --- /dev/null +++ b/Engine/Source/Runtime/Src/Asset/Level.cpp @@ -0,0 +1,12 @@ +// +// Created by johnk on 2025/2/19. +// + +#include + +namespace Runtime { + Level::Level(Core::Uri inUri) + : Asset(std::move(inUri)) + { + } +} diff --git a/Engine/Source/Runtime/Src/Client.cpp b/Engine/Source/Runtime/Src/Client.cpp new file mode 100644 index 000000000..01da78d82 --- /dev/null +++ b/Engine/Source/Runtime/Src/Client.cpp @@ -0,0 +1,11 @@ +// +// Created by johnk on 2025/2/18. +// + +#include + +namespace Runtime { + Client::Client() = default; + + Client::~Client() = default; +} diff --git a/Engine/Source/Runtime/Src/Component/Transform.cpp b/Engine/Source/Runtime/Src/Component/Transform.cpp index 29e611655..65e572ee8 100644 --- a/Engine/Source/Runtime/Src/Component/Transform.cpp +++ b/Engine/Source/Runtime/Src/Component/Transform.cpp @@ -29,25 +29,25 @@ namespace Runtime { { } - bool HierarchyUtils::HasParent(ECRegistry& inRegistry, Entity inTarget) + bool HierarchyOps::HasParent(ECRegistry& inRegistry, Entity inTarget) { const auto& hierarchy = inRegistry.Get(inTarget); return hierarchy.parent != entityNull; } - bool HierarchyUtils::HasBro(ECRegistry& inRegistry, Entity inTarget) + bool HierarchyOps::HasBro(ECRegistry& inRegistry, Entity inTarget) { const auto& hierarchy = inRegistry.Get(inTarget); return hierarchy.prevBro != entityNull || hierarchy.nextBro != entityNull; } - bool HierarchyUtils::HasChildren(ECRegistry& inRegistry, Entity inTarget) + bool HierarchyOps::HasChildren(ECRegistry& inRegistry, Entity inTarget) { const auto& hierarchy = inRegistry.Get(inTarget); return hierarchy.firstChild != entityNull; } - void HierarchyUtils::AttachToParent(ECRegistry& inRegistry, Entity inChild, Entity inParent) + void HierarchyOps::AttachToParent(ECRegistry& inRegistry, Entity inChild, Entity inParent) { Assert(!HasParent(inRegistry, inChild) && !HasBro(inRegistry, inChild)); auto& childHierarchy = inRegistry.Get(inChild); @@ -61,7 +61,7 @@ namespace Runtime { parentHierarchy.firstChild = inChild; } - void HierarchyUtils::DetachFromParent(ECRegistry& inRegistry, Entity inChild) + void HierarchyOps::DetachFromParent(ECRegistry& inRegistry, Entity inChild) { Assert(HasParent(inRegistry, inChild)); auto& childHierarchy = inRegistry.Get(inChild); @@ -93,7 +93,7 @@ namespace Runtime { childHierarchy.parent = entityNull; } - void HierarchyUtils::TraverseChildren(ECRegistry& inRegistry, Entity inParent, const TraverseFunc& inFunc) + void HierarchyOps::TraverseChildren(ECRegistry& inRegistry, Entity inParent, const TraverseFunc& inFunc) { const auto& parentHierarchy = inRegistry.Get(inParent); for (auto child = parentHierarchy.firstChild; child != entityNull;) { @@ -103,7 +103,7 @@ namespace Runtime { } } - void HierarchyUtils::TraverseChildrenRecursively(ECRegistry& inRegistry, Entity inParent, const TraverseFunc& inFunc) // NOLINT + void HierarchyOps::TraverseChildrenRecursively(ECRegistry& inRegistry, Entity inParent, const TraverseFunc& inFunc) // NOLINT { const auto& parentHierarchy = inRegistry.Get(inParent); for (auto child = parentHierarchy.firstChild; child != entityNull;) { diff --git a/Engine/Source/Runtime/Src/ECS.cpp b/Engine/Source/Runtime/Src/ECS.cpp index 1b6f1e29f..1cef17cc6 100644 --- a/Engine/Source/Runtime/Src/ECS.cpp +++ b/Engine/Source/Runtime/Src/ECS.cpp @@ -7,7 +7,7 @@ #include namespace Runtime { - System::System(ECRegistry& inRegistry) + System::System(ECRegistry& inRegistry, const SystemSetupContext&) : registry(inRegistry) { } @@ -18,6 +18,11 @@ namespace Runtime { } namespace Runtime::Internal { + static bool IsGlobalCompClass(GCompClass inClass) + { + return inClass->HasMeta("global") && inClass->GetMetaBool("global"); + } + CompRtti::CompRtti(CompClass inClass) : clazz(inClass) , bound(false) @@ -67,14 +72,14 @@ namespace Runtime::Internal { return offset; } - size_t CompRtti::Size() const + size_t CompRtti::MemorySize() const { return clazz->SizeOf(); } Archetype::Archetype(const std::vector& inRttiVec) : id(0) - , size(0) + , count(0) , elemSize(1) , rttiVec(inRttiVec) { @@ -86,7 +91,7 @@ namespace Runtime::Internal { id += clazz->GetTypeInfo()->id; rtti.Bind(elemSize); - elemSize += rtti.Size(); + elemSize += rtti.MemorySize(); } } @@ -123,7 +128,7 @@ namespace Runtime::Internal { ElemPtr Archetype::EmplaceElem(Entity inEntity) { ElemPtr result = AllocateNewElemBack(); - auto backElem = size - 1; + auto backElem = count - 1; entityMap.emplace(inEntity, backElem); elemMap.emplace(backElem, inEntity); return result; @@ -152,7 +157,7 @@ namespace Runtime::Internal { { const auto elemIndex = entityMap.at(inEntity); ElemPtr elem = ElemAt(elemIndex); - const auto lastElemIndex = Size() - 1; + const auto lastElemIndex = count - 1; const auto entityToLastElem = elemMap.at(lastElemIndex); ElemPtr lastElem = ElemAt(lastElemIndex); for (const auto& rtti : rttiVec) { @@ -162,7 +167,7 @@ namespace Runtime::Internal { entityMap.erase(inEntity); elemMap.at(elemIndex) = entityToLastElem; elemMap.erase(lastElemIndex); - size--; + count--; } ElemPtr Archetype::GetElem(Entity inEntity) const @@ -182,9 +187,9 @@ namespace Runtime::Internal { return GetCompRtti(inCompClass).Get(element).ConstRef(); } - size_t Archetype::Size() const + size_t Archetype::Count() const { - return size; + return count; } const std::vector& Archetype::GetRttiVec() const @@ -246,7 +251,7 @@ namespace Runtime::Internal { const size_t newCapacity = static_cast(std::ceil(static_cast(std::max(Capacity(), static_cast(1))) * inRatio)); std::vector newMemory(newCapacity * elemSize); - for (auto i = 0; i < size; i++) { + for (auto i = 0; i < count; i++) { for (const auto& rtti : rttiVec) { void* dstElem = ElemAt(newMemory, i); void* srcElem = ElemAt(i); @@ -259,11 +264,11 @@ namespace Runtime::Internal { void* Archetype::AllocateNewElemBack() { - if (Size() == Capacity()) { + if (Count() == Capacity()) { Reserve(); } - size++; - return ElemAt(size - 1); + count++; + return ElemAt(count - 1); } EntityPool::EntityPool() @@ -271,7 +276,7 @@ namespace Runtime::Internal { { } - size_t EntityPool::Size() const + size_t EntityPool::Count() const { return allocated.size(); } @@ -289,12 +294,27 @@ namespace Runtime::Internal { free.erase(result); } else { result = counter++; - allocated.emplace(result); } + allocated.emplace(result); SetArchetype(result, 0); return result; } + void EntityPool::Allocate(Entity inEntity) + { + if (inEntity < counter) { + Assert(free.contains(inEntity)); + free.erase(inEntity); + } else { + for (uint32_t i = counter; i < inEntity; i++) { + free.emplace(i); + } + counter = inEntity + 1; + } + allocated.emplace(inEntity); + SetArchetype(inEntity, 0); + } + void EntityPool::Free(Entity inEntity) { Assert(Valid(inEntity)); @@ -305,7 +325,7 @@ namespace Runtime::Internal { void EntityPool::Clear() { - counter = 0; + counter = 1; free.clear(); allocated.clear(); archetypeMap.clear(); @@ -344,9 +364,9 @@ namespace Runtime::Internal { BuildArgumentLists(); } - Common::UniquePtr SystemFactory::Build(ECRegistry& inRegistry) const + Common::UniquePtr SystemFactory::Build(ECRegistry& inRegistry, const SystemSetupContext& inSetupContext) const { - const Mirror::Any system = clazz->New(inRegistry); + const Mirror::Any system = clazz->New(inRegistry, inSetupContext); const Mirror::Any systemRef = system.Deref(); for (const auto& [name, argument] : arguments) { clazz->GetMemberVariable(name).SetDyn(systemRef, argument.ConstRef()); @@ -450,7 +470,7 @@ namespace Runtime { return OnEvent(registry.EventsDyn(inClass).onRemove); } - size_t Observer::Size() const + size_t Observer::Count() const { return entities.size(); } @@ -541,19 +561,19 @@ namespace Runtime { EventsObserverDyn::~EventsObserverDyn() = default; - size_t EventsObserverDyn::ConstructedSize() const + size_t EventsObserverDyn::ConstructedCount() const { - return constructedObserver.Size(); + return constructedObserver.Count(); } - size_t EventsObserverDyn::UpdatedSize() const + size_t EventsObserverDyn::UpdatedCount() const { - return updatedObserver.Size(); + return updatedObserver.Count(); } - size_t EventsObserverDyn::RemovedSize() const + size_t EventsObserverDyn::RemovedCount() const { - return removedObserver.Size(); + return removedObserver.Count(); } void EventsObserverDyn::EachConstructed(const EntityTraverseFunc& inFunc) const @@ -615,7 +635,7 @@ namespace Runtime { ECRegistry::~ECRegistry() { - ResetTransients(); + CheckEventsUnbound(); } ECRegistry::ECRegistry(const ECRegistry& inOther) @@ -648,12 +668,6 @@ namespace Runtime { return *this; } - void ECRegistry::ResetTransients() - { - compEvents.clear(); - globalCompEvents.clear(); - } - Entity ECRegistry::Create() { const Entity result = entities.Allocate(); @@ -661,6 +675,12 @@ namespace Runtime { return result; } + void ECRegistry::Create(Entity inEntity) + { + entities.Allocate(inEntity); + archetypes.at(entities.GetArchetype(inEntity)).EmplaceElem(inEntity); + } + void ECRegistry::Destroy(Entity inEntity) { Assert(Valid(inEntity)); @@ -673,9 +693,9 @@ namespace Runtime { return entities.Valid(inEntity); } - size_t ECRegistry::Size() const + size_t ECRegistry::Count() const { - return entities.Size(); + return entities.Count(); } void ECRegistry::Clear() @@ -683,12 +703,12 @@ namespace Runtime { entities.Clear(); globalComps.clear(); archetypes.clear(); - ResetTransients(); + archetypes.emplace(0, Internal::Archetype({})); } void ECRegistry::Each(const EntityTraverseFunc& inFunc) const { - return entities.Each(inFunc); + entities.Each(inFunc); } ECRegistry::ConstIter ECRegistry::Begin() const @@ -711,6 +731,22 @@ namespace Runtime { return End(); } + void ECRegistry::CompEach(Entity inEntity, const CompTraverseFunc& inFunc) const + { + const Internal::ArchetypeId archetypeId = entities.GetArchetype(inEntity); + const Internal::Archetype& archetype = archetypes.at(archetypeId); + for (const auto& compRtti : archetype.GetRttiVec()) { + inFunc(compRtti.Class()); + } + } + + size_t ECRegistry::CompCount(Entity inEntity) const + { + const Internal::ArchetypeId archetypeId = entities.GetArchetype(inEntity); + const Internal::Archetype& archetype = archetypes.at(archetypeId); + return archetype.GetRttiVec().size(); + } + Runtime::RuntimeView ECRegistry::RuntimeView(const RuntimeFilter& inFilter) { return Runtime::RuntimeView { *this, inFilter }; @@ -763,6 +799,80 @@ namespace Runtime { return Runtime::Observer { *this }; } + void ECRegistry::Save(ECArchive& outArchive) const + { + outArchive = {}; + outArchive.entities.reserve(Count()); + Each([&](Entity entity) -> void { + outArchive.entities.emplace(entity, EntityArchive {}); + auto& comps = outArchive.entities.at(entity).comps; + comps.reserve(CompCount(entity)); + + CompEach(entity, [&](CompClass clazz) -> void { + if (clazz->IsTransient()) { + return; + } + comps.emplace(clazz, std::vector {}); + Common::MemorySerializeStream stream(comps.at(clazz)); + GetDyn(clazz, entity).Serialize(stream); + }); + }); + + auto& gComps = outArchive.globalComps; + gComps.reserve(GCompCount()); + GCompEach([&](GCompClass clazz) -> void { + if (clazz->IsTransient()) { + return; + } + gComps.emplace(clazz, std::vector {}); + Common::MemorySerializeStream stream(gComps.at(clazz)); + GGetDyn(clazz).Serialize(stream); + }); + } + + void ECRegistry::Load(const ECArchive& inArchive) + { + Clear(); + + for (const auto& [entity, entityArchive] : inArchive.entities) { + Create(entity); + for (const auto& [compClass, compData] : entityArchive.comps) { + if (compClass->IsTransient()) { + continue; + } + Assert(compClass->HasDefaultConstructor()); + Mirror::Any compRef = EmplaceDyn(compClass, entity, {}); + Common::MemoryDeserializeStream stream(compData); + compRef.Deserialize(stream); + } + } + + for (const auto& [gCompClass, gCompData] : inArchive.globalComps) { + if (gCompClass->IsTransient()) { + continue; + } + Assert(gCompClass->HasDefaultConstructor()); + Mirror::Any gCompRef = GEmplaceDyn(gCompClass, {}); + Common::MemoryDeserializeStream stream(gCompData); + gCompRef.Deserialize(stream); + } + } + + void ECRegistry::CheckEventsUnbound() const + { + for (const auto& events : compEvents | std::views::values) { + Assert(events.onConstructed.Count() == 0); + Assert(events.onUpdated.Count() == 0); + Assert(events.onRemove.Count() == 0); + } + + for (const auto& events : globalCompEvents | std::views::values) { + Assert(events.onConstructed.Count() == 0); + Assert(events.onUpdated.Count() == 0); + Assert(events.onRemove.Count() == 0); + } + } + Mirror::Any ECRegistry::EmplaceDyn(CompClass inClass, Entity inEntity, const Mirror::ArgumentList& inArgs) { Assert(Valid(inEntity)); @@ -886,6 +996,7 @@ namespace Runtime { Mirror::Any ECRegistry::GEmplaceDyn(GCompClass inClass, const Mirror::ArgumentList& inArgs) { + Assert(Internal::IsGlobalCompClass(inClass)); Assert(!GHasDyn(inClass)); globalComps.emplace(inClass, inClass->ConstructDyn(inArgs)); GNotifyConstructedDyn(inClass); @@ -894,6 +1005,7 @@ namespace Runtime { void ECRegistry::GRemoveDyn(GCompClass inClass) { + Assert(Internal::IsGlobalCompClass(inClass)); Assert(GHasDyn(inClass)); GNotifyRemoveDyn(inClass); globalComps.erase(inClass); @@ -901,6 +1013,7 @@ namespace Runtime { void ECRegistry::GUpdateDyn(GCompClass inClass, const DynUpdateFunc& inFunc) { + Assert(Internal::IsGlobalCompClass(inClass)); Assert(GHasDyn(inClass)); inFunc(GGetDyn(inClass)); GNotifyUpdatedDyn(inClass); @@ -908,42 +1021,61 @@ namespace Runtime { GScopedUpdaterDyn ECRegistry::GUpdateDyn(GCompClass inClass) { + Assert(Internal::IsGlobalCompClass(inClass)); Assert(GHasDyn(inClass)); return { *this, inClass, GGetDyn(inClass) }; } bool ECRegistry::GHasDyn(GCompClass inClass) const { + Assert(Internal::IsGlobalCompClass(inClass)); return globalComps.contains(inClass); } Mirror::Any ECRegistry::GFindDyn(GCompClass inClass) { + Assert(Internal::IsGlobalCompClass(inClass)); return GHasDyn(inClass) ? GGetDyn(inClass) : Mirror::Any(); } Mirror::Any ECRegistry::GFindDyn(GCompClass inClass) const { + Assert(Internal::IsGlobalCompClass(inClass)); return GHasDyn(inClass) ? GGetDyn(inClass) : Mirror::Any(); } Mirror::Any ECRegistry::GGetDyn(GCompClass inClass) { + Assert(Internal::IsGlobalCompClass(inClass)); Assert(GHasDyn(inClass)); return globalComps.at(inClass).Ref(); } Mirror::Any ECRegistry::GGetDyn(GCompClass inClass) const { + Assert(Internal::IsGlobalCompClass(inClass)); Assert(GHasDyn(inClass)); return globalComps.at(inClass).ConstRef(); } ECRegistry::GCompEvents& ECRegistry::GEventsDyn(GCompClass inClass) { + Assert(Internal::IsGlobalCompClass(inClass)); return globalCompEvents[inClass]; } + void ECRegistry::GCompEach(const GCompTraverseFunc& inFunc) const + { + for (const auto& clazz : globalComps | std::views::keys) { + inFunc(clazz); + } + } + + size_t ECRegistry::GCompCount() const + { + return globalComps.size(); + } + SystemGroup::SystemGroup(std::string inName, SystemExecuteStrategy inStrategy) : name(std::move(inName)) , strategy(inStrategy) @@ -1155,13 +1287,18 @@ namespace Runtime { .wait(); } - SystemGraphExecutor::SystemGraphExecutor(ECRegistry& inEcRegistry, const SystemGraph& inSystemGraph) + SystemSetupContext::SystemSetupContext() + : client(nullptr) + { + } + + SystemGraphExecutor::SystemGraphExecutor(ECRegistry& inEcRegistry, const SystemGraph& inSystemGraph, const SystemSetupContext& inSetupContext) : ecRegistry(inEcRegistry) , systemGraph(inSystemGraph) , pipeline(inSystemGraph) { pipeline.ParallelPerformAction([&](SystemPipeline::SystemContext& context) -> void { - context.instance = context.factory.Build(inEcRegistry); + context.instance = context.factory.Build(inEcRegistry, inSetupContext); }); } @@ -1170,6 +1307,7 @@ namespace Runtime { pipeline.ParallelPerformAction([](SystemPipeline::SystemContext& context) -> void { context.instance = nullptr; }); + ecRegistry.CheckEventsUnbound(); } void SystemGraphExecutor::Tick(float inDeltaTimeSeconds) diff --git a/Engine/Source/Runtime/Src/Engine.cpp b/Engine/Source/Runtime/Src/Engine.cpp index d9376716d..4d5daf6f3 100644 --- a/Engine/Source/Runtime/Src/Engine.cpp +++ b/Engine/Source/Runtime/Src/Engine.cpp @@ -9,20 +9,26 @@ #include #include #include +#include +#include #include +#include namespace Runtime { Engine::Engine(const EngineInitParams& inParams) { - Core::ScopedThreadTag tag(Core::ThreadTag::game); - if (!inParams.projectFile.empty()) { - Core::Paths::SetCurrentProjectFile(inParams.projectFile); + Core::ThreadContext::SetTag(Core::ThreadTag::game); + if (!inParams.gameRoot.empty()) { + Core::Paths::SetGameRoot(inParams.gameRoot); } if (inParams.logToFile) { AttachLogFile(); } InitRender(inParams.rhiType); + + // TODO load all modules and plugins + SettingsRegistry::Get().LoadAllSettings(); } Engine::~Engine() @@ -46,8 +52,18 @@ namespace Runtime { return *renderModule; } - void Engine::Tick(float inDeltaTimeSeconds) const + void Engine::Tick(float inDeltaTimeSeconds) { + // game thread can run faster than render thread 1 frame as max + if (last2FrameRenderThreadFence.valid()) { + last2FrameRenderThreadFence.wait(); + } + + auto& renderThread = renderModule->GetRenderThread(); + renderThread.EmplaceTask([]() -> void { + Core::Console::Get().PerformRenderThreadSettingsCopy(); + }); + for (auto* world : worlds) { if (!world->Playing()) { continue; @@ -55,19 +71,15 @@ namespace Runtime { world->Tick(inDeltaTimeSeconds); } - // TODO emplace render thread task, like wait fence, console command copy - } - - Common::UniquePtr Engine::CreateWorld(const std::string& inName) const // NOLINT - { - return new World(inName); + last2FrameRenderThreadFence = std::move(lastFrameRenderThreadFence); + lastFrameRenderThreadFence = renderThread.EmplaceTask([]() -> void {}); } - void Engine::AttachLogFile() // NOLINT + void Engine::AttachLogFile() const // NOLINT { const auto time = Common::Time(Common::TimePoint::Now()); const auto logName = Core::Paths::ExecutablePath().FileNameWithoutExtension() + "-" + time.ToString() + ".log"; - const auto logFile = ((Core::Paths::HasSetProjectFile() ? Core::Paths::ProjectLogDir() : Core::Paths::EngineLogDir()) / logName).String(); + const auto logFile = ((Core::Paths::HasSetGameRoot() ? Core::Paths::GameLogDir() : Core::Paths::EngineLogDir()) / logName).String(); Core::Logger::Get().Attach(new Core::FileLogStream(logFile)); LogInfo(Core, "logger attached to file {}", logFile); diff --git a/Engine/Source/Runtime/Src/Settings/Registry.cpp b/Engine/Source/Runtime/Src/Settings/Registry.cpp new file mode 100644 index 000000000..4e3794c87 --- /dev/null +++ b/Engine/Source/Runtime/Src/Settings/Registry.cpp @@ -0,0 +1,67 @@ +// +// Created by johnk on 2025/2/21. +// + +#include + +namespace Runtime::Internal { + static Common::Path GetConfigPathForSettings(SettingsClass inClass) + { + // TODO order: game -> engine + } +} + +namespace Runtime { + SettingsRegistry& SettingsRegistry::Get() + { + static SettingsRegistry instance; + return instance; + } + + SettingsRegistry::SettingsRegistry() + { + RegisterInternalSettings(); + } + + SettingsRegistry::~SettingsRegistry() = default; + + void SettingsRegistry::RegisterSettingsDyn(SettingsClass inClass) + { + settingsMap.emplace(inClass, inClass->Construct()); + } + + Mirror::Any SettingsRegistry::GetSettingsDyn(SettingsClass inClass) + { + AssertWithReason(settingsMap.contains(inClass), "did you forget to register settings in your module ?"); + return settingsMap.at(inClass); + } + + void SettingsRegistry::SaveSettingsDyn(SettingsClass inClass) + { + // TODO + } + + void SettingsRegistry::LoadSettingsDyn(SettingsClass inClass) + { + // TODO + } + + void SettingsRegistry::SaveAllSettings() + { + for (const auto* clazz : settingsMap | std::views::keys) { + SaveSettingsDyn(clazz); + } + } + + void SettingsRegistry::LoadAllSettings() + { + for (const auto* clazz : settingsMap | std::views::keys) { + LoadSettingsDyn(clazz); + } + } + + void SettingsRegistry::RegisterInternalSettings() + { + // TODO + } +} diff --git a/Engine/Source/Runtime/Src/System/Scene.cpp b/Engine/Source/Runtime/Src/System/Scene.cpp index 65bd5fa19..d1f03f2cd 100644 --- a/Engine/Source/Runtime/Src/System/Scene.cpp +++ b/Engine/Source/Runtime/Src/System/Scene.cpp @@ -3,13 +3,14 @@ // #include +#include #include #include namespace Runtime { - SceneSystem::SceneSystem(ECRegistry& inRegistry) - : System(inRegistry) - , renderModule(Core::ModuleManager::Get().GetTyped("Render")) + SceneSystem::SceneSystem(ECRegistry& inRegistry, const SystemSetupContext& inContext) + : System(inRegistry, inContext) + , renderModule(EngineHolder::Get().GetRenderModule()) , scene(renderModule.NewScene()) , transformUpdatedObserver(inRegistry.Observer()) , directionalLightsObserver(inRegistry.EventsObserver()) diff --git a/Engine/Source/Runtime/Src/System/Transform.cpp b/Engine/Source/Runtime/Src/System/Transform.cpp index bde634481..5c3b84800 100644 --- a/Engine/Source/Runtime/Src/System/Transform.cpp +++ b/Engine/Source/Runtime/Src/System/Transform.cpp @@ -5,8 +5,8 @@ #include namespace Runtime { - TransformSystem::TransformSystem(ECRegistry& inRegistry) - : System(inRegistry) + TransformSystem::TransformSystem(ECRegistry& inRegistry, const SystemSetupContext& inContext) + : System(inRegistry, inContext) , worldTransformUpdatedObserver(registry.Observer()) , localTransformUpdatedObserver(registry.Observer()) { @@ -23,21 +23,21 @@ namespace Runtime { std::vector pendingUpdateChildrenWorldTransforms; std::vector pendingUpdateSelfAndChildrenWorldTransforms; - pendingUpdateLocalTransforms.reserve(worldTransformUpdatedObserver.Size()); - pendingUpdateChildrenWorldTransforms.reserve(worldTransformUpdatedObserver.Size()); + pendingUpdateLocalTransforms.reserve(worldTransformUpdatedObserver.Count()); + pendingUpdateChildrenWorldTransforms.reserve(worldTransformUpdatedObserver.Count()); worldTransformUpdatedObserver.EachThenClear([&](Entity e) -> void { - if (registry.Has(e) && registry.Has(e) && HierarchyUtils::HasParent(registry, e)) { + if (registry.Has(e) && registry.Has(e) && HierarchyOps::HasParent(registry, e)) { pendingUpdateLocalTransforms.emplace_back(e); } - if (registry.Has(e) && HierarchyUtils::HasChildren(registry, e)) { + if (registry.Has(e) && HierarchyOps::HasChildren(registry, e)) { pendingUpdateChildrenWorldTransforms.emplace_back(e); } }); - pendingUpdateSelfAndChildrenWorldTransforms.reserve(localTransformUpdatedObserver.Size()); + pendingUpdateSelfAndChildrenWorldTransforms.reserve(localTransformUpdatedObserver.Count()); localTransformUpdatedObserver.EachThenClear([&](Entity e) -> void { - if (registry.Has(e) && registry.Has(e) && HierarchyUtils::HasParent(registry, e)) { + if (registry.Has(e) && registry.Has(e) && HierarchyOps::HasParent(registry, e)) { pendingUpdateSelfAndChildrenWorldTransforms.emplace_back(e); } }); @@ -70,7 +70,7 @@ namespace Runtime { }; for (const auto e : pendingUpdateChildrenWorldTransforms) { - HierarchyUtils::TraverseChildrenRecursively(registry, e, [&](Entity child, Entity parent) -> void { + HierarchyOps::TraverseChildrenRecursively(registry, e, [&](Entity child, Entity parent) -> void { updateWorldByLocal(child, parent); }); } @@ -78,7 +78,7 @@ namespace Runtime { const auto& hierarchy = registry.Get(e); updateWorldByLocal(e, hierarchy.parent); - HierarchyUtils::TraverseChildrenRecursively(registry, e, [&](Entity child, Entity parent) -> void { + HierarchyOps::TraverseChildrenRecursively(registry, e, [&](Entity child, Entity parent) -> void { updateWorldByLocal(child, parent); }); } diff --git a/Engine/Source/Runtime/Src/Viewport.cpp b/Engine/Source/Runtime/Src/Viewport.cpp new file mode 100644 index 000000000..4fbe26044 --- /dev/null +++ b/Engine/Source/Runtime/Src/Viewport.cpp @@ -0,0 +1,18 @@ +// +// Created by johnk on 2025/2/18. +// + +#include + +namespace Runtime { + Viewport::~Viewport() = default; + + Viewport::Viewport() = default; + + PresentInfo::PresentInfo() + : backBuffer(nullptr) + , imageReadySemaphore(nullptr) + , renderFinishedSemaphore(nullptr) + { + } +} diff --git a/Engine/Source/Runtime/Src/World.cpp b/Engine/Source/Runtime/Src/World.cpp index 5fb1e05c0..0060f3d80 100644 --- a/Engine/Source/Runtime/Src/World.cpp +++ b/Engine/Source/Runtime/Src/World.cpp @@ -5,12 +5,17 @@ #include #include +#include + namespace Runtime { - World::World(const std::string& inName) - : name(inName) + World::World(std::string inName, Client* inClient) + : name(std::move(inName)) , playStatus(PlayStatus::stopped) + , systemSetupContext() { EngineHolder::Get().MountWorld(this); + + systemSetupContext.client = inClient; } World::~World() @@ -23,11 +28,6 @@ namespace Runtime { systemGraph = inSystemGraph; } - void World::Reset() - { - playStatus = PlayStatus::stopped; - } - PlayStatus World::PlayStatus() const { return playStatus; @@ -52,7 +52,7 @@ namespace Runtime { { Assert(Stopped() && !executor.has_value()); playStatus = PlayStatus::playing; - executor.emplace(ecRegistry, systemGraph); + executor.emplace(ecRegistry, systemGraph, systemSetupContext); } void World::Resume() @@ -74,6 +74,18 @@ namespace Runtime { executor.reset(); } + void World::LoadFrom(AssetPtr inLevel) + { + Assert(Stopped()); + ecRegistry.Load(inLevel->archive); + } + + void World::SaveTo(AssetPtr inLevel) + { + Assert(Stopped()); + ecRegistry.Save(inLevel->archive); + } + void World::Tick(float inDeltaTimeSeconds) { executor->Tick(inDeltaTimeSeconds); diff --git a/Engine/Source/Runtime/Test/AssetTest.h b/Engine/Source/Runtime/Test/AssetTest.h index 0f108e9b1..33ed10113 100644 --- a/Engine/Source/Runtime/Test/AssetTest.h +++ b/Engine/Source/Runtime/Test/AssetTest.h @@ -5,7 +5,7 @@ #pragma once #include -#include +#include using namespace Common; using namespace Runtime; diff --git a/Engine/Source/Runtime/Test/ECSTest.cpp b/Engine/Source/Runtime/Test/ECSTest.cpp index 26cdd767a..66dee1313 100644 --- a/Engine/Source/Runtime/Test/ECSTest.cpp +++ b/Engine/Source/Runtime/Test/ECSTest.cpp @@ -10,7 +10,7 @@ TEST(ECSTest, EntityTest) ECRegistry registry; const auto entity0 = registry.Create(); const auto entity1 = registry.Create(); - ASSERT_EQ(registry.Size(), 2); + ASSERT_EQ(registry.Count(), 2); ASSERT_TRUE(registry.Valid(entity0)); ASSERT_TRUE(registry.Valid(entity1)); ASSERT_FALSE(registry.Valid(99)); @@ -74,28 +74,28 @@ TEST(ECSTest, ComponentStaticTest) std::unordered_set expected = { entity1, entity2 }; const auto view0 = registry.View(); - ASSERT_EQ(view0.Size(), 2); + ASSERT_EQ(view0.Count(), 2); view0.Each([&](Entity e, CompA& compA) -> void { ASSERT_TRUE(expected.contains(e)); }); expected = { entity1 }; const auto view1 = registry.ConstView(Exclude {}); - ASSERT_EQ(view1.Size(), 1); + ASSERT_EQ(view1.Count(), 1); view1.Each([&](Entity e, const CompA& compA) -> void { ASSERT_TRUE(expected.contains(e)); }); expected = { entity2, entity3 }; const auto view2 = registry.View(); - ASSERT_EQ(view2.Size(), 2); + ASSERT_EQ(view2.Count(), 2); for (const auto& [entity, compB] : view2) { ASSERT_TRUE(expected.contains(entity)); } expected = { entity3 }; const auto view3 = registry.ConstView(Exclude {}); - ASSERT_EQ(view3.Size(), 1); + ASSERT_EQ(view3.Count(), 1); for (const auto& [entity, compB] : view3) { ASSERT_TRUE(expected.contains(entity)); } @@ -123,7 +123,7 @@ TEST(ECSTest, ComponentDynamicTest) const auto view0 = registry.ConstRuntimeView( RuntimeFilter() .IncludeDyn(compAClass)); - ASSERT_EQ(view0.Size(), 2); + ASSERT_EQ(view0.Count(), 2); view0.Each([&](Entity e, const CompA& compA) -> void { ASSERT_TRUE(expected.contains(e)); }); @@ -138,7 +138,7 @@ TEST(ECSTest, ComponentDynamicTest) RuntimeFilter() .Include()); expected = { entity0, entity1, entity2 }; - ASSERT_EQ(view1.Size(), 3); + ASSERT_EQ(view1.Count(), 3); for (const auto& entity : view1) { ASSERT_TRUE(expected.contains(entity)); } @@ -148,7 +148,7 @@ TEST(ECSTest, ComponentDynamicTest) .Include() .Exclude()); expected = { entity0, entity1 }; - ASSERT_EQ(view2.Size(), 2); + ASSERT_EQ(view2.Count(), 2); view2.Each([&](Entity e, const CompA& compA) -> void { ASSERT_TRUE(expected.contains(e)); }); @@ -157,7 +157,7 @@ TEST(ECSTest, ComponentDynamicTest) RuntimeFilter() .Include()); expected = { entity2, entity3 }; - ASSERT_EQ(view3.Size(), 2); + ASSERT_EQ(view3.Count(), 2); view3.Each([&](Entity e, const CompB& compB) -> void { ASSERT_TRUE(expected.contains(e)); }); @@ -167,7 +167,7 @@ TEST(ECSTest, ComponentDynamicTest) .Include() .Exclude()); expected = { entity3 }; - ASSERT_EQ(view4.Size(), 1); + ASSERT_EQ(view4.Count(), 1); view4.Each([&](Entity e, const CompB& compB) -> void { ASSERT_TRUE(expected.contains(e)); }); @@ -177,7 +177,7 @@ TEST(ECSTest, ComponentDynamicTest) .Include() .Include()); expected = { entity2 }; - ASSERT_EQ(view5.Size(), 1); + ASSERT_EQ(view5.Count(), 1); view5.Each([&](Entity e, const CompA& compA, const CompB& compB) -> void { ASSERT_TRUE(expected.contains(e)); }); @@ -236,9 +236,9 @@ TEST(ECSTest, ComponentEventStaticTest) { EventCounts count; ECRegistry registry; - registry.Events().onConstructed.BindLambda([&](ECRegistry&, Entity) -> void { count.onConstructed++; }); - registry.Events().onUpdated.BindLambda([&](ECRegistry&, Entity) -> void { count.onUpdated++; }); - registry.Events().onRemove.BindLambda([&](ECRegistry&, Entity) -> void { count.onRemove++; }); + const auto constructedCallback = registry.Events().onConstructed.BindLambda([&](ECRegistry&, Entity) -> void { count.onConstructed++; }); + const auto updatedCallback = registry.Events().onUpdated.BindLambda([&](ECRegistry&, Entity) -> void { count.onUpdated++; }); + const auto removeCallback = registry.Events().onRemove.BindLambda([&](ECRegistry&, Entity) -> void { count.onRemove++; }); const auto entity0 = registry.Create(); const auto entity1 = registry.Create(); @@ -264,6 +264,10 @@ TEST(ECSTest, ComponentEventStaticTest) ASSERT_EQ(count, EventCounts(2, 3, 1)); registry.Remove(entity1); ASSERT_EQ(count, EventCounts(2, 3, 2)); + + registry.Events().onConstructed.Unbind(constructedCallback); + registry.Events().onUpdated.Unbind(updatedCallback); + registry.Events().onRemove.Unbind(removeCallback); } TEST(ECSTest, ComponentEventDynamicTest) @@ -271,9 +275,9 @@ TEST(ECSTest, ComponentEventDynamicTest) CompClass compAClass = &Mirror::Class::Get(); EventCounts count; ECRegistry registry; - registry.EventsDyn(compAClass).onConstructed.BindLambda([&](ECRegistry&, Entity) -> void { count.onConstructed++; }); - registry.EventsDyn(compAClass).onUpdated.BindLambda([&](ECRegistry&, Entity) -> void { count.onUpdated++; }); - registry.EventsDyn(compAClass).onRemove.BindLambda([&](ECRegistry&, Entity) -> void { count.onRemove++; }); + const auto constructedCallback = registry.EventsDyn(compAClass).onConstructed.BindLambda([&](ECRegistry&, Entity) -> void { count.onConstructed++; }); + const auto updatedCallback = registry.EventsDyn(compAClass).onUpdated.BindLambda([&](ECRegistry&, Entity) -> void { count.onUpdated++; }); + const auto removeCallback = registry.EventsDyn(compAClass).onRemove.BindLambda([&](ECRegistry&, Entity) -> void { count.onRemove++; }); const auto entity0 = registry.Create(); const auto entity1 = registry.Create(); @@ -299,15 +303,19 @@ TEST(ECSTest, ComponentEventDynamicTest) ASSERT_EQ(count, EventCounts(2, 3, 1)); registry.RemoveDyn(compAClass, entity1); ASSERT_EQ(count, EventCounts(2, 3, 2)); + + registry.EventsDyn(compAClass).onConstructed.Unbind(constructedCallback); + registry.EventsDyn(compAClass).onUpdated.Unbind(updatedCallback); + registry.EventsDyn(compAClass).onRemove.Unbind(removeCallback); } TEST(ECSTest, GlobalComponentEventStaticTest) { EventCounts count; ECRegistry registry; - registry.GEvents().onConstructed.BindLambda([&](ECRegistry&) -> void { count.onConstructed++; }); - registry.GEvents().onUpdated.BindLambda([&](ECRegistry&) -> void { count.onUpdated++; }); - registry.GEvents().onRemove.BindLambda([&](ECRegistry&) -> void { count.onRemove++; }); + const auto constructedCallback = registry.GEvents().onConstructed.BindLambda([&](ECRegistry&) -> void { count.onConstructed++; }); + const auto updatedCallback = registry.GEvents().onUpdated.BindLambda([&](ECRegistry&) -> void { count.onUpdated++; }); + const auto removeCallback = registry.GEvents().onRemove.BindLambda([&](ECRegistry&) -> void { count.onRemove++; }); registry.GEmplace(1); ASSERT_EQ(count, EventCounts(1, 0, 0)); @@ -327,6 +335,10 @@ TEST(ECSTest, GlobalComponentEventStaticTest) registry.GRemove(); ASSERT_EQ(count, EventCounts(1, 3, 1)); + + registry.GEvents().onConstructed.Unbind(constructedCallback); + registry.GEvents().onUpdated.Unbind(updatedCallback); + registry.GEvents().onRemove.Unbind(removeCallback); } TEST(ECSTest, GlobalComponentEventDynamicTest) @@ -334,9 +346,9 @@ TEST(ECSTest, GlobalComponentEventDynamicTest) GCompClass gCompAClass = &Mirror::Class::Get(); EventCounts count; ECRegistry registry; - registry.GEventsDyn(gCompAClass).onConstructed.BindLambda([&](ECRegistry&) -> void { count.onConstructed++; }); - registry.GEventsDyn(gCompAClass).onUpdated.BindLambda([&](ECRegistry&) -> void { count.onUpdated++; }); - registry.GEventsDyn(gCompAClass).onRemove.BindLambda([&](ECRegistry&) -> void { count.onRemove++; }); + const auto constructedCallback = registry.GEventsDyn(gCompAClass).onConstructed.BindLambda([&](ECRegistry&) -> void { count.onConstructed++; }); + const auto updatedCallback = registry.GEventsDyn(gCompAClass).onUpdated.BindLambda([&](ECRegistry&) -> void { count.onUpdated++; }); + const auto removeCallback = registry.GEventsDyn(gCompAClass).onRemove.BindLambda([&](ECRegistry&) -> void { count.onRemove++; }); registry.GEmplaceDyn(gCompAClass, Mirror::ForwardAsArgList(1)); ASSERT_EQ(count, EventCounts(1, 0, 0)); @@ -356,6 +368,10 @@ TEST(ECSTest, GlobalComponentEventDynamicTest) registry.GRemove(); ASSERT_EQ(count, EventCounts(1, 3, 1)); + + registry.GEventsDyn(gCompAClass).onConstructed.Unbind(constructedCallback); + registry.GEventsDyn(gCompAClass).onUpdated.Unbind(updatedCallback); + registry.GEventsDyn(gCompAClass).onRemove.Unbind(removeCallback); } TEST(ECSTest, ObserverTest) @@ -367,10 +383,10 @@ TEST(ECSTest, ObserverTest) .ObUpdatedDyn(&Mirror::Class::Get()); const auto entity0 = registry.Create(); - ASSERT_EQ(observer.Size(), 0); + ASSERT_EQ(observer.Count(), 0); registry.Emplace(entity0, 1); - ASSERT_EQ(observer.Size(), 1); + ASSERT_EQ(observer.Count(), 1); std::unordered_set expected = { entity0 }; observer.Each([&](Entity e) -> void { ASSERT_TRUE(expected.contains(e)); @@ -378,11 +394,11 @@ TEST(ECSTest, ObserverTest) const auto entity1 = registry.Create(); registry.Emplace(entity1, 2.0f); - ASSERT_EQ(observer.Size(), 1); + ASSERT_EQ(observer.Count(), 1); registry.Update(entity1, [](CompB& compB) -> void { compB.value = 3.0f; }); - ASSERT_EQ(observer.Size(), 2); + ASSERT_EQ(observer.Count(), 2); expected = { entity0, entity1 }; for (const auto e : expected) { ASSERT_TRUE(expected.contains(e)); @@ -401,3 +417,33 @@ TEST(ECSTest, ECRegistryCopyTest) ASSERT_EQ(registry1.Get(entity0).value, 1); ASSERT_EQ(registry1.Get(entity1).value, 2.0f); } + +TEST(ECSTest, ECSRegistrySaveLoadTest) +{ + ECArchive archive; + { + ECRegistry registry; + const auto entity0 = registry.Create(); + const auto entity1 = registry.Create(); + (void) registry.Create(); + registry.Emplace(entity0, 1); + registry.Emplace(entity1, 2.0f); + registry.GEmplace(1); + registry.GEmplace(2.0f); + + registry.Save(archive); + } + + { + ECRegistry registry; + registry.Load(archive); + + ASSERT_EQ(registry.Count(), 3); + ASSERT_EQ(registry.Get(1u).value, 1); + ASSERT_EQ(registry.Get(2u).value, 2.0f); + ASSERT_EQ(registry.CompCount(3u), 0); + ASSERT_EQ(registry.GGet().value, 1); + ASSERT_EQ(registry.GGet().value, 2.0f); + ASSERT_EQ(registry.GCompCount(), 2); + } +} diff --git a/Engine/Source/Runtime/Test/ECSTest.h b/Engine/Source/Runtime/Test/ECSTest.h index 94dcb58be..f091a78f9 100644 --- a/Engine/Source/Runtime/Test/ECSTest.h +++ b/Engine/Source/Runtime/Test/ECSTest.h @@ -11,45 +11,65 @@ using namespace Runtime; struct EClass() CompA { EClassBody(CompA) + CompA() + : value(0) + { + } + explicit CompA(int inValue) : value(inValue) { } - int value; + EProperty() int value; }; struct EClass() CompB { EClassBody(CompB) + CompB() + : value(0.0f) + { + } + explicit CompB(float inValue) : value(inValue) { } - float value; + EProperty() float value; }; -struct EClass() GCompA { +struct EClass(global) GCompA { EClassBody(GCompA) + GCompA() + : value(0) + { + } + explicit GCompA(int inValue) : value(inValue) { } - int value; + EProperty() int value; }; -struct EClass() GCompB { +struct EClass(global) GCompB { EClassBody(GCompB) + GCompB() + : value(0.0f) + { + } + explicit GCompB(float inValue) : value(inValue) { } - float value; + EProperty() float value; }; struct EventCounts { diff --git a/Engine/Source/Runtime/Test/WorldTest.cpp b/Engine/Source/Runtime/Test/WorldTest.cpp index 5cd72a04e..5431394f8 100644 --- a/Engine/Source/Runtime/Test/WorldTest.cpp +++ b/Engine/Source/Runtime/Test/WorldTest.cpp @@ -4,9 +4,7 @@ #include #include -#include #include -#include using namespace Runtime; struct WorldTest : testing::Test { @@ -63,8 +61,8 @@ bool Velocity::operator==(const Velocity& inRhs) const && Common::CompareNumber(y, inRhs.y); } -BasicTest_MotionSystem::BasicTest_MotionSystem(ECRegistry& inRegistry) - : System(inRegistry) +BasicTest_MotionSystem::BasicTest_MotionSystem(ECRegistry& inRegistry, const Runtime::SystemSetupContext& inContext) + : System(inRegistry, inContext) { auto entity0 = registry.Create(); registry.Emplace(entity0, 1.0f, 2.0f); @@ -74,7 +72,7 @@ BasicTest_MotionSystem::BasicTest_MotionSystem(ECRegistry& inRegistry) registry.Emplace(entity1, 0.0f, 1.0f); registry.Emplace(entity1, 1.0f, 0.0f); - auto& [entities] = registry.GEmplace(); + auto& [entities] = registry.GEmplace(); entities = { { entity0, Position(1.5f, 3.0f) }, { entity1, Position(1.0f, 1.0f) } @@ -85,7 +83,7 @@ BasicTest_MotionSystem::~BasicTest_MotionSystem() = default; void BasicTest_MotionSystem::Tick(float inDeltaTimeSeconds) { - auto& [entities] = registry.GGet(); + auto& [entities] = registry.GGet(); const auto view = registry.View(); view.Each([&](Entity entity, Position& position, Velocity& velocity) -> void { @@ -97,7 +95,7 @@ void BasicTest_MotionSystem::Tick(float inDeltaTimeSeconds) expectPos.x = position.x + velocity.x; expectPos.y = position.y + velocity.y; }); - registry.GNotifyUpdated(); + registry.GNotifyUpdated(); } TEST_F(WorldTest, BasicTest) @@ -106,17 +104,17 @@ TEST_F(WorldTest, BasicTest) auto& mainGroup = systemGraph.AddGroup("MainGroup", SystemExecuteStrategy::sequential); mainGroup.EmplaceSystemDyn(&BasicTest_MotionSystem::GetStaticClass()); - const auto world = engine->CreateWorld(); - world->SetSystemGraph(systemGraph); - world->Play(); + World world("TestWorld", nullptr); + world.SetSystemGraph(systemGraph); + world.Play(); for (auto i = 0; i < 5; i++) { engine->Tick(0.0167f); } - world->Stop(); + world.Stop(); } -ConcurrentTest_SystemA::ConcurrentTest_SystemA(Runtime::ECRegistry& inRegistry) - : System(inRegistry) +ConcurrentTest_SystemA::ConcurrentTest_SystemA(Runtime::ECRegistry& inRegistry, const Runtime::SystemSetupContext& inContext) + : System(inRegistry, inContext) { } @@ -124,12 +122,12 @@ ConcurrentTest_SystemA::~ConcurrentTest_SystemA() = default; void ConcurrentTest_SystemA::Tick(float inDeltaTimeSeconds) { - auto& context = registry.GGet(); + auto& context = registry.GGet(); context.a = 1; } -ConcurrentTest_SystemB::ConcurrentTest_SystemB(Runtime::ECRegistry& inRegistry) - : System(inRegistry) +ConcurrentTest_SystemB::ConcurrentTest_SystemB(Runtime::ECRegistry& inRegistry, const Runtime::SystemSetupContext& inContext) + : System(inRegistry, inContext) { } @@ -137,14 +135,14 @@ ConcurrentTest_SystemB::~ConcurrentTest_SystemB() = default; void ConcurrentTest_SystemB::Tick(float inDeltaTimeSeconds) { - auto& context = registry.GGet(); + auto& context = registry.GGet(); context.b = 2; } -ConcurrentTest_VerifySystem::ConcurrentTest_VerifySystem(Runtime::ECRegistry& inRegistry) - : System(inRegistry) +ConcurrentTest_VerifySystem::ConcurrentTest_VerifySystem(Runtime::ECRegistry& inRegistry, const Runtime::SystemSetupContext& inContext) + : System(inRegistry, inContext) { - auto& context = registry.GEmplace(); + auto& context = registry.GEmplace(); context.a = 0; context.b = 0; context.sum = 0; @@ -155,7 +153,7 @@ ConcurrentTest_VerifySystem::~ConcurrentTest_VerifySystem() = default; void ConcurrentTest_VerifySystem::Tick(float inDeltaTimeSeconds) { - auto& context = registry.GGet(); + auto& context = registry.GGet(); context.sum = context.sum + context.a + context.b; context.a = 0; context.b = 0; @@ -172,11 +170,11 @@ TEST_F(WorldTest, ConcurrentTest) auto& verifyGroup = systemGraph.AddGroup("VerifyGroup", SystemExecuteStrategy::sequential); verifyGroup.EmplaceSystem(); - const auto world = engine->CreateWorld(); - world->SetSystemGraph(systemGraph); - world->Play(); + World world("TestWorld", nullptr); + world.SetSystemGraph(systemGraph); + world.Play(); for (auto i = 0; i < 5; i++) { engine->Tick(0.0167f); } - world->Stop(); + world.Stop(); } diff --git a/Engine/Source/Runtime/Test/WorldTest.h b/Engine/Source/Runtime/Test/WorldTest.h index c31e82df1..104c78a98 100644 --- a/Engine/Source/Runtime/Test/WorldTest.h +++ b/Engine/Source/Runtime/Test/WorldTest.h @@ -34,23 +34,23 @@ struct EClass() Velocity { float y; }; -struct EClass() BasicTest_ExpectVerifyResult { - EClassBody(BasicTest_ExpectVerifyResult) +struct EClass(global) GBasicTest_ExpectVerifyResult { + EClassBody(GBasicTest_ExpectVerifyResult) std::unordered_map entities; }; class EClass() BasicTest_MotionSystem : public Runtime::System { - EClassBody(BasicTest_MotionSystem) + EPolyClassBody(BasicTest_MotionSystem) - explicit BasicTest_MotionSystem(Runtime::ECRegistry& inRegistry); + explicit BasicTest_MotionSystem(Runtime::ECRegistry& inRegistry, const Runtime::SystemSetupContext& inContext); ~BasicTest_MotionSystem() override; void Tick(float inDeltaTimeSeconds) override; }; -struct EClass() ConcurrentTest_Context { - EClassBody(ConcurrentTest_Context) +struct EClass(global) GConcurrentTest_Context { + EClassBody(GConcurrentTest_Context) uint32_t a; uint32_t b; @@ -59,27 +59,27 @@ struct EClass() ConcurrentTest_Context { }; struct EClass() ConcurrentTest_SystemA : public Runtime::System { - EClassBody(ConcurrentTest_SystemA) + EPolyClassBody(ConcurrentTest_SystemA) - explicit ConcurrentTest_SystemA(Runtime::ECRegistry& inRegistry); + explicit ConcurrentTest_SystemA(Runtime::ECRegistry& inRegistry, const Runtime::SystemSetupContext& inContext); ~ConcurrentTest_SystemA() override; void Tick(float inDeltaTimeSeconds) override; }; struct EClass() ConcurrentTest_SystemB : public Runtime::System { - EClassBody(ConcurrentTest_SystemB) + EPolyClassBody(ConcurrentTest_SystemB) - explicit ConcurrentTest_SystemB(Runtime::ECRegistry& inRegistry); + explicit ConcurrentTest_SystemB(Runtime::ECRegistry& inRegistry, const Runtime::SystemSetupContext& inContext); ~ConcurrentTest_SystemB() override; void Tick(float inDeltaTimeSeconds) override; }; struct EClass() ConcurrentTest_VerifySystem : public Runtime::System { - EClassBody(ConcurrentTest_VerifySystem) + EPolyClassBody(ConcurrentTest_VerifySystem) - explicit ConcurrentTest_VerifySystem(Runtime::ECRegistry& inRegistry); + explicit ConcurrentTest_VerifySystem(Runtime::ECRegistry& inRegistry, const Runtime::SystemSetupContext& inContext); ~ConcurrentTest_VerifySystem() override; void Tick(float inDeltaTimeSeconds) override; diff --git a/Sample/Rendering-Triangle/Triangle.cpp b/Sample/Rendering-Triangle/Triangle.cpp index e970a73cc..8639777f7 100644 --- a/Sample/Rendering-Triangle/Triangle.cpp +++ b/Sample/Rendering-Triangle/Triangle.cpp @@ -21,7 +21,7 @@ class TriangleVS : public GlobalShader { public: ShaderInfo( "TriangleVS", - "../Test/Sample/Rendering-Triangle/Triangle.esl", + "Engine/Test/Sample/Rendering-Triangle/Triangle.esl", "VSMain", RHI::ShaderStageBits::sVertex); @@ -33,7 +33,7 @@ class TrianglePS : public GlobalShader { public: ShaderInfo( "TrianglePS", - "../Test/Sample/Rendering-Triangle/Triangle.esl", + "Engine/Test/Sample/Rendering-Triangle/Triangle.esl", "PSMain", RHI::ShaderStageBits::sPixel);