Skip to content

Commit

Permalink
Merge pull request #158 from hakasapl/pgtools
Browse files Browse the repository at this point in the history
PGTools
  • Loading branch information
hakasapl authored Dec 17, 2024
2 parents cb6d3d9 + 4e22bea commit 3808254
Show file tree
Hide file tree
Showing 12 changed files with 294 additions and 14 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [0.8.0] - UNRELEASED

- Added pgtools.exe modding tools

## [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)
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ execute_process(COMMAND ${CMAKE_COMMAND} --build ${EXTRN_BUILD_DIR}/nifly --targ
enable_testing()
add_subdirectory(ParallaxGen)
add_subdirectory(ParallaxGenLib)
add_subdirectory(PGTools)

# Install Global Things
# Copy Folders
Expand Down
21 changes: 21 additions & 0 deletions PGTools/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add Files
set (HEADERS
)

set(SOURCES
"src/main.cpp"
)

include_directories("include")
add_executable(pgtools ${SOURCES} ${HEADERS})

# Packages
find_package(CLI11 REQUIRED CONFIG)

target_link_libraries(pgtools PRIVATE
ParallaxGenLib
CLI11::CLI11
)

install(TARGETS pgtools DESTINATION . )
install(FILES $<TARGET_RUNTIME_DLLS:pgtools> DESTINATION . )
229 changes: 229 additions & 0 deletions PGTools/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
#include <CLI/CLI.hpp>

#include <spdlog/spdlog.h>

#include <windows.h>

#include <string>
#include <unordered_set>

#include "ParallaxGen.hpp"
#include "ParallaxGenD3D.hpp"
#include "ParallaxGenDirectory.hpp"
#include "ParallaxGenWarnings.hpp"

#include "patchers/Patcher.hpp"
#include "patchers/PatcherComplexMaterial.hpp"
#include "patchers/PatcherTruePBR.hpp"
#include "patchers/PatcherUpgradeParallaxToCM.hpp"
#include "patchers/PatcherVanillaParallax.hpp"

using namespace std;

auto getExecutablePath() -> filesystem::path {
wchar_t Buffer[MAX_PATH]; // NOLINT
if (GetModuleFileNameW(nullptr, Buffer, MAX_PATH) == 0) { // NOLINT
cerr << "Error getting executable path: " << GetLastError() << "\n";
exit(1);
}

filesystem::path OutPath = filesystem::path(Buffer);

if (filesystem::exists(OutPath)) {
return OutPath;
}

cerr << "Error getting executable path: path does not exist\n";
exit(1);

return {};
}

struct PGToolsCLIArgs {
int Verbosity = 0;
bool Multithreading = true;
bool GPUAcceleration = true;

struct Patch {
CLI::App *SubCommand = nullptr;
unordered_set<string> Patchers;
filesystem::path Source = ".";
filesystem::path Output = "ParallaxGen_Output";
bool MapTexturesFromMeshes = false;
bool HighMem = false;
} Patch;
};

void mainRunner(PGToolsCLIArgs &Args) {
// Welcome Message
spdlog::info("Welcome to PGTools version {}!", PARALLAXGEN_VERSION);

// Get EXE path
const auto ExePath = getExecutablePath().parent_path();

// Test message if required
if (PARALLAXGEN_TEST_VERSION > 0) {
spdlog::warn("This is an EXPERIMENTAL development build of ParallaxGen: {} Test Build {}", PARALLAXGEN_VERSION,
PARALLAXGEN_TEST_VERSION);
}

// Check if patch subcommand was used
if (Args.Patch.SubCommand->parsed()) {
// Get current time to compare later
auto StartTime = chrono::high_resolution_clock::now();
long long TimeTaken = 0;

Args.Patch.Source = filesystem::absolute(Args.Patch.Source);
Args.Patch.Output = filesystem::absolute(Args.Patch.Output);

auto PGD = ParallaxGenDirectory(Args.Patch.Source, Args.Patch.Output, nullptr);
auto PGD3D = ParallaxGenD3D(&PGD, Args.Patch.Output, ExePath, Args.GPUAcceleration);
auto PG = ParallaxGen(Args.Patch.Output, &PGD, &PGD3D, Args.Patch.Patchers.contains("optimize"));

Patcher::loadStatics(PGD, PGD3D);
ParallaxGenWarnings::init(&PGD, {});

// Check if GPU needs to be initialized
if (Args.GPUAcceleration) {
PGD3D.initGPU();
}

// Create output directory
try {
filesystem::create_directories(Args.Patch.Output);
} catch (const filesystem::filesystem_error &E) {
spdlog::error("Failed to create output directory: {}", E.what());
exit(1);
}

// If output dir is the same as data dir meshes might get overwritten
if (filesystem::equivalent(Args.Patch.Output, PGD.getDataPath())) {
spdlog::critical("Output directory cannot be the same directory as your data folder. "
"Exiting.");
exit(1);
}

// delete existing output
PG.deleteOutputDir();

// Init file map
PGD.populateFileMap(false);

// Map files
PGD.mapFiles({}, {}, {}, {}, Args.Patch.MapTexturesFromMeshes, Args.Multithreading, Args.Patch.HighMem);

if (Args.Patch.Patchers.contains("complexmaterial")) {
// Find CM maps
spdlog::info("Finding complex material env maps");
PGD3D.findCMMaps({});
spdlog::info("Done finding complex material env maps");
}

// Create patcher factory
PatcherUtil::PatcherSet Patchers;
if (Args.Patch.Patchers.contains("parallax")) {
Patchers.ShaderPatchers.emplace(PatcherVanillaParallax::getShaderType(), PatcherVanillaParallax::getFactory());
}
if (Args.Patch.Patchers.contains("complexmaterial")) {
Patchers.ShaderPatchers.emplace(PatcherComplexMaterial::getShaderType(), PatcherComplexMaterial::getFactory());
PatcherComplexMaterial::loadStatics(Args.Patch.Patchers.contains("disablemlp"), {});
}
if (Args.Patch.Patchers.contains("truepbr")) {
Patchers.ShaderPatchers.emplace(PatcherTruePBR::getShaderType(), PatcherTruePBR::getFactory());
PatcherTruePBR::loadStatics(PGD.getPBRJSONs());
}
if (Args.Patch.Patchers.contains("parallaxtocm")) {
Patchers.ShaderTransformPatchers[PatcherUpgradeParallaxToCM::getFromShader()].emplace(
PatcherUpgradeParallaxToCM::getToShader(), PatcherUpgradeParallaxToCM::getFactory());
}

PG.patchMeshes(Patchers, nullptr, Args.Multithreading, false);

// Release cached files, if any
PGD.clearCache();

// Check if dynamic cubemap file is needed
if (Args.Patch.Patchers.contains("complexmaterial")) {
// Install default cubemap file if needed
static const filesystem::path DynCubeMapPath = "textures/cubemaps/dynamic1pxcubemap_black.dds";

spdlog::info("Installing default dynamic cubemap file");

// Create Directory
const filesystem::path OutputCubemapPath = Args.Patch.Output / DynCubeMapPath.parent_path();
filesystem::create_directories(OutputCubemapPath);

filesystem::path AssetPath = filesystem::path(ExePath) / "assets/dynamic1pxcubemap_black_ENB.dds";
filesystem::path OutputPath = filesystem::path(Args.Patch.Output) / DynCubeMapPath;

// Move File
filesystem::copy_file(AssetPath, OutputPath, filesystem::copy_options::overwrite_existing);
}

const auto EndTime = chrono::high_resolution_clock::now();
TimeTaken += chrono::duration_cast<chrono::seconds>(EndTime - StartTime).count();

spdlog::info("ParallaxGen took {} seconds to complete", TimeTaken);
}
}

void addArguments(CLI::App &App, PGToolsCLIArgs &Args) {
// Logging
App.add_flag("-v", Args.Verbosity,
"Verbosity level -v for DEBUG data or -vv for TRACE data "
"(warning: TRACE data is very verbose)");
App.add_flag("--no-multithreading", Args.Multithreading, "Disable multithreading");
App.add_flag("--no-gpu-acceleration", Args.GPUAcceleration, "Disable GPU acceleration");

Args.Patch.SubCommand = App.add_subcommand("patch", "Patch meshes");
Args.Patch.SubCommand->add_option("patcher", Args.Patch.Patchers, "List of patchers to use")
->required()
->delimiter(',');
Args.Patch.SubCommand->add_option("source", Args.Patch.Source, "Source directory")->default_str("");
Args.Patch.SubCommand->add_option("output", Args.Patch.Output, "Output directory")->default_str("ParallaxGen_Output");
Args.Patch.SubCommand->add_flag("--map-textures-from-meshes", Args.Patch.MapTexturesFromMeshes,
"Map textures from meshes (default: false)");
Args.Patch.SubCommand->add_flag("--high-mem", Args.Patch.HighMem, "High memory usage mode (default: false)");
}

auto main(int ArgC, char **ArgV) -> int {
// Block until enter only in debug mode
#ifdef _DEBUG
cout << "Press ENTER to start (DEBUG mode)...";
cin.get();
#endif

SetConsoleOutputCP(CP_UTF8);

// CLI Arguments
PGToolsCLIArgs Args;
CLI::App App{"PGTools: A collection of tools for ParallaxGen"};
addArguments(App, Args);

// Parse CLI Arguments (this is what exits on any validation issues)
CLI11_PARSE(App, ArgC, ArgV);

// Initialize Logger
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v");

// Set logging mode
if (Args.Verbosity >= 1) {
spdlog::set_level(spdlog::level::debug);
spdlog::debug("DEBUG logging enabled");
}

if (Args.Verbosity >= 2) {
spdlog::set_level(spdlog::level::trace);
spdlog::trace("TRACE logging enabled");
}

// Main Runner (Catches all exceptions)
try {
mainRunner(Args);
} catch (const exception &E) {
spdlog::critical("An unhandled exception occurred (Please provide this entire message "
"in your bug report).\n\nException type: {}\nMessage: {}",
typeid(E).name(), E.what());
abort();
}
}
4 changes: 2 additions & 2 deletions ParallaxGen/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,10 @@ void mainRunner(ParallaxGenCLIArgs &Args, const filesystem::path &ExePath) {
spdlog::info(L"ParallaxGen output directory: {}", Params.Output.Dir.wstring());

// Create relevant objects
const auto BG = BethesdaGame(Params.Game.Type, true, Params.Game.Dir);
auto BG = BethesdaGame(Params.Game.Type, true, Params.Game.Dir);

auto MMD = ModManagerDirectory(Params.ModManager.Type);
auto PGD = ParallaxGenDirectory(BG, Params.Output.Dir, &MMD);
auto PGD = ParallaxGenDirectory(&BG, Params.Output.Dir, &MMD);
auto PGD3D = ParallaxGenD3D(&PGD, Params.Output.Dir, ExePath, Params.Processing.GPUAcceleration);
auto PG = ParallaxGen(Params.Output.Dir, &PGD, &PGD3D, Params.PostPatcher.OptimizeMeshes);

Expand Down
14 changes: 12 additions & 2 deletions ParallaxGenLib/include/BethesdaDirectory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class BethesdaDirectory {
std::mutex FileCacheMutex; /** < Mutex for the file cache map */

bool Logging; /** < Bool for whether logging is enabled or not */
BethesdaGame BG; /** < BethesdaGame which stores a BethesdaGame object
BethesdaGame *BG; /** < BethesdaGame which stores a BethesdaGame object
corresponding to this load order */
ModManagerDirectory *MMD; /** < ModManagerDirectory which stores a pointer to a
ModManagerDirectory object corresponding to this
Expand All @@ -103,7 +103,17 @@ class BethesdaDirectory {
* @param BG BethesdaGame object corresponding to load order
* @param Logging Whether to enable CLI logging
*/
BethesdaDirectory(BethesdaGame &BG, std::filesystem::path GeneratedPath = "", ModManagerDirectory *MMD = nullptr, const bool &Logging = false);
BethesdaDirectory(BethesdaGame *BG, std::filesystem::path GeneratedPath = "", ModManagerDirectory *MMD = nullptr, const bool &Logging = false);

/**
* @brief Construct a new Bethesda Directory object without a game type, for generic folders only
*
* @param DataPath Data path
* @param GeneratedPath Generated path
* @param MMD ModManagerDirectory object
* @param Logging Whether to enable CLI logging
*/
BethesdaDirectory(std::filesystem::path DataPath, std::filesystem::path GeneratedPath = "", ModManagerDirectory *MMD = nullptr, const bool &Logging = false);

/**
* @brief Populate file map with all files in the load order
Expand Down
3 changes: 2 additions & 1 deletion ParallaxGenLib/include/ParallaxGenDirectory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ class ParallaxGenDirectory : public BethesdaDirectory {

public:
// constructor - calls the BethesdaDirectory constructor
ParallaxGenDirectory(BethesdaGame BG, std::filesystem::path OutputPath = "", ModManagerDirectory *MMD = nullptr);
ParallaxGenDirectory(BethesdaGame *BG, std::filesystem::path OutputPath = "", ModManagerDirectory *MMD = nullptr);
ParallaxGenDirectory(std::filesystem::path DataPath, std::filesystem::path OutputPath = "", ModManagerDirectory *MMD = nullptr);

/// @brief Map all files in the load order to their type
///
Expand Down
21 changes: 16 additions & 5 deletions ParallaxGenLib/src/BethesdaDirectory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,23 @@
using namespace std;
using namespace ParallaxGenUtil;

BethesdaDirectory::BethesdaDirectory(BethesdaGame &BG, filesystem::path GeneratedPath, ModManagerDirectory *MMD, const bool &Logging) : GeneratedDir(std::move(GeneratedPath)), Logging(Logging), BG(BG), MMD(MMD) {
BethesdaDirectory::BethesdaDirectory(BethesdaGame *BG, filesystem::path GeneratedPath, ModManagerDirectory *MMD, const bool &Logging) : GeneratedDir(std::move(GeneratedPath)), Logging(Logging), BG(BG), MMD(MMD) {
// Assign instance vars
DataDir = filesystem::path(this->BG.getGameDataPath());
DataDir = filesystem::path(this->BG->getGameDataPath());

if (this->Logging) {
// Log starting message
spdlog::info(L"Opening Data Folder \"{}\"", DataDir.wstring());
}
}

BethesdaDirectory::BethesdaDirectory(filesystem::path DataPath, filesystem::path GeneratedPath, ModManagerDirectory *MMD, const bool &Logging) : DataDir(std::move(DataPath)), GeneratedDir(std::move(GeneratedPath)), Logging(Logging), BG(nullptr), MMD(MMD) {
if (this->Logging) {
// Log starting message
spdlog::info(L"Opening Data Folder \"{}\"", DataDir.wstring());
}
}

//
// Constant Definitions
//
Expand Down Expand Up @@ -97,7 +104,7 @@ void BethesdaDirectory::populateFileMap(bool IncludeBSAs) {
FileMap.clear();
}

if (IncludeBSAs) {
if (IncludeBSAs && BG != nullptr) {
// add BSA files to file map
addBSAFilesToMap();
}
Expand Down Expand Up @@ -278,6 +285,10 @@ auto BethesdaDirectory::getDataPath() const -> filesystem::path { return DataDir
auto BethesdaDirectory::getGeneratedPath() const -> filesystem::path { return GeneratedDir; }

void BethesdaDirectory::addBSAFilesToMap() {
if (BG == nullptr) {
throw runtime_error("BethesdaGame object is not set which is required to load BSA files");
}

if (Logging) {
spdlog::info("Adding BSA files to file map.");
}
Expand Down Expand Up @@ -416,7 +427,7 @@ auto BethesdaDirectory::getBSALoadOrder() const -> vector<wstring> {
vector<wstring> OutBSAOrder = getBSAFilesFromINIs();

// get esp priority list
const vector<wstring> LoadOrder = BG.getActivePlugins(true);
const vector<wstring> LoadOrder = BG->getActivePlugins(true);

// list BSA files in data directory
const vector<wstring> AllBSAFiles = getBSAFilesInDirectory();
Expand Down Expand Up @@ -463,7 +474,7 @@ auto BethesdaDirectory::getBSAFilesFromINIs() const -> vector<wstring> {
}

// find ini paths
const BethesdaGame::ININame INILocs = BG.getINIPaths();
const BethesdaGame::ININame INILocs = BG->getINIPaths();

vector<filesystem::path> INIFileOrder = {INILocs.INI, INILocs.INICustom};

Expand Down
5 changes: 4 additions & 1 deletion ParallaxGenLib/src/ParallaxGenDirectory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@
using namespace std;
using namespace ParallaxGenUtil;

ParallaxGenDirectory::ParallaxGenDirectory(BethesdaGame BG, filesystem::path OutputPath, ModManagerDirectory *MMD)
ParallaxGenDirectory::ParallaxGenDirectory(BethesdaGame *BG, filesystem::path OutputPath, ModManagerDirectory *MMD)
: BethesdaDirectory(BG, std::move(OutputPath), MMD, true) {}

ParallaxGenDirectory::ParallaxGenDirectory(filesystem::path DataPath, filesystem::path OutputPath, ModManagerDirectory *MMD)
: BethesdaDirectory(std::move(DataPath), std::move(OutputPath), MMD, true) {}

auto ParallaxGenDirectory::findFiles() -> void {
// Clear existing unconfirmedtextures
UnconfirmedTextures.clear();
Expand Down
Loading

0 comments on commit 3808254

Please sign in to comment.