diff --git a/src/engine/editor/Editor.cpp b/src/engine/editor/Editor.cpp index f158a8edd..9f35f6780 100644 --- a/src/engine/editor/Editor.cpp +++ b/src/engine/editor/Editor.cpp @@ -150,6 +150,8 @@ namespace Editor { eObjectPicker.eGizmo.dimensions.MinZ = minZ + -1000; eObjectPicker.eGizmo.dimensions.MaxZ = maxZ + 1000; } + + // This is more of a reset void Editor::NewTrack() { auto course = gWorldInstance.CurrentCourse = new Course(); course->Props.New(); diff --git a/src/engine/editor/SaveLevel.cpp b/src/engine/editor/SaveLevel.cpp deleted file mode 100644 index 93b4b596b..000000000 --- a/src/engine/editor/SaveLevel.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "SaveLevel.h" -#include "port/Game.h" -#include "CoreMath.h" -#include "World.h" -#include "GameObject.h" - -#include -#include -#include - -namespace Editor { - - // SaveLevel::SaveLevel() {} - - void SaveLevel() { - auto props = gWorldInstance.CurrentCourse->Props; - - nlohmann::json data; - - data["Props"] = props.to_json(); - - nlohmann::json actors; - - /* for (const auto& actor : gWorldInstance.StaticMeshActors) { - actors.push_back(actor->to_json()); - } - - data["Actors"] = actors;*/ - - std::ofstream file("track.json"); - file << data.dump(4); // Pretty print with indent - } - - void LoadLevel() { - // Open the JSON file - std::ifstream file("track.json"); - if (!file.is_open()) { - std::cerr << "Failed to open track.json for reading!" << std::endl; - return; - } - - // Check if level data file is empty - if (file.peek() == std::ifstream::traits_type::eof()) { - return; - } - - // Parse the JSON file into a nlohmann::json object - nlohmann::json data; - file >> data; - - - // Load the Props (deserialize it) - if (data.contains("Props")) { - auto& propsJson = data["Props"]; - gWorldInstance.CurrentCourse->Props.from_json(propsJson); // Assuming you have a `from_json` function - } else { - std::cerr << "Props data not found in the JSON file!" << std::endl; - } - - // Load the Actors (deserialize them) - if (data.contains("Actors")) { - auto& actorsJson = data["Actors"]; - gWorldInstance.StaticMeshActors.clear(); // Clear existing actors, if any - - for (const auto& actorJson : actorsJson) { - Load_AddStaticMeshActor(actorJson); - } - } else { - std::cerr << "Actors data not found in the JSON file!" << std::endl; - } - // Close the file after loading - file.close(); - } - - void Load_AddStaticMeshActor(const nlohmann::json& actorJson) { - gWorldInstance.StaticMeshActors.push_back(new StaticMeshActor("", FVector(0, 0, 0), IRotator(0, 0, 0), FVector(1, 1, 1), "", nullptr)); - auto actor = gWorldInstance.StaticMeshActors.back(); - actor->from_json(actorJson); - -printf("After from_json: Pos(%f, %f, %f), Name: %s, Model: %s\n", - actor->Pos.x, actor->Pos.y, actor->Pos.z, actor->Name.c_str(), actor->Model.c_str()); - gEditor.AddObject(actor->Name.c_str(), &actor->Pos, (Vec3s*)&actor->Rot, &actor->Scale, (Gfx*) nullptr, 1.0f, - GameObject::CollisionType::BOUNDING_BOX, 20.0f, (int32_t*) &actor->bPendingDestroy, (int32_t) 1); - } -} diff --git a/src/engine/editor/SaveLevel.h b/src/engine/editor/SaveLevel.h deleted file mode 100644 index 682df0910..000000000 --- a/src/engine/editor/SaveLevel.h +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include "Course.h" - -namespace Editor { - void SaveLevel(); - void LoadLevel(); - void Load_AddStaticMeshActor(const nlohmann::json& actorJson); -} diff --git a/src/engine/editor/SceneManager.cpp b/src/engine/editor/SceneManager.cpp new file mode 100644 index 000000000..65c199808 --- /dev/null +++ b/src/engine/editor/SceneManager.cpp @@ -0,0 +1,108 @@ +#include "SceneManager.h" +#include "port/Game.h" +#include "CoreMath.h" +#include "World.h" +#include "GameObject.h" + +#include +#include +#include +#include "port/Engine.h" +#include +#include + +namespace Editor { + + std::shared_ptr CurrentArchive; + std::string SceneFile = ""; + + void SaveLevel() { + auto props = gWorldInstance.CurrentCourse->Props; + + if ((CurrentArchive) && (!SceneFile.empty())) { + nlohmann::json data; + + data["Props"] = props.to_json(); + + nlohmann::json actors; + + // for (const auto& actor : gWorldInstance.StaticMeshActors) { + // actors.push_back(actor->to_json()); + // } + // data["Actors"] = actors; + + auto dat = data.dump(); + std::vector stringify; + stringify.assign(dat.begin(), dat.end()); + + bool wrote = GameEngine::Instance->context->GetResourceManager()->GetArchiveManager()->WriteFile(CurrentArchive, SceneFile, stringify); + if (wrote) { + printf("Successfully wrote scene file!\n"); + } else { + printf("Failed to write scene file!\n"); + } + } else { + printf("Could not save scene file, SceneFile or CurrentArchive not set\n"); + } + } + + void LoadLevel(std::string sceneFile) { + printf("LOAD TRACK PROPS %s\n", sceneFile.c_str()); + + nlohmann::json data; + + CurrentArchive = GameEngine::Instance->context->GetResourceManager()->GetArchiveManager()->GetArchiveFromFile(sceneFile); + SceneFile = sceneFile; + + if (CurrentArchive) { + auto initData = std::make_shared(); + initData->Parent = CurrentArchive; + initData->Format = RESOURCE_FORMAT_BINARY; + initData->ByteOrder = Ship::Endianness::Little; + initData->Type = static_cast(Ship::ResourceType::Json); + initData->ResourceVersion = 0; + + nlohmann::json data = std::static_pointer_cast( + GameEngine::Instance->context->GetResourceManager()->LoadResource(sceneFile, true, initData))->Data; + + if (data == nullptr) { + return; + } + + // Load the Props (deserialize it) + if (data.contains("Props")) { + auto& propsJson = data["Props"]; + gWorldInstance.CurrentCourse->Props.from_json(propsJson); // Assuming you have a `from_json` function + } else { + std::cerr << "Props data not found in the JSON file!" << std::endl; + } + + // Load the Actors (deserialize them) + if (data.contains("Actors")) { + auto& actorsJson = data["Actors"]; + gWorldInstance.StaticMeshActors.clear(); // Clear existing actors, if any + + for (const auto& actorJson : actorsJson) { + Load_AddStaticMeshActor(actorJson); + } + } else { + std::cerr << "Actors data not found in the JSON file!" << std::endl; + } + } + } + + void Load_AddStaticMeshActor(const nlohmann::json& actorJson) { + gWorldInstance.StaticMeshActors.push_back(new StaticMeshActor("", FVector(0, 0, 0), IRotator(0, 0, 0), FVector(1, 1, 1), "", nullptr)); + auto actor = gWorldInstance.StaticMeshActors.back(); + actor->from_json(actorJson); + +printf("After from_json: Pos(%f, %f, %f), Name: %s, Model: %s\n", + actor->Pos.x, actor->Pos.y, actor->Pos.z, actor->Name.c_str(), actor->Model.c_str()); + gEditor.AddObject(actor->Name.c_str(), &actor->Pos, (Vec3s*)&actor->Rot, &actor->Scale, (Gfx*) nullptr, 1.0f, + GameObject::CollisionType::BOUNDING_BOX, 20.0f, (int32_t*) &actor->bPendingDestroy, (int32_t) 1); + } + + void SetSceneFile(std::string sceneFile) { + SceneFile = sceneFile; + } +} diff --git a/src/engine/editor/SceneManager.h b/src/engine/editor/SceneManager.h new file mode 100644 index 000000000..d2915888c --- /dev/null +++ b/src/engine/editor/SceneManager.h @@ -0,0 +1,12 @@ +#include +#include "Course.h" + +namespace Editor { + void SaveLevel(); + void LoadLevel(std::string sceneFile); + void Load_AddStaticMeshActor(const nlohmann::json& actorJson); + void SetSceneFile(std::string sceneFile); + + extern std::shared_ptr CurrentArchive; // This is used to retrieve and write the scene data file + extern std::string SceneFile; +} diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index b2d38b184..dd6f6e3f4 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -77,7 +77,7 @@ GameEngine::GameEngine() { if (std::filesystem::is_directory(patches_path)) { for (const auto& p : std::filesystem::recursive_directory_iterator(patches_path)) { auto ext = p.path().extension().string(); - if (StringHelper::IEquals(ext, ".otr") || StringHelper::IEquals(ext, ".o2r")) { + if (StringHelper::IEquals(ext, ".zip") || StringHelper::IEquals(ext, ".o2r")) { archiveFiles.push_back(p.path().generic_string()); } } diff --git a/src/port/Game.cpp b/src/port/Game.cpp index cd5800092..29327c704 100644 --- a/src/port/Game.cpp +++ b/src/port/Game.cpp @@ -47,7 +47,7 @@ #include "engine/editor/Editor.h" #include "engine/editor/EditorMath.h" -#include "engine/editor/SaveLevel.h" +#include "engine/editor/SceneManager.h" extern "C" { #include "main.h" @@ -212,8 +212,12 @@ void HM_DrawIntro() { gMenuIntro.HM_DrawIntro(); } -void CM_LoadLevelProps() { - Editor::LoadLevel(); +void CM_SpawnFromLevelProps() { + // Spawning actors needs to be delayed to the correct time. + // And loadlevel needs to happen asap + + //Editor::LoadLevel(nullptr); + // Editor::SpawnFromLevelProps(); } World* GetWorld(void) { diff --git a/src/port/Game.h b/src/port/Game.h index f0887d415..25b4398dc 100644 --- a/src/port/Game.h +++ b/src/port/Game.h @@ -26,7 +26,7 @@ void HM_InitIntro(void); void HM_TickIntro(void); void HM_DrawIntro(void); -void CM_LoadLevelProps(); +void CM_SpawnFromLevelProps(); void CM_DisplayBattleBombKart(s32 playerId, s32 primAlpha); void CM_DrawBattleBombKarts(s32 cameraId); diff --git a/src/port/ui/ContentBrowser.cpp b/src/port/ui/ContentBrowser.cpp index b2d0dee1f..554c3b120 100644 --- a/src/port/ui/ContentBrowser.cpp +++ b/src/port/ui/ContentBrowser.cpp @@ -16,6 +16,8 @@ #include "CoreMath.h" #include "World.h" #include "AllActors.h" +#include "port/Game.h" +#include "src/engine/editor/SceneManager.h" namespace Editor { @@ -24,51 +26,36 @@ namespace Editor { } void ContentBrowserWindow::DrawElement() { - static bool refresh = true; static bool actorContent = false; static bool objectContent = false; static bool customContent = false; + static bool trackContent = false; + - // List the available mods/o2r files - // for (size_t i = 0; i < GameEngine::Instance->archiveFiles.size(); i++) { - // auto str = GameEngine::Instance->archiveFiles[i]; - // if (str.starts_with("./mods/")) { - // ImGui::Text(GameEngine::Instance->archiveFiles[i].c_str()); - // } - // } if (ImGui::Button(ICON_FA_REFRESH)) { - refresh = true; + Refresh = true; } // Query content in o2r and add them to Content - if (refresh) { - refresh = false; + if (Refresh) { + Refresh = false; + Tracks.clear(); Content.clear(); + TrackAssetMap.clear(); + TrackPath.clear(); + FindTracks(); FindContent(); return; } + ContentBrowserWindow::FolderButton("Tracks", trackContent); + ContentBrowserWindow::FolderButton("Actors", actorContent); + ContentBrowserWindow::FolderButton("Objects", objectContent); + ContentBrowserWindow::FolderButton("Custom", customContent); - std::string actorText = fmt::format("{0} {1}", actorContent ? ICON_FA_FOLDER_OPEN_O : ICON_FA_FOLDER_O, "Actors"); - if (ImGui::Button(actorText.c_str(), ImVec2(80, 32))) { - actorContent = !actorContent; - objectContent = false; - customContent = false; - } - - std::string objectText = fmt::format("{0} {1}", objectContent ? ICON_FA_FOLDER_OPEN_O : ICON_FA_FOLDER_O, "Objects"); - if (ImGui::Button(objectText.c_str(), ImVec2(80, 32))) { - objectContent = !objectContent; - actorContent = false; - customContent = false; - } - - std::string customText = fmt::format("{0} {1}", customContent ? ICON_FA_FOLDER_OPEN_O : ICON_FA_FOLDER_O, "Custom"); - if (ImGui::Button(customText.c_str(), ImVec2(80, 32))) { - customContent = !customContent; - actorContent = false; - objectContent = false; + if (trackContent) { + AddTrackContent(); } if (actorContent) { @@ -84,6 +71,13 @@ namespace Editor { } } + void ContentBrowserWindow::FolderButton(const char* label, bool& contentFlag, const ImVec2& size) { + std::string buttonText = fmt::format("{0} {1}", contentFlag ? ICON_FA_FOLDER_OPEN_O : ICON_FA_FOLDER_O, label); + if (ImGui::Button(buttonText.c_str(), size)) { + contentFlag = !contentFlag; + } + } + std::unordered_map> ActorList = { { "Mario Sign", [](const FVector& pos) { return new AMarioSign(pos); } }, { "Wario Sign", [](const FVector& pos) { return new AWarioSign(pos); } }, @@ -125,6 +119,39 @@ namespace Editor { { "Podium", [](const FVector& pos) { return new OPodium(pos); } }, }; + void ContentBrowserWindow::AddTrackContent() { + size_t i_track = 0; + for (const auto& track : Tracks) { + std::string label = fmt::format("{}##{}", track, i_track); + bool foundTrackFile = false; + + for (auto& asset : TrackAssetMap[track]) { + if (asset.ends_with("track.json")) { + foundTrackFile = true; + if (ImGui::Button(label.c_str())) { + + SetCourseByClass(new Course()); + LoadLevel(asset); + + gGamestateNext = RACING; + break; + } + } + } + + if (!foundTrackFile) { + std::string label = fmt::format("{} {}", "ADD PATH AND MESH TO TRACK PROPS AND CLICK HERE TO INIT TRACK:", track); + if (ImGui::Button(label.c_str())) { + SetSceneFile(TrackPath[track]); + SaveLevel(); + Refresh = true; + } + } + + i_track += 1; + } + } + void ContentBrowserWindow::AddActorContent() { FVector pos = GetPositionAheadOfCamera(300.0f); @@ -183,12 +210,47 @@ namespace Editor { } } + // Finds modded archives only. For discovering tracks + void ContentBrowserWindow::FindTracks() { + + // ListFiles(whitelist, blacklist); + auto ptr2 = GameEngine::Instance->context->GetResourceManager()->GetArchiveManager()->ListFiles({"*/tracks/*"}, {""}); + if (ptr2) { + auto files = *ptr2; + std::set uniqueTracks; + + for (const auto& file : files) { + //printf("TRACK FILES: %s\n", file.c_str()); + + // Find "tracks/" + size_t pos = file.find("tracks/"); + if (pos == std::string::npos) continue; // Skip if not found + + // Extract track name + size_t start = pos + 7; // Move past "tracks/" + size_t end = file.find('/', start); // Find next '/' + std::string trackName = file.substr(start, end - start); + + TrackAssetMap[trackName].push_back(file); + + // Insert into set (ensuring uniqueness) + uniqueTracks.insert(trackName); + + // Extract directory path (remove filename) + size_t lastSlash = file.find_last_of('/'); + std::string directoryPath = (lastSlash != std::string::npos) ? file.substr(0, lastSlash) : file; + + // Store in TrackPath + TrackPath[trackName] = directoryPath; + } + + // Move unique tracks into the vector + Tracks.assign(uniqueTracks.begin(), uniqueTracks.end()); + } + } + void ContentBrowserWindow::FindContent() { - std::list myList = {"tracks/*", "actors/*", "objects/*"}; - std::list myList2 = {""}; - - - auto ptr = GameEngine::Instance->context->GetResourceManager()->GetArchiveManager()->ListFiles(myList, myList2); + auto ptr = GameEngine::Instance->context->GetResourceManager()->GetArchiveManager()->ListFiles({"*tracks/*","actors/*", "objects/*"}, {""}); if (ptr) { auto files = *ptr; for (const auto& file : files) { diff --git a/src/port/ui/ContentBrowser.h b/src/port/ui/ContentBrowser.h index c6a15f35e..49a503995 100644 --- a/src/port/ui/ContentBrowser.h +++ b/src/port/ui/ContentBrowser.h @@ -9,13 +9,21 @@ public: ~ContentBrowserWindow(); std::vector Content; + std::vector Tracks; // Contains modded archives in mods/ + std::unordered_map> TrackAssetMap; + std::unordered_map TrackPath; + + bool Refresh = true; protected: void InitElement() override {}; void DrawElement() override; void UpdateElement() override {}; + void AddTrackContent(); void AddActorContent(); void AddObjectContent(); void AddCustomContent(); + void FindTracks(); void FindContent(); + void FolderButton(const char* label, bool& contentFlag, const ImVec2& size = ImVec2(80, 32)); }; } diff --git a/src/port/ui/Tools.cpp b/src/port/ui/Tools.cpp index b0b89eb0b..bad43a1a6 100644 --- a/src/port/ui/Tools.cpp +++ b/src/port/ui/Tools.cpp @@ -11,7 +11,7 @@ #include #include #include "port/Game.h" -#include "engine/editor/SaveLevel.h" +#include "engine/editor/SceneManager.h" extern "C" { #include "code_800029B0.h" diff --git a/src/racing/actors.c b/src/racing/actors.c index 1b65bbe69..9344bd3eb 100644 --- a/src/racing/actors.c +++ b/src/racing/actors.c @@ -1227,8 +1227,8 @@ void init_actors_and_load_textures(void) { init_red_shell_texture(); destroy_all_actors(); CM_CleanWorld(); - CM_LoadLevelProps(); + CM_SpawnFromLevelProps(); CM_BeginPlay(); spawn_course_actors(); }