Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.7.3: hotfix for 0.7.2 #146

Merged
merged 7 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading