aboutsummaryrefslogtreecommitdiff
path: root/source/30-game/Ires.cpp
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2022-05-30 17:03:20 -0700
committerrtk0c <[email protected]>2022-05-30 17:03:20 -0700
commite66286ebe30afc9acc4531fc2bea29b7fb924f93 (patch)
treefa6b76554c3eb88bc8f088fbab68e20c40118ca7 /source/30-game/Ires.cpp
parent366ef5a5450c6e0e680c924c3454943a9ae9814d (diff)
Changeset: 56 Buildsystem cleanup: change to layered structure for different targets
Diffstat (limited to 'source/30-game/Ires.cpp')
-rw-r--r--source/30-game/Ires.cpp425
1 files changed, 425 insertions, 0 deletions
diff --git a/source/30-game/Ires.cpp b/source/30-game/Ires.cpp
new file mode 100644
index 0000000..10a6867
--- /dev/null
+++ b/source/30-game/Ires.cpp
@@ -0,0 +1,425 @@
+#include "Ires.hpp"
+
+#include "AppConfig.hpp"
+#include "EditorCore.hpp"
+#include "EditorUtils.hpp"
+#include "Macros.hpp"
+#include "Material.hpp"
+#include "RapidJsonHelper.hpp"
+#include "ScopeGuard.hpp"
+#include "Shader.hpp"
+#include "Sprite.hpp"
+#include "Texture.hpp"
+#include "Utils.hpp"
+
+#include <imgui.h>
+#include <misc/cpp/imgui_stdlib.h>
+#include <rapidjson/document.h>
+#include <rapidjson/filereadstream.h>
+#include <rapidjson/filewritestream.h>
+#include <rapidjson/prettywriter.h>
+#include <rapidjson/writer.h>
+#include <algorithm>
+#include <cassert>
+
+namespace fs = std::filesystem;
+using namespace std::literals;
+
+IresObject::IresObject(Kind kind)
+ : mKind{ kind } {
+}
+
+std::string_view IresObject::ToString(Kind kind) {
+ switch (kind) {
+ case KD_Texture: return BRUSSEL_TAG_PREFIX_Ires "Texture"sv;
+ case KD_Shader: return BRUSSEL_TAG_PREFIX_Ires "Shader"sv;
+ case KD_Material: return BRUSSEL_TAG_PREFIX_Ires "Material"sv;
+ case KD_SpriteFiles: return BRUSSEL_TAG_PREFIX_Ires "SpriteFiles"sv;
+ case KD_Spritesheet: return BRUSSEL_TAG_PREFIX_Ires "Spritesheet"sv;
+ case KD_COUNT: break;
+ }
+ return std::string_view();
+}
+
+IresObject::Kind IresObject::FromString(std::string_view name) {
+ if (name == BRUSSEL_TAG_PREFIX_Ires "Texture"sv) return KD_Texture;
+ if (name == BRUSSEL_TAG_PREFIX_Ires "Shader"sv) return KD_Shader;
+ if (name == BRUSSEL_TAG_PREFIX_Ires "Material"sv) return KD_Material;
+ if (name == BRUSSEL_TAG_PREFIX_Ires "SpriteFiles"sv) return KD_SpriteFiles;
+ if (name == BRUSSEL_TAG_PREFIX_Ires "Spritesheet"sv) return KD_Spritesheet;
+ return KD_COUNT;
+}
+
+std::unique_ptr<IresObject> IresObject::Create(Kind kind) {
+ switch (kind) {
+ case KD_Texture: return std::make_unique<IresTexture>();
+ case KD_Shader: return std::make_unique<IresShader>();
+ case KD_Material: return std::make_unique<IresMaterial>();
+ case KD_SpriteFiles: return std::make_unique<IresSpriteFiles>();
+ case KD_Spritesheet: return std::make_unique<IresSpritesheet>();
+ case KD_COUNT: break;
+ }
+ return nullptr;
+}
+
+bool IresObject::IsAnnoymous() const {
+ return mName.empty();
+}
+
+void IresObject::SetName(std::string name) {
+ if (mMan) {
+ mMan->Rename(this, std::move(name));
+ } else {
+ mName = std::move(name);
+ }
+}
+
+void IresObject::ShowNameSafe(IresObject* ires) {
+ if (ires) {
+ ires->ShowName();
+ } else {
+ ShowNameNull();
+ }
+}
+
+void IresObject::ShowNameNull() {
+ ImGui::Text("<null>");
+}
+
+void IresObject::ShowName() const {
+ if (IsAnnoymous()) {
+ ImGui::Text("<annoymous %p>", (void*)this);
+ } else {
+ ImGui::Text("%s", mName.c_str());
+ }
+}
+
+void IresObject::ShowReferenceSafe(IEditor& editor, IresObject* ires) {
+ if (ires) {
+ ires->ShowReference(editor);
+ } else {
+ ShowReferenceNull(editor);
+ }
+}
+
+void IresObject::ShowReferenceNull(IEditor& editor) {
+ ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
+ ImGui::Text("<null>");
+ ImGui::PopStyleColor();
+ ImGui::AddUnderLine(ImGui::GetStyle().Colors[ImGuiCol_Button]);
+}
+
+void IresObject::ShowReference(IEditor& editor) {
+ ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
+ if (IsAnnoymous()) {
+ ImGui::Text("<annoymous %p>", (void*)this);
+ } else {
+ ImGui::Text("%s", mName.c_str());
+ }
+ ImGui::PopStyleColor();
+ if (ImGui::IsItemHovered()) {
+ if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
+ editor.GetInspector().SelectTarget(IEditorInspector::ITT_Ires, this);
+ }
+ ImGui::AddUnderLine(ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
+ } else {
+ ImGui::AddUnderLine(ImGui::GetStyle().Colors[ImGuiCol_Button]);
+ }
+}
+
+void IresObject::ShowEditor(IEditor& editor) {
+ ImGui::Text("%s", ToString(mKind).data());
+
+ bool isAnnoymous = mName.empty();
+ if (isAnnoymous) {
+ ImGui::Text("Name: <annoymous: %p>", (void*)this);
+ } else {
+ ImGui::Text("Name: %s", mName.c_str());
+ }
+
+ if (mUid.IsNull()) {
+ ImGui::TextUnformatted("Uid: <none>");
+ } else {
+ ImGui::Text("Uid: %lx-%lx", mUid.upper, mUid.lower);
+ }
+}
+
+void IresObject::WriteFull(IresWritingContext& ctx, IresObject* ires, rapidjson::Value& value, rapidjson::Document& root) {
+ rapidjson::Value rvIres(rapidjson::kObjectType);
+ ires->Write(ctx, rvIres, root);
+
+ value.AddMember("Type", rapidjson::StringRef(ToString(ires->GetKind())), root.GetAllocator());
+ value.AddMember("Uid", ires->mUid.Write(root), root.GetAllocator());
+ value.AddMember("Value", rvIres, root.GetAllocator());
+}
+
+std::unique_ptr<IresObject> IresObject::ReadFull(IresLoadingContext& ctx, const rapidjson::Value& value) {
+ auto ires = ReadBasic(value);
+ if (!ires) {
+ return nullptr;
+ }
+
+ if (!ReadPartial(ctx, ires.get(), value)) {
+ return nullptr;
+ }
+
+ return ires;
+}
+
+std::unique_ptr<IresObject> IresObject::ReadBasic(const rapidjson::Value& value) {
+ auto rvType = rapidjson::GetProperty(value, rapidjson::kStringType, "Type"sv);
+ if (!rvType) return nullptr;
+ auto kind = FromString(rapidjson::AsStringView(*rvType));
+ auto ires = Create(kind);
+ if (!ires) return nullptr;
+
+ auto rvUid = rapidjson::GetProperty(value, rapidjson::kArrayType, "Uid"sv);
+ if (!rvUid) return nullptr;
+ ires->mUid.Read(*rvUid);
+
+ return ires;
+}
+
+bool IresObject::ReadPartial(IresLoadingContext& ctx, IresObject* ires, const rapidjson::Value& value) {
+ auto rvValue = rapidjson::GetProperty(value, "Value"sv);
+ if (!rvValue) return false;
+ ires->Read(ctx, *rvValue);
+ return true;
+}
+
+void IresObject::Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const {
+}
+
+void IresObject::Read(IresLoadingContext& ctx, const rapidjson::Value& value) {
+}
+
+void IresManager::DiscoverFilesDesignatedLocation() {
+ auto path = AppConfig::assetDirPath / "Ires";
+ DiscoverFiles(path);
+}
+
+void IresManager::DiscoverFiles(const fs::path& dir) {
+ struct LoadCandidate {
+ fs::path path;
+ rapidjson::Document data;
+ RcPtr<IresObject> ires;
+ };
+
+ class IresLoadTimeContext final : public IresLoadingContext {
+ public:
+ // NOTE: pointer stability required
+ robin_hood::unordered_node_map<Uid, LoadCandidate> candidates;
+ std::vector<std::vector<LoadCandidate*>> candidatesByKind;
+
+ IresLoadTimeContext() {
+ candidatesByKind.resize(IresObject::KD_COUNT);
+ }
+
+ std::vector<LoadCandidate*>& GetByKind(IresObject::Kind kind) {
+ int i = static_cast<int>(kind);
+ return candidatesByKind[i];
+ }
+
+ virtual IresObject* FindIres(const Uid& uid) const override {
+ auto iter = candidates.find(uid);
+ if (iter != candidates.end()) {
+ auto& cand = iter->second;
+ return cand.ires.Get();
+ } else {
+ return nullptr;
+ }
+ }
+ } ctx;
+
+ for (auto& item : fs::directory_iterator(dir)) {
+ if (!item.is_regular_file()) {
+ continue;
+ }
+ if (item.path().extension() != ".json") {
+ continue;
+ }
+
+ auto file = Utils::OpenCstdioFile(item.path(), Utils::Read);
+ if (!file) {
+ fprintf(stderr, "Ires file [" PLATFORM_PATH_STR "] Failed to open file.", item.path().c_str());
+ continue;
+ }
+ DEFER { fclose(file); };
+
+ char readerBuffer[65536];
+ rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer));
+
+ rapidjson::Document root;
+ root.ParseStream(stream);
+
+ auto ires = IresObject::ReadBasic(root);
+ if (!ires) {
+ fprintf(stderr, "Ires file: [" PLATFORM_PATH_STR "] Failed to parse header.", item.path().c_str());
+ continue;
+ }
+
+ // Load name from filename
+ ires->mName = item.path().filename().replace_extension().string();
+ auto& iresName = ires->mName;
+
+ // Load uid should be handled by IresObject::ReadBasic
+ assert(!ires->mUid.IsNull());
+ auto iresUid = ires->GetUid();
+
+ auto iresKind = ires->GetKind();
+
+ auto&& [iter, DISCARD] = ctx.candidates.try_emplace(
+ iresUid,
+ LoadCandidate{
+ .path = item.path(),
+ .data = std::move(root),
+ .ires = RcPtr(ires.release()),
+ });
+ auto& cand = iter->second;
+
+ ctx.GetByKind(iresKind).push_back(&cand);
+ }
+
+ // Load Ires in order by type, there are dependencies between them
+ // TODO full arbitary dependency between Ires
+ for (int i = 0; i < IresObject::KD_COUNT; ++i) {
+ auto kind = static_cast<IresObject::Kind>(i);
+ auto& list = ctx.GetByKind(kind);
+ for (auto cand : list) {
+ auto& ires = cand->ires;
+
+ if (!IresObject::ReadPartial(ctx, ires.Get(), cand->data)) {
+ fprintf(stderr, "Ires file: [" PLATFORM_PATH_STR "] Failed to parse object data.", cand->path.c_str());
+ continue;
+ }
+
+ ires->mMan = this;
+ mObjByUid.try_emplace(ires->GetUid(), ires);
+ }
+ }
+}
+
+std::pair<IresObject*, bool> IresManager::Add(IresObject* ires) {
+ auto& name = ires->mName;
+ if (name.empty()) {
+ name = Utils::MakeRandomNumberedName(IresObject::ToString(ires->GetKind()).data());
+ }
+
+ auto& uid = ires->mUid;
+ if (uid.IsNull()) {
+ uid = Uid::Create();
+ }
+
+ auto [iter, inserted] = mObjByUid.try_emplace(uid, ires);
+ if (inserted) {
+ ires->mMan = this;
+ // TODO handle full path
+ return { ires, true };
+ } else {
+ return { iter->second.Get(), false };
+ }
+}
+
+IresObject* IresManager::Load(const fs::path& filePath) {
+ auto file = Utils::OpenCstdioFile(filePath, Utils::Read);
+ if (!file) return nullptr;
+ DEFER { fclose(file); };
+
+ char readerBuffer[65536];
+ rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer));
+
+ rapidjson::Document root;
+ root.ParseStream(stream);
+
+ auto ires = IresObject::ReadFull(*this, root);
+ if (!ires) {
+ return nullptr;
+ }
+
+ // Load uid should be handled by IresObject::ReadFull
+ assert(!ires->mUid.IsNull());
+ // Load name from filename
+ ires->mName = filePath.filename().replace_extension().string();
+ Add(ires.get());
+
+ return ires.release();
+}
+
+static fs::path GetDesignatedPath(IresObject* ires) {
+ return AppConfig::assetDirPath / "Ires" / fs::path(ires->GetName()).replace_extension(".json");
+}
+
+void IresManager::Delete(IresObject* ires) {
+ // TODO
+}
+
+bool IresManager::Rename(IresObject* ires, std::string newName) {
+ auto oldPath = GetDesignatedPath(ires);
+ ires->mName = std::move(newName);
+ auto newPath = GetDesignatedPath(ires);
+ if (fs::exists(oldPath)) {
+ fs::rename(oldPath, newPath);
+ }
+
+ // TODO validate no name duplication
+#if 0
+ if (mObjByPath.contains(newName)) {
+ return false;
+ }
+
+ // Keep the material from being deleted, in case the old entry in map is the only one existing
+ RcPtr rc(ires);
+
+ // Remove old entry (must do before replacing Material::mName, because the std::string_view in the map is a reference to it)
+ mObjByPath.erase(ires->GetName());
+
+ // Add new entry
+ ires->mName = std::move(newName);
+ // TODO handle full path
+ mObjByPath.try_emplace(ires->GetName(), ires);
+#endif
+ return true;
+}
+
+void IresManager::Reload(IresObject* ires) {
+ auto file = Utils::OpenCstdioFile(GetDesignatedPath(ires), Utils::Read);
+ if (!file) return;
+ DEFER { fclose(file); };
+
+ char readerBuffer[65536];
+ rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer));
+
+ rapidjson::Document root;
+ root.ParseStream(stream);
+
+ IresObject::ReadPartial(*this, ires, root);
+}
+
+void IresManager::Save(IresObject* ires) {
+ Save(ires, GetDesignatedPath(ires));
+}
+
+void IresManager::Save(IresObject* ires, const fs::path& filePath) {
+ rapidjson::Document root(rapidjson::kObjectType);
+
+ IresObject::WriteFull(*this, ires, root, root);
+
+ auto file = Utils::OpenCstdioFile(filePath, Utils::WriteTruncate);
+ if (!file) return;
+ DEFER { fclose(file); };
+
+ char writerBuffer[65536];
+ rapidjson::FileWriteStream stream(file, writerBuffer, sizeof(writerBuffer));
+ rapidjson::PrettyWriter<rapidjson::FileWriteStream> writer(stream);
+ writer.SetFormatOptions(rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
+ root.Accept(writer);
+}
+
+IresObject* IresManager::FindIres(const Uid& uid) const {
+ auto iter = mObjByUid.find(uid);
+ if (iter != mObjByUid.end()) {
+ return iter->second.Get();
+ } else {
+ return nullptr;
+ }
+}