diff --git a/CHANGELOG.md b/CHANGELOG.md index f83559b..373f3e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [0.7.3] - 2024-12-09 + +- Added a warning for simplicity of snow users if PBR or CM is enabled (SoS is incompatible with these shaders) +- Fixed thread gridlock that casued mesh patching to get suck occasionally +- Fixed PBR JSON delete: true not working +- Fixed uncaught exception if mod folder does not exist in MO2 +- Fixed dyncubemap blocklist not saving/loading correctly in the GUI +- Fixed dyncubemap blocklist not showing up on start in the GUI if advanced options is enabled +- Fixed uncaught exception when a NIF has non-ASCII chars as a texture slot + ## [0.7.2] - 2024-12-07 - Added mesh allowlist diff --git a/CMakeLists.txt b/CMakeLists.txt index d3a21cd..378c5e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ add_compile_definitions(_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR) set(PROJECT_NAME "ParallaxGen") # Initialize Project -set(PARALLAXGEN_VERSION 0.7.2) +set(PARALLAXGEN_VERSION 0.7.3) project(${PROJECT_NAME} VERSION ${PARALLAXGEN_VERSION}) add_compile_definitions(PARALLAXGEN_VERSION="${PARALLAXGEN_VERSION}") diff --git a/ParallaxGen/src/GUI/LauncherWindow.cpp b/ParallaxGen/src/GUI/LauncherWindow.cpp index 4b7cbc2..72c0cc0 100644 --- a/ParallaxGen/src/GUI/LauncherWindow.cpp +++ b/ParallaxGen/src/GUI/LauncherWindow.cpp @@ -209,7 +209,7 @@ LauncherWindow::LauncherWindow(ParallaxGenConfig &PGC) ShaderPatcherComplexMaterialDynCubemapBlocklist->SetColumnWidth( 0, ShaderPatcherComplexMaterialDynCubemapBlocklist->GetClientSize().GetWidth() - ScrollbarWidth); ShaderPatcherComplexMaterialDynCubemapBlocklist->Bind( - wxEVT_LIST_ITEM_ACTIVATED, &LauncherWindow::onShaderPatcherComplexMaterialDynCubemapBlocklistChange, this); + wxEVT_LIST_END_LABEL_EDIT, &LauncherWindow::onShaderPatcherComplexMaterialDynCubemapBlocklistChange, this); ShaderPatcherComplexMaterialDynCubemapBlocklist->Bind(wxEVT_LIST_ITEM_ACTIVATED, &LauncherWindow::onListItemActivated, this); ShaderPatcherComplexMaterialOptionsSizer->Add(ShaderPatcherComplexMaterialDynCubemapBlocklist, 0, wxEXPAND | wxALL, @@ -509,7 +509,6 @@ void LauncherWindow::loadConfig() { // Advanced AdvancedOptionsCheckbox->SetValue(InitParams.Advanced); - updateAdvanced(); // Processing ProcessingPluginPatchingCheckbox->SetValue(InitParams.Processing.PluginPatching); @@ -571,6 +570,8 @@ void LauncherWindow::loadConfig() { TextureRulesVanillaBSAList->InsertItem(TextureRulesVanillaBSAList->GetItemCount(), VanillaBSA); } TextureRulesVanillaBSAList->InsertItem(TextureRulesVanillaBSAList->GetItemCount(), ""); // Add empty line + + updateAdvanced(); } // Component event handlers diff --git a/ParallaxGen/src/ParallaxGenConfig.cpp b/ParallaxGen/src/ParallaxGenConfig.cpp index c309e28..2a5ddaa 100644 --- a/ParallaxGen/src/ParallaxGenConfig.cpp +++ b/ParallaxGen/src/ParallaxGenConfig.cpp @@ -68,9 +68,8 @@ auto ParallaxGenConfig::getDefaultParams() -> PGParams { OutParams.Output.Dir = ExePath / "ParallaxGen_Output"; // Mesh Rules - static const vector DefaultMeshBlocklist = {L"*\\cameras\\*", L"*\\dyndolod\\*", L"*\\lod\\*", - L"*\\magic\\*", L"*\\markers\\*", L"*\\mps\\*", - L"*\\sky\\*"}; + static const vector DefaultMeshBlocklist = { + L"*\\cameras\\*", L"*\\dyndolod\\*", L"*\\lod\\*", L"*\\magic\\*", L"*\\markers\\*", L"*\\mps\\*", L"*\\sky\\*"}; OutParams.MeshRules.BlockList = DefaultMeshBlocklist; // Texture Rules @@ -230,6 +229,12 @@ auto ParallaxGenConfig::addConfigJSON(const nlohmann::json &J) -> void { if (ParamJ.contains("shaderpatcher") && ParamJ["shaderpatcher"].contains("complexmaterial")) { ParamJ["shaderpatcher"]["complexmaterial"].get_to(Params.ShaderPatcher.ComplexMaterial); } + if (ParamJ.contains("shaderpatcher") && ParamJ["shaderpatcher"].contains("complexmaterialdyncubemapblocklist")) { + for (const auto &Item : ParamJ["shaderpatcher"]["complexmaterialdyncubemapblocklist"]) { + Params.ShaderPatcher.ShaderPatcherComplexMaterial.ListsDyncubemapBlocklist.push_back( + UTF8toUTF16(Item.get())); + } + } if (ParamJ.contains("shaderpatcher") && ParamJ["shaderpatcher"].contains("truepbr")) { ParamJ["shaderpatcher"]["truepbr"].get_to(Params.ShaderPatcher.TruePBR); } @@ -384,8 +389,7 @@ auto ParallaxGenConfig::validateParams(const PGParams &Params, vector &E } // Check if the MO2 profile exists - const auto Profiles = - ModManagerDirectory::getMO2ProfilesFromInstanceDir(Params.ModManager.MO2InstanceDir); + const auto Profiles = ModManagerDirectory::getMO2ProfilesFromInstanceDir(Params.ModManager.MO2InstanceDir); if (find(Profiles.begin(), Profiles.end(), Params.ModManager.MO2Profile) == Profiles.end()) { Errors.emplace_back("MO2 Profile does not exist"); } @@ -513,6 +517,8 @@ void ParallaxGenConfig::saveUserConfig() { // "shaderpatcher" J["params"]["shaderpatcher"]["parallax"] = Params.ShaderPatcher.Parallax; J["params"]["shaderpatcher"]["complexmaterial"] = Params.ShaderPatcher.ComplexMaterial; + J["params"]["shaderpatcher"]["complexmaterialdyncubemapblocklist"] = + utf16VectorToUTF8(Params.ShaderPatcher.ShaderPatcherComplexMaterial.ListsDyncubemapBlocklist); J["params"]["shaderpatcher"]["truepbr"] = Params.ShaderPatcher.TruePBR; // "shadertransforms" diff --git a/ParallaxGen/src/main.cpp b/ParallaxGen/src/main.cpp index 1a42755..861eb38 100644 --- a/ParallaxGen/src/main.cpp +++ b/ParallaxGen/src/main.cpp @@ -75,8 +75,7 @@ auto getGameTypeMapStr() -> string { return GameTypeStr; } -auto deployAssets(const filesystem::path &OutputDir, - const filesystem::path &ExePath) -> void { +auto deployAssets(const filesystem::path &OutputDir, const filesystem::path &ExePath) -> void { // Install default cubemap file if needed static const filesystem::path DynCubeMapPath = "textures/cubemaps/dynamic1pxcubemap_black.dds"; @@ -200,6 +199,20 @@ void mainRunner(ParallaxGenCLIArgs &Args, const filesystem::path &ExePath) { exit(1); } + // Check if dyndolod.esp exists + const auto ActivePlugins = BG.getActivePlugins(false, true); + if (find(ActivePlugins.begin(), ActivePlugins.end(), L"dyndolod.esp") != ActivePlugins.end()) { + spdlog::critical("DynDoLOD and TexGen outputs must be disabled prior to running ParallaxGen. It is recommended to " + "generate LODs after running ParallaxGen with the ParallaxGen output enabled."); + exit(1); + } + + if (find(ActivePlugins.begin(), ActivePlugins.end(), L"simplicity of snow.esp") != ActivePlugins.end() && + (Params.ShaderPatcher.TruePBR || Params.ShaderPatcher.ComplexMaterial)) { + spdlog::warn("You have Simplicity of Snow installed. SoS is incompatible with complex material and PBR. Use a " + "single-pass snow mod such as Better Dynamic Snow v3 instead."); + } + // Init PGP library if (Params.Processing.PluginPatching) { spdlog::info("Initializing plugin patcher"); @@ -229,14 +242,6 @@ void mainRunner(ParallaxGenCLIArgs &Args, const filesystem::path &ExePath) { exit(1); } - // Check if dyndolod.esp exists - const auto ActivePlugins = BG.getActivePlugins(false, true); - if (find(ActivePlugins.begin(), ActivePlugins.end(), L"dyndolod.esp") != ActivePlugins.end()) { - spdlog::critical("DynDoLOD and TexGen outputs must be disabled prior to running ParallaxGen. It is recommended to " - "generate LODs after running ParallaxGen with the ParallaxGen output enabled."); - exit(1); - } - // Init file map PGD.populateFileMap(Params.Processing.BSA); diff --git a/ParallaxGenLib/include/ParallaxGenTask.hpp b/ParallaxGenLib/include/ParallaxGenTask.hpp index 759562d..5d6d466 100644 --- a/ParallaxGenLib/include/ParallaxGenTask.hpp +++ b/ParallaxGenLib/include/ParallaxGenTask.hpp @@ -28,12 +28,14 @@ class ParallaxGenTask { ParallaxGenTask(std::string TaskName, const size_t &TotalJobs, const int &ProgressPrintModulo = 1); void completeJob(const PGResult &Result); - void initJobStatus(); - void printJobStatus(bool Force = false); - void printJobSummary(); - [[nodiscard]] auto getCompletedJobs() -> size_t; [[nodiscard]] auto isCompleted() -> bool; static void updatePGResult(PGResult &Result, const PGResult &CurrentResult, const PGResult &Threshold = PGResult::FAILURE); + +private: + void initJobStatus(); + void printJobStatus(bool Force = false); + void printJobSummary(); + [[nodiscard]] auto getCompletedJobs() -> size_t; }; diff --git a/ParallaxGenLib/src/ModManagerDirectory.cpp b/ParallaxGenLib/src/ModManagerDirectory.cpp index 2ec1d85..b41343e 100644 --- a/ParallaxGenLib/src/ModManagerDirectory.cpp +++ b/ParallaxGenLib/src/ModManagerDirectory.cpp @@ -157,6 +157,12 @@ void ModManagerDirectory::populateModFileMapMO2(const filesystem::path &Instance Mod.erase(0, 1); // remove + const auto CurModDir = ModDir / Mod; + // Check if mod folder exists + if (!filesystem::exists(CurModDir)) { + spdlog::warn(L"Mod directory from modlist.txt does not exist: {}", CurModDir.wstring()); + continue; + } + // check if mod dir is output dir if (filesystem::equivalent(CurModDir, OutputDir)) { spdlog::critical(L"If outputting to MO2 you must disable the mod {} first to prevent issues with MO2 VFS", Mod); @@ -166,11 +172,6 @@ void ModManagerDirectory::populateModFileMapMO2(const filesystem::path &Instance AllMods.insert(Mod); InferredOrder.insert(InferredOrder.begin(), Mod); - if (!filesystem::exists(CurModDir)) { - spdlog::warn(L"Mod directory from modlist.txt does not exist: {}", CurModDir.wstring()); - continue; - } - try { for (const auto &File : filesystem::recursive_directory_iterator(CurModDir, filesystem::directory_options::skip_permission_denied)) { diff --git a/ParallaxGenLib/src/ParallaxGen.cpp b/ParallaxGenLib/src/ParallaxGen.cpp index 866a8b7..356f837 100644 --- a/ParallaxGenLib/src/ParallaxGen.cpp +++ b/ParallaxGenLib/src/ParallaxGen.cpp @@ -69,19 +69,17 @@ void ParallaxGen::patchMeshes(const PatcherUtil::PatcherSet &Patchers, const uno boost::asio::thread_pool MeshPatchPool(NumThreads); - bool KillThreads = false; - mutex KillThreadsMutex; + atomic KillThreads = false; for (const auto &Mesh : Meshes) { boost::asio::post(MeshPatchPool, [this, &TaskTracker, &DiffJSON, &Mesh, &PatchPlugin, &Patchers, &ModPriority, - &KillThreads, &KillThreadsMutex] { + &KillThreads] { ParallaxGenTask::PGResult Result = ParallaxGenTask::PGResult::SUCCESS; try { Result = processNIF(Patchers, ModPriority, Mesh, &DiffJSON, PatchPlugin); } catch (const exception &E) { - lock_guard Lock(KillThreadsMutex); spdlog::error(L"Exception in thread patching NIF {}: {}", Mesh.wstring(), ASCIItoUTF16(E.what())); - KillThreads = true; + KillThreads.store(true, std::memory_order_release); Result = ParallaxGenTask::PGResult::FAILURE; } @@ -90,10 +88,12 @@ void ParallaxGen::patchMeshes(const PatcherUtil::PatcherSet &Patchers, const uno } while (!TaskTracker.isCompleted()) { - if (KillThreads) { + if (KillThreads.load(std::memory_order_acquire)) { MeshPatchPool.stop(); throw runtime_error("Exception in thread patching NIF"); } + + this_thread::sleep_for(std::chrono::milliseconds(10)); } // verify that all threads complete (should be redundant) @@ -138,21 +138,19 @@ auto ParallaxGen::findModConflicts(const PatcherUtil::PatcherSet &Patchers, cons const size_t NumThreads = boost::thread::hardware_concurrency(); #endif - bool KillThreads = false; - mutex KillThreadsMutex; + atomic KillThreads = false; boost::asio::thread_pool MeshPatchPool(NumThreads); for (const auto &Mesh : Meshes) { boost::asio::post(MeshPatchPool, [this, &TaskTracker, &Mesh, &PatchPlugin, &ConflictMods, &ConflictModsMutex, - &Patchers, &KillThreads, &KillThreadsMutex] { + &Patchers, &KillThreads] { ParallaxGenTask::PGResult Result = ParallaxGenTask::PGResult::SUCCESS; try { Result = processNIF(Patchers, nullptr, Mesh, nullptr, PatchPlugin, true, &ConflictMods, &ConflictModsMutex); } catch (const exception &E) { - lock_guard Lock(KillThreadsMutex); spdlog::error(L"Exception in thread finding mod conflicts {}: {}", Mesh.wstring(), ASCIItoUTF16(E.what())); - KillThreads = true; + KillThreads.store(true, std::memory_order_release); Result = ParallaxGenTask::PGResult::FAILURE; } @@ -161,10 +159,12 @@ auto ParallaxGen::findModConflicts(const PatcherUtil::PatcherSet &Patchers, cons } while (!TaskTracker.isCompleted()) { - if (KillThreads) { + if (KillThreads.load(std::memory_order_acquire)) { MeshPatchPool.stop(); throw runtime_error("Exception in thread finding mod conflicts"); } + + this_thread::sleep_for(std::chrono::milliseconds(10)); } // verify that all threads complete (should be redundant) @@ -303,6 +303,17 @@ auto ParallaxGen::processNIF( bool OneShapeSuccess = false; vector> ShapeTracker; for (NiShape *NIFShape : NIF.GetShapes()) { + // Check for any non-ascii chars + for (uint32_t Slot = 0; Slot < NUM_TEXTURE_SLOTS; Slot++) { + string Texture; + NIF.GetTextureSlot(NIFShape, Texture, Slot); + + if (!ContainsOnlyAscii(Texture)) { + spdlog::error(L"NIF {} has texture slot(s) with invalid non-ASCII chars (skipping)", NIFFile.wstring()); + return ParallaxGenTask::PGResult::FAILURE; + } + } + // get shape name and blockid const auto ShapeBlockID = NIF.GetBlockID(NIFShape); const auto ShapeName = ASCIItoUTF16(NIFShape->name.get()); @@ -320,16 +331,14 @@ auto ParallaxGen::processNIF( ConflictModsMutex), ParallaxGenTask::PGResult::SUCCESS_WITH_WARNINGS); + NIFModified |= ShapeModified; + // Update NIFModified if shape was modified - if (ShapeModified && ShaderApplied != NIFUtil::ShapeShader::NONE) { - NIFModified = true; - - if (PatchPlugin) { - // Process plugin - array NewSlots; - ParallaxGenPlugin::processShape(ShaderApplied, NIFFile.wstring(), ShapeName, OldShapeIndex, PatcherObjects, - ModPriority, NewSlots); - } + if (ShaderApplied != NIFUtil::ShapeShader::NONE && PatchPlugin) { + // Process plugin + array NewSlots; + ParallaxGenPlugin::processShape(ShaderApplied, NIFFile.wstring(), ShapeName, OldShapeIndex, PatcherObjects, + ModPriority, NewSlots); } if (Result == ParallaxGenTask::PGResult::SUCCESS) { diff --git a/ParallaxGenLib/src/ParallaxGenDirectory.cpp b/ParallaxGenLib/src/ParallaxGenDirectory.cpp index 0676986..1aa6b68 100644 --- a/ParallaxGenLib/src/ParallaxGenDirectory.cpp +++ b/ParallaxGenLib/src/ParallaxGenDirectory.cpp @@ -96,8 +96,7 @@ auto ParallaxGenDirectory::mapFiles(const vector &NIFBlocklist, const v const size_t NumThreads = boost::thread::hardware_concurrency(); #endif - bool KillThreads = false; - mutex KillThreadsMutex; + atomic KillThreads = false; boost::asio::thread_pool MapTextureFromMeshPool(NumThreads); @@ -125,36 +124,31 @@ auto ParallaxGenDirectory::mapFiles(const vector &NIFBlocklist, const v } if (Multithreading) { - boost::asio::post( - MapTextureFromMeshPool, [this, &TaskTracker, &Mesh, &CacheNIFs, &KillThreads, &KillThreadsMutex] { - ParallaxGenTask::PGResult Result = ParallaxGenTask::PGResult::SUCCESS; - try { - Result = mapTexturesFromNIF(Mesh, CacheNIFs); - } catch (const exception &E) { - lock_guard Lock(KillThreadsMutex); - spdlog::error(L"Exception in thread loading NIF \"{}\": {}", Mesh.wstring(), ASCIItoUTF16(E.what())); - KillThreads = true; - Result = ParallaxGenTask::PGResult::FAILURE; - } - - TaskTracker.completeJob(Result); - }); + boost::asio::post(MapTextureFromMeshPool, [this, &TaskTracker, &Mesh, &CacheNIFs, &KillThreads] { + ParallaxGenTask::PGResult Result = ParallaxGenTask::PGResult::SUCCESS; + try { + Result = mapTexturesFromNIF(Mesh, CacheNIFs); + } catch (const exception &E) { + spdlog::error(L"Exception in thread loading NIF \"{}\": {}", Mesh.wstring(), ASCIItoUTF16(E.what())); + KillThreads.store(true, std::memory_order_release); + Result = ParallaxGenTask::PGResult::FAILURE; + } + + TaskTracker.completeJob(Result); + }); } else { - try { - TaskTracker.completeJob(mapTexturesFromNIF(Mesh, CacheNIFs)); - } catch (const exception &E) { - spdlog::error(L"Exception loading NIF \"{}\": {}", Mesh.wstring(), ASCIItoUTF16(E.what())); - TaskTracker.completeJob(ParallaxGenTask::PGResult::FAILURE); - } + TaskTracker.completeJob(mapTexturesFromNIF(Mesh, CacheNIFs)); } } if (Multithreading) { while (!TaskTracker.isCompleted()) { - if (KillThreads) { + if (KillThreads.load(std::memory_order_acquire)) { MapTextureFromMeshPool.stop(); throw runtime_error("Exception in thread mapping textures from NIFs"); } + + this_thread::sleep_for(std::chrono::milliseconds(10)); } // Wait for all threads to complete @@ -281,6 +275,12 @@ auto ParallaxGenDirectory::mapTexturesFromNIF(const filesystem::path &NIFPath, for (uint32_t Slot = 0; Slot < NUM_TEXTURE_SLOTS; Slot++) { string Texture; NIF.GetTextureSlot(Shape, Texture, Slot); + + if (!ContainsOnlyAscii(Texture)) { + spdlog::error(L"NIF {} has texture slot(s) with invalid non-ASCII chars (skipping)", NIFPath.wstring()); + return ParallaxGenTask::PGResult::FAILURE; + } + if (Texture.empty()) { // No texture in this slot continue; diff --git a/ParallaxGenLib/src/ParallaxGenTask.cpp b/ParallaxGenLib/src/ParallaxGenTask.cpp index 086f34b..f88c7b7 100644 --- a/ParallaxGenLib/src/ParallaxGenTask.cpp +++ b/ParallaxGenLib/src/ParallaxGenTask.cpp @@ -13,11 +13,6 @@ void ParallaxGenTask::completeJob(const PGResult &Result) { // Use lock_guard to make this method thread-safe const lock_guard Lock(NumJobsCompletedMutex); - // Check if NumJobsCompleted has Result for key - if (NumJobsCompleted.find(Result) == NumJobsCompleted.end()) { - NumJobsCompleted[Result] = 0; - } - NumJobsCompleted[Result]++; printJobStatus(); } @@ -25,6 +20,11 @@ void ParallaxGenTask::completeJob(const PGResult &Result) { void ParallaxGenTask::initJobStatus() { LastPerc = 0; + // Initialize all known PGResult values + NumJobsCompleted[PGResult::FAILURE] = 0; + NumJobsCompleted[PGResult::SUCCESS] = 0; + NumJobsCompleted[PGResult::SUCCESS_WITH_WARNINGS] = 0; + spdlog::info("{} Starting...", TaskName); } @@ -48,8 +48,10 @@ void ParallaxGenTask::printJobSummary() { // Print each job status Result string OutputLog = TaskName + " Summary: "; for (const auto &Pair : NumJobsCompleted) { - const string StateStr = PGResultStr[Pair.first]; - OutputLog += "[ " + StateStr + " : " + to_string(Pair.second) + " ] "; + if (Pair.second > 0) { + const string StateStr = PGResultStr[Pair.first]; + OutputLog += "[ " + StateStr + " : " + to_string(Pair.second) + " ] "; + } } OutputLog += "See log to see error messages, if any."; spdlog::info(OutputLog); diff --git a/ParallaxGenLib/src/ParallaxGenWarnings.cpp b/ParallaxGenLib/src/ParallaxGenWarnings.cpp index f21b7d7..72801e2 100644 --- a/ParallaxGenLib/src/ParallaxGenWarnings.cpp +++ b/ParallaxGenLib/src/ParallaxGenWarnings.cpp @@ -1,5 +1,6 @@ #include "ParallaxGenWarnings.hpp" +#include #include using namespace std; @@ -46,8 +47,15 @@ void ParallaxGenWarnings::mismatchWarn(const wstring &MatchedPath, const wstring return; } - MismatchWarnDebugTracker[MatchedPathMod].insert(std::make_pair(MatchedPath,BaseTex)); - MismatchWarnTracker[MatchedPathMod].insert(BaseTexMod); + { + const lock_guard Lock(MismatchWarnDebugTrackerMutex); + MismatchWarnDebugTracker[MatchedPathMod].insert(std::make_pair(MatchedPath,BaseTex)); + } + + { + const lock_guard Lock2(MismatchWarnTrackerMutex); + MismatchWarnTracker[MatchedPathMod].insert(BaseTexMod); + } } void ParallaxGenWarnings::printWarnings() { @@ -132,9 +140,4 @@ void ParallaxGenWarnings::meshWarn(const wstring &MatchedPath, const wstring &NI // add to tracker if not MeshWarnTracker.insert(Key); } - - // log warning - //spdlog::warn( - // L"[Potential Mesh Mismatch] Mod \"{}\" assets were used on meshes from mod \"{}\". Please verify that this is intended.", - // MatchedPathMod, NIFPathMod); } diff --git a/ParallaxGenLib/src/patchers/PatcherTruePBR.cpp b/ParallaxGenLib/src/patchers/PatcherTruePBR.cpp index aff3789..8166acc 100644 --- a/ParallaxGenLib/src/patchers/PatcherTruePBR.cpp +++ b/ParallaxGenLib/src/patchers/PatcherTruePBR.cpp @@ -209,14 +209,27 @@ auto PatcherTruePBR::shouldApply(const std::array(JSONData); + // loop through json data + bool DeleteShape = false; + for (const auto &[Sequence, Data] : JSONData) { + if (get<0>(Data).contains("delete") && get<0>(Data)["delete"].get()) { + Logger::trace(L"PBR JSON Match: Result marked for deletion, skipping slot checks"); + DeleteShape = true; + break; + } + } + // check paths bool Valid = true; - const auto NewSlots = applyPatchSlots(OldSlots, Match); - for (size_t I = 0; I < NUM_TEXTURE_SLOTS; I++) { - if (!NewSlots[I].empty() && !getPGD()->isFile(NewSlots[I])) { - // Slot does not exist - Logger::trace(L"PBR JSON Match: Result texture slot {} does not exist", NewSlots[I]); - Valid = false; + + if (!DeleteShape) { + const auto NewSlots = applyPatchSlots(OldSlots, Match); + for (size_t I = 0; I < NUM_TEXTURE_SLOTS; I++) { + if (!NewSlots[I].empty() && !getPGD()->isFile(NewSlots[I])) { + // Slot does not exist + Logger::trace(L"PBR JSON Match: Result texture slot {} does not exist", NewSlots[I]); + Valid = false; + } } }