Skip to content

Commit

Permalink
Merge pull request #146 from hakasapl/0.7.3
Browse files Browse the repository at this point in the history
0.7.3: hotfix for 0.7.2
  • Loading branch information
hakasapl authored Dec 9, 2024
2 parents c73e375 + 97cd008 commit cb6d3d9
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 91 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}")

Expand Down
5 changes: 3 additions & 2 deletions ParallaxGen/src/GUI/LauncherWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -509,7 +509,6 @@ void LauncherWindow::loadConfig() {

// Advanced
AdvancedOptionsCheckbox->SetValue(InitParams.Advanced);
updateAdvanced();

// Processing
ProcessingPluginPatchingCheckbox->SetValue(InitParams.Processing.PluginPatching);
Expand Down Expand Up @@ -571,6 +570,8 @@ void LauncherWindow::loadConfig() {
TextureRulesVanillaBSAList->InsertItem(TextureRulesVanillaBSAList->GetItemCount(), VanillaBSA);
}
TextureRulesVanillaBSAList->InsertItem(TextureRulesVanillaBSAList->GetItemCount(), ""); // Add empty line

updateAdvanced();
}

// Component event handlers
Expand Down
16 changes: 11 additions & 5 deletions ParallaxGen/src/ParallaxGenConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,8 @@ auto ParallaxGenConfig::getDefaultParams() -> PGParams {
OutParams.Output.Dir = ExePath / "ParallaxGen_Output";

// Mesh Rules
static const vector<wstring> DefaultMeshBlocklist = {L"*\\cameras\\*", L"*\\dyndolod\\*", L"*\\lod\\*",
L"*\\magic\\*", L"*\\markers\\*", L"*\\mps\\*",
L"*\\sky\\*"};
static const vector<wstring> DefaultMeshBlocklist = {
L"*\\cameras\\*", L"*\\dyndolod\\*", L"*\\lod\\*", L"*\\magic\\*", L"*\\markers\\*", L"*\\mps\\*", L"*\\sky\\*"};
OutParams.MeshRules.BlockList = DefaultMeshBlocklist;

// Texture Rules
Expand Down Expand Up @@ -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<bool>(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<string>()));
}
}
if (ParamJ.contains("shaderpatcher") && ParamJ["shaderpatcher"].contains("truepbr")) {
ParamJ["shaderpatcher"]["truepbr"].get_to<bool>(Params.ShaderPatcher.TruePBR);
}
Expand Down Expand Up @@ -384,8 +389,7 @@ auto ParallaxGenConfig::validateParams(const PGParams &Params, vector<string> &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");
}
Expand Down Expand Up @@ -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"
Expand Down
25 changes: 15 additions & 10 deletions ParallaxGen/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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);

Expand Down
10 changes: 6 additions & 4 deletions ParallaxGenLib/include/ParallaxGenTask.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
11 changes: 6 additions & 5 deletions ParallaxGenLib/src/ModManagerDirectory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)) {
Expand Down
51 changes: 30 additions & 21 deletions ParallaxGenLib/src/ParallaxGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool> 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<mutex> 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;
}

Expand All @@ -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)
Expand Down Expand Up @@ -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<bool> 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<mutex> 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;
}

Expand All @@ -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)
Expand Down Expand Up @@ -303,6 +303,17 @@ auto ParallaxGen::processNIF(
bool OneShapeSuccess = false;
vector<tuple<NiShape *, int, int>> 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());
Expand All @@ -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<wstring, NUM_TEXTURE_SLOTS> NewSlots;
ParallaxGenPlugin::processShape(ShaderApplied, NIFFile.wstring(), ShapeName, OldShapeIndex, PatcherObjects,
ModPriority, NewSlots);
}
if (ShaderApplied != NIFUtil::ShapeShader::NONE && PatchPlugin) {
// Process plugin
array<wstring, NUM_TEXTURE_SLOTS> NewSlots;
ParallaxGenPlugin::processShape(ShaderApplied, NIFFile.wstring(), ShapeName, OldShapeIndex, PatcherObjects,
ModPriority, NewSlots);
}

if (Result == ParallaxGenTask::PGResult::SUCCESS) {
Expand Down
46 changes: 23 additions & 23 deletions ParallaxGenLib/src/ParallaxGenDirectory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ auto ParallaxGenDirectory::mapFiles(const vector<wstring> &NIFBlocklist, const v
const size_t NumThreads = boost::thread::hardware_concurrency();
#endif

bool KillThreads = false;
mutex KillThreadsMutex;
atomic<bool> KillThreads = false;

boost::asio::thread_pool MapTextureFromMeshPool(NumThreads);

Expand Down Expand Up @@ -125,36 +124,31 @@ auto ParallaxGenDirectory::mapFiles(const vector<wstring> &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<mutex> 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
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit cb6d3d9

Please sign in to comment.