diff --git a/.gitignore b/.gitignore index 95ec471..4ba396e 100644 --- a/.gitignore +++ b/.gitignore @@ -63,4 +63,7 @@ _deps build/ ######################## ASSET IGNORES -ui_files/ \ No newline at end of file +ui_files/ + +######################## TEST FILE IGNORES +test*/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 876e2bd..275e074 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "external/box2d"] path = external/box2d url = https://github.com/erincatto/box2d +[submodule "external/nativefiledialog-extended"] + path = external/nativefiledialog-extended + url = https://github.com/btzy/nativefiledialog-extended.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d55687..0586614 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,9 @@ add_subdirectory(external/freetype) # add box2d add_subdirectory(external/box2d) +# add nativefiledialog +add_subdirectory(external/nativefiledialog-extended) + # add run mode tester add_subdirectory(src/run_modes/testbed) @@ -173,6 +176,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC external/glad/include PUBLIC external/freetype/include PUBLIC external/box2d/include + PUBLIC external/nativefiledialog-extended/src/include ) target_link_directories(${PROJECT_NAME} @@ -183,9 +187,10 @@ target_link_directories(${PROJECT_NAME} PRIVATE external/glad/src PRIVATE external/freetype/src PRIVATE external/box2d/bin + PRIVATE external/nativefiledialog-extended ) -target_link_libraries(${PROJECT_NAME} box2d glfw glad glm dearimgui lua_static freetype testbed) +target_link_libraries(${PROJECT_NAME} box2d glfw glad glm dearimgui lua_static freetype nfd testbed) if (NOT NO_EDITOR) target_link_libraries(${PROJECT_NAME} editor) diff --git a/docs/tasks/Bugs.todo b/docs/tasks/Bugs.todo index 5c3adf0..74461c6 100644 --- a/docs/tasks/Bugs.todo +++ b/docs/tasks/Bugs.todo @@ -1,6 +1,6 @@ High Importance: - ☐ Editor does not get absolute paths from the file browser - replace with NFD dialogs @critical + ✔ Editor does not get absolute paths from the file browser - replace with NFD dialogs @critical @done(22-05-20 18:36) ✔ The Map Editor does not get the tile maps when a project is opened @high @done (3/3/2022, 2:47:41 PM) ✔ Tile Set IDs (as opposed to the Asset ID) is not saved or loaded yet @high @done (3/11/2022, 2:10:30 PM) ✔ Had to flip the V component of the UVs for the sprite vertices. This fixes the partial image drawing but will need to be accounted for in other places in the editor. @done (3/14/2022, 1:46:48 PM) diff --git a/docs/tasks/core.todo b/docs/tasks/core.todo index 6da2e26..b18cf34 100644 --- a/docs/tasks/core.todo +++ b/docs/tasks/core.todo @@ -7,7 +7,8 @@ Build System: ✔ Modify .sh scripts to recognize the noeditor flag @done (1/25/2022, 3:59:23 PM) Core: - Add custom (64 bit?) UUID generator (based on Chreno's UUIDs: ) + ☐ Wrap NFD in an API in the platform module @low + ☐ Add custom (64 bit?) UUID generator (based on Chreno's UUIDs) ✔ Add Terminal subsystem to allow for printing colored text in a cross-platform way @done(22-05-16 18:05) ✔ Create a LogListener that uses the colored text (replace the current stdout listener) @done(22-05-16 18:23) ✔ Replace XML with JSON (https://github.com/nlohmann/json) @high @done(22-05-19 15:35) diff --git a/docs/tasks/editor.todo b/docs/tasks/editor.todo index 12b7e92..5292cac 100644 --- a/docs/tasks/editor.todo +++ b/docs/tasks/editor.todo @@ -1,7 +1,7 @@ Editor: - ☐ Asset Location MUST be relative to the project root directory @critical - ☐ Switch to NFD dialogs + ✔ Asset Location MUST be relative to the project root directory @critical @done(22-05-20 18:35) + ✔ Switch to NFD dialogs @done(22-05-20 18:35) ✔ Come up with project directory structure @done (9/17/2021, 6:46:44 PM) ✔ Make the editor a separate module @high @done (11/1/2021, 2:24:35 PM) ✔ Implement Run Mode interface class @high @done (2/8/2022, 4:05:17 PM) @@ -45,6 +45,7 @@ Editor: Tools: Tile Map Editor: + ☐ Switch to NFD dialogs (or move tile set import to the main editor) @high ☐ Allow Tile Maps to be named Tile Map Canvas: diff --git a/external/nativefiledialog-extended b/external/nativefiledialog-extended new file mode 160000 index 0000000..28ade5a --- /dev/null +++ b/external/nativefiledialog-extended @@ -0,0 +1 @@ +Subproject commit 28ade5a5cc5d17cea8fe4034572cac8fd54eb53f diff --git a/src/run_modes/editor/CMakeLists.txt b/src/run_modes/editor/CMakeLists.txt index 8e66c01..37b3faa 100644 --- a/src/run_modes/editor/CMakeLists.txt +++ b/src/run_modes/editor/CMakeLists.txt @@ -21,12 +21,16 @@ set(EDITOR_SRC add_library(editor ${EDITOR_SRC}) -target_link_libraries(editor) +target_link_directories(editor +PRIVATE ${PROJECT_BINARY_DIR}/external/nativefiledialog-extended) + +target_link_libraries(editor nfd) target_include_directories(editor PUBLIC "${PROJECT_BINARY_DIR}" PUBLIC ../../ PUBLIC ../../run_modes + PUBLIC external/nativefiledialog-extended/src/include PUBLIC ../../../external/glm PUBLIC ../../../external/glad/include PUBLIC ../../../external/glfw/include diff --git a/src/run_modes/editor/contents/content_manager.cpp b/src/run_modes/editor/contents/content_manager.cpp index b971741..8f21f84 100644 --- a/src/run_modes/editor/contents/content_manager.cpp +++ b/src/run_modes/editor/contents/content_manager.cpp @@ -101,7 +101,10 @@ namespace lunarium { namespace editor if (c.is_null()) { - return OpRes::Fail("content_meta.xml missing Contents node"); + return OpRes::OK(); + + // Not actually an error if a project does not have any content yet + //return OpRes::Fail("content_meta.xml missing Contents node"); } // ARRAY EXAMPLE @@ -176,7 +179,7 @@ namespace lunarium { namespace editor return OpRes::Fail("ConentManager::Save failed: no project set"); } - nlohmann::json oj; + nlohmann::ordered_json oj; auto& p = oj["Project"]; // Save header info @@ -204,7 +207,7 @@ namespace lunarium { namespace editor c.push_back(asset); } - std::ofstream ofs = std::ofstream(mContentFile.string().c_str()); + std::ofstream ofs = std::ofstream(mContentFile.string().c_str(), std::ios_base::trunc); if (!ofs.is_open()) { return OpRes::Fail("ContentManager could not save file: %s - could not open file", mContentFile.string().c_str()); @@ -279,6 +282,11 @@ namespace lunarium { namespace editor return OpRes::Fail("Could not import asset, ID collision. ID: %llu", mNextID); } + if (std::filesystem::exists(to_location)) + { + std::filesystem::remove(to_location); + } + if (!std::filesystem::copy_file(file, to_location)) { return OpRes::Fail("Could not copy asset file from: %s to: %s", file.string().c_str(), to_location.string().c_str()); @@ -290,7 +298,7 @@ namespace lunarium { namespace editor pAsset->mAssetDir = mpProject->GetAssetDirectory(); pAsset->mID = mNextID; mNextID++; - pAsset->mLocation = mpProject->MakeRelativeToRoot(to_location); + pAsset->mLocation = mpProject->MakeRelativeToAssets(to_location); if (Failed(pAsset->LoadRawFile().LogIfFailed(Editor::LogCat))) { delete pAsset; diff --git a/src/run_modes/editor/editor.cpp b/src/run_modes/editor/editor.cpp index f0dcb7b..ca42e8a 100644 --- a/src/run_modes/editor/editor.cpp +++ b/src/run_modes/editor/editor.cpp @@ -25,6 +25,8 @@ // Tools #include "tools/map_editor/map_editor.h" +#include + using namespace lunarium::gui; // ERROR LOG MACRO @@ -37,7 +39,7 @@ namespace editor uint32_t Editor::LogCat = -1; Editor::Editor() - : mpPath(nullptr), mDoNewProject(false), mDoOpenProject(false), + : mDoNewProject(false), mDoOpenProject(false), mDoSaveProject(false), mDoSaveAs(false), mNextWindowID(0), mpMapEditor(nullptr) { } @@ -109,11 +111,6 @@ namespace editor { mAboutPanel.DoFrame(); } - - if (mFileBrowser.IsOpen()) - { - mFileBrowser.DoFrame(); - } } uint32_t Editor::GetLogCat() const @@ -192,77 +189,6 @@ namespace editor void Editor::HandleMenuEvents() { - /////////////////////////// - // FILE - if (mDoNewProject) - { - if (mFileBrowser.GetResult() == FileBrowser::Result::OK) - { - mpPath = mFileBrowser.GetSelectedItem(); - Logger::Info(LogCat, "Generating new project at %s", mpPath->string().c_str()); - - // Generate new project at mpPath - OpRes result = mProject.GenerateProject(mpPath->filename().string(), mpPath->parent_path()); - if (Failed(result)) - { - Logger::Error(LogCat, "Could not create a new project: %s", result.Description); - } - ((AssetBrowser*)mPanelManager.GetPanel(mPanels.AssetBrowser))->SetAssetDirectory(*mpPath / std::filesystem::path("contents/assets")); - mDoNewProject = false; - } - else if (mFileBrowser.GetResult() == FileBrowser::Result::CANCEL) - { - Logger::Info(LogCat, "New Project operation cancelled"); - mDoNewProject = false; - } - - } - - if (mDoOpenProject) - { - if (mFileBrowser.GetResult() == FileBrowser::Result::OK) - { - mpPath = mFileBrowser.GetSelectedItem(); - Logger::Info(LogCat, "Opening project: %s", mpPath->string().c_str()); - - // Open project at mpPath - if (Failed(mProject.LoadProject(*mpPath).LogIfFailed(Editor::LogCat))) - { - mDoOpenProject = false; - return; - } - ((AssetBrowser*)mPanelManager.GetPanel(mPanels.AssetBrowser))->SetAssetDirectory(mpPath->parent_path() / std::filesystem::path("contents/assets")); - mDoOpenProject = false; - } - else if (mFileBrowser.GetResult() == FileBrowser::Result::CANCEL) - { - Logger::Info(LogCat, "Open Project operation cancelled"); - mDoOpenProject = false; - } - - } - - if (mDoSaveProject) - { - if (!mProject.IsLoaded()) - { - mDoSaveAs = true; - mDoSaveProject = false; - } - else - { - mProject.SaveProject(); - mDoSaveProject = false; - } - } - - if (mDoSaveAs) - { - - mDoSaveAs = false; - } - /////////////////////////// - } //////////////////////////////////////////////////////////// @@ -277,37 +203,87 @@ namespace editor { if (ImGui::MenuItem("New Project")) { - mDoNewProject = true; - mFileBrowser.SetSelectionMode(FileBrowser::SelectionMode::FILES_ONLY); - mFileBrowser.SetPrompt("Pick a location and name for the project"); - if (!mFileBrowser.OpenInDirectory("")) + NFD::Guard nfdGuard; + + // auto-freeing memory + NFD::UniquePath outPath; + + // prepare filters for the dialog + nfdfilteritem_t filterItem[1] = {{"Lunarium Project File", "lproj"}}; + + // show the dialog + nfdresult_t result = NFD::SaveDialog(outPath, filterItem, 1); + if (result == NFD_OKAY) { - mDoNewProject = false; - Logger::Error(LogCat, "Could not open the File Browser"); + std::filesystem::path the_path = outPath.get(); + Logger::Info(LogCat, "Generating new project at %s", the_path.string().c_str()); + OpRes result = mProject.GenerateProject(the_path.filename().string(), the_path.parent_path()); + if (Failed(result)) + { + Logger::Error(LogCat, "Could not create a new project: %s", result.Description); + } + else + { + ((AssetBrowser*)mPanelManager.GetPanel(mPanels.AssetBrowser))-> + SetAssetDirectory(the_path.parent_path() / std::filesystem::path("contents/assets")); + } + + } + else if (result == NFD_CANCEL) + { + Logger::Info(LogCat, "User cancelled project create"); + } + else + { + Logger::Error(LogCat, "Error getting project file location: %s", NFD::GetError()); } } if (ImGui::MenuItem("Open Project")) { - mDoOpenProject = true; - mFileBrowser.SetSelectionMode(FileBrowser::SelectionMode::FILES_ONLY); - mFileBrowser.SetPrompt("Pick a location and name for the project"); - if (!mFileBrowser.OpenInDirectory("")) + NFD::Guard nfdGuard; + + // auto-freeing memory + NFD::UniquePath outPath; + + // prepare filters for the dialog + nfdfilteritem_t filterItem[1] = {{"Lunarium Project File", "lproj"}}; + + // show the dialog + nfdresult_t result = NFD::OpenDialog(outPath, filterItem, 1); + if (result == NFD_OKAY) + { + std::filesystem::path the_path = outPath.get(); + + Logger::Info(LogCat, "Opening project: %s", the_path.string().c_str()); + + // Open project at mpPath + if (Failed(mProject.LoadProject(the_path).LogIfFailed(Editor::LogCat))) + { + Logger::Error(LogCat, "Failed to load project: %s", the_path.string().c_str()); + } + else + { + ((AssetBrowser*)mPanelManager.GetPanel(mPanels.AssetBrowser))-> + SetAssetDirectory(the_path.parent_path() / std::filesystem::path("contents/assets")); + } + + + } + else if (result == NFD_CANCEL) + { + Logger::Info(LogCat, "User cancelled project open"); + } + else { - mDoOpenProject = false; - Logger::Error(LogCat, "Could not open the File Browser"); + Logger::Error(LogCat, "Error getting project file location: %s", NFD::GetError()); } } if (ImGui::MenuItem("Save Project")) { - mDoSaveProject = true; - } - - if (ImGui::MenuItem("Save Project As")) - { - mDoSaveAs = true; + mProject.SaveProject(); } ImGui::Separator(); diff --git a/src/run_modes/editor/editor.h b/src/run_modes/editor/editor.h index 618f646..3a556df 100644 --- a/src/run_modes/editor/editor.h +++ b/src/run_modes/editor/editor.h @@ -15,7 +15,6 @@ #include "project.h" #include "panel_manager.h" #include "panels/about.h" -#include #include #include @@ -67,9 +66,6 @@ namespace lunarium { namespace editor unsigned int mNextWindowID; MapEditor* mpMapEditor; - FileBrowser mFileBrowser; - const std::filesystem::path* mpPath; - // Panels struct { diff --git a/src/run_modes/editor/project.cpp b/src/run_modes/editor/project.cpp index 7605a3a..f25441b 100644 --- a/src/run_modes/editor/project.cpp +++ b/src/run_modes/editor/project.cpp @@ -30,17 +30,21 @@ namespace lunarium { namespace editor mName = name; mLocation = location; - mLocation /= mName; + mLocation /= std::filesystem::path(mName).stem(); - if (std::filesystem::exists(mLocation)) + if (!std::filesystem::exists(mLocation)) + { + std::filesystem::create_directory(mLocation); + } + + if (std::filesystem::exists(mLocation / mName)) { return OpRes::Fail("A project with that name (%s) already exists in this location: %s", mName.c_str(), mLocation.string().c_str()); } - std::filesystem::create_directory(mLocation); std::filesystem::path proj_doc = mLocation; - proj_doc /= std::filesystem::path(mName + ".lproj"); + proj_doc /= std::filesystem::path(mName); nlohmann::json j; auto& p = j["Project"]; @@ -125,33 +129,24 @@ namespace lunarium { namespace editor return GetContentDirectory() / "assets"; } - std::filesystem::path Project::MakeRelativeToRoot(std::filesystem::path abs_path) const + std::filesystem::path Project::MakeRelativeToRoot(std::filesystem::path abs_path, bool include_root) const { - /* // HOW TO GET THE PATH RELATIVE TO THE PROJECT ROOT - std::filesystem::path p = "D:\\Projects\\lunarium\\test2\\contents\\assets\\LinkToThePast1_sized.png"; - - std::cout << "\nFull Path: " << p; - std::cout << "\nRelative path: " << p.relative_path(); - - std::filesystem::path proj = "D:\\Projects\\lunarium\\test2"; - std::filesystem::path new_path = proj.filename(); - for (auto iter = p.begin(); iter != p.end(); iter++) - { - std::cout << "\n" << (*iter); - if (proj.string().find((*iter).string()) == std::string::npos) - { - new_path /= (*iter); - } - - } + return MakeRelativeTo(abs_path, mLocation, include_root); + } - std::cout << "\nNew Path: " << new_path; // new_path is what should be used as the location of the asset! - */ + + std::filesystem::path Project::MakeRelativeToAssets(std::filesystem::path abs_path, bool include_root) const + { + return MakeRelativeTo(abs_path, GetAssetDirectory(), include_root); + } - // Validate the passed in path + + std::filesystem::path Project::MakeRelativeTo(std::filesystem::path abs_path, std::filesystem::path rel_to, bool include_root) const + { + // Validate the passed in path if (!abs_path.is_absolute()) { - if (abs_path.filename() == std::filesystem::path(*mLocation.begin())) + if (abs_path.filename() == std::filesystem::path(*rel_to.begin())) { // This path is already relative to the project root return abs_path; @@ -161,10 +156,12 @@ namespace lunarium { namespace editor return ""; } - std::filesystem::path new_path = mLocation.filename(); + std::filesystem::path new_path; + if (include_root) new_path = rel_to.filename(); + for (auto iter = abs_path.begin(); iter != abs_path.end(); iter++) { - if (mLocation.string().find((*iter).string()) == std::string::npos) + if (rel_to.string().find((*iter).string()) == std::string::npos) { new_path /= (*iter); } diff --git a/src/run_modes/editor/project.h b/src/run_modes/editor/project.h index 243e2c6..3e41366 100644 --- a/src/run_modes/editor/project.h +++ b/src/run_modes/editor/project.h @@ -40,7 +40,8 @@ namespace lunarium { namespace editor std::filesystem::path GetAssetDirectory() const; // Convert an absolute path to a path relative to the project root - std::filesystem::path MakeRelativeToRoot(std::filesystem::path) const; + std::filesystem::path MakeRelativeToRoot(std::filesystem::path, bool include_root = false) const; + std::filesystem::path MakeRelativeToAssets(std::filesystem::path, bool include_root = false) const; private: bool mIsLoaded; @@ -52,6 +53,7 @@ namespace lunarium { namespace editor private: void LoadContent(); + std::filesystem::path MakeRelativeTo(std::filesystem::path p, std::filesystem::path rel_to, bool include_root) const; }; }} diff --git a/src/run_modes/editor/tools/map_editor/map_editor.cpp b/src/run_modes/editor/tools/map_editor/map_editor.cpp index 08fdd62..1a07be2 100644 --- a/src/run_modes/editor/tools/map_editor/map_editor.cpp +++ b/src/run_modes/editor/tools/map_editor/map_editor.cpp @@ -270,7 +270,7 @@ namespace lunarium { namespace editor Logger::Info(Editor::LogCat, "Importing tile set: %s", path->string().c_str()); uint64_t id = 0; - ContentManager::GetInstance().ImportFile(*path, /*mpEditor->GetAssetBrowserLocation() / */ path->filename(), AssetType::EATYPE_TILE_SET, id).LogIfFailed(Editor::LogCat); + ContentManager::GetInstance().ImportFile(*path, mpEditor->GetAssetBrowserLocation() / path->filename(), AssetType::EATYPE_TILE_SET, id).LogIfFailed(Editor::LogCat); TileSet* ts = (TileSet*)ContentManager::GetInstance().GetAsset(id); ts->SetTileSize({16, 16}); // NOTE: Hardcoding the tile size for testing