2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2026-06-27 03:18:17 +00:00

feat: xmodel preview in ModMan (#835)

* chore: upgrade webwindowed for dynamic assets

* chore: make enums in ModMan lowercase

* chore: add missing platform wiiu in ModMan

* fix: register asset handler on all windows

* chore: properly localize game and platform

* chore: render example cube as xmodel preview

* chore: allow origin * in debug

* feat: show preview of xmodels with ModMan

* feat: show images in xmodel preview

* feat: auto load search paths in ModMan

* chore: load objcontainer of loaded zones in ModMan

* chore: add iw4x specific recognized zone dirs

* chore: show when models are loading

* fix: make sure webwindowed handles window and app destruction in correct order

* chore: track and properly free threejs resources

* chore: add skybox for 3d preview

* chore: add small border radius to preview

* fix: linting

* fix: linux compilation

* chore: update package lock
This commit is contained in:
Jan
2026-06-18 18:52:52 +02:00
committed by GitHub
parent 4fd164ee33
commit 85aa7417c4
53 changed files with 2692 additions and 964 deletions
+84
View File
@@ -0,0 +1,84 @@
#include "AutoSearchPaths.h"
#include "IW3/AutoSearchPathsIW3.h"
#include "IW4/AutoSearchPathsIW4.h"
#include "IW5/AutoSearchPathsIW5.h"
#include "T4/AutoSearchPathsT4.h"
#include "T5/AutoSearchPathsT5.h"
#include "T6/AutoSearchPathsT6.h"
#include "Utils/StringUtils.h"
#include <algorithm>
#include <cassert>
#include <filesystem>
#include <optional>
#include <utility>
namespace fs = std::filesystem;
namespace
{
std::optional<std::string> FindGameRootFolder(const std::string& zoneParentPath, const std::vector<std::string>& zoneDirs)
{
std::string lowerZoneParentPath(zoneParentPath);
utils::MakeStringLowerCase(lowerZoneParentPath);
for (const auto& dir : zoneDirs)
{
std::string normalizedDir(dir);
utils::MakeStringLowerCase(normalizedDir);
if (lowerZoneParentPath.ends_with(normalizedDir) && lowerZoneParentPath[lowerZoneParentPath.size() - dir.size() - 1] == '/')
return zoneParentPath.substr(0, zoneParentPath.size() - dir.size() - 1);
}
return std::nullopt;
}
} // namespace
std::vector<std::string> AutoSearchPaths::GetSearchPathsForZonePath(const std::string& zonePath) const
{
auto folderName = fs::absolute(fs::path(zonePath)).parent_path().string();
std::ranges::replace(folderName, '\\', '/');
const auto maybeGameRootFolder = FindGameRootFolder(folderName, RecognizedZoneDirs());
if (!maybeGameRootFolder)
return {folderName};
std::vector<std::string> result;
const fs::path gameRootFolderPath(*maybeGameRootFolder);
for (const auto& dir : RecognizedZoneDirs())
{
auto dirPath = fs::weakly_canonical(gameRootFolderPath / dir);
if (fs::is_directory(dirPath))
result.emplace_back(dirPath.string());
}
for (const auto& dir : AdditionalSearchPaths())
{
auto dirPath = fs::weakly_canonical(gameRootFolderPath / dir);
if (fs::is_directory(dirPath))
result.emplace_back(dirPath.string());
}
return result;
}
AutoSearchPaths* AutoSearchPaths::GetForGame(GameId gameId)
{
static AutoSearchPaths* autoSearchPaths[]{
new AutoSearchPathsIW3(),
new AutoSearchPathsIW4(),
new AutoSearchPathsIW5(),
new AutoSearchPathsT4(),
new AutoSearchPathsT5(),
new AutoSearchPathsT6(),
};
static_assert(std::extent_v<decltype(autoSearchPaths)> == static_cast<unsigned>(GameId::COUNT));
assert(static_cast<unsigned>(gameId) < static_cast<unsigned>(GameId::COUNT));
return autoSearchPaths[std::to_underlying(gameId)];
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include "Game/IGame.h"
#include <string>
#include <vector>
class AutoSearchPaths
{
public:
AutoSearchPaths() = default;
virtual ~AutoSearchPaths() = default;
AutoSearchPaths(const AutoSearchPaths& other) = default;
AutoSearchPaths(AutoSearchPaths&& other) noexcept = default;
AutoSearchPaths& operator=(const AutoSearchPaths& other) = default;
AutoSearchPaths& operator=(AutoSearchPaths&& other) noexcept = default;
std::vector<std::string> GetSearchPathsForZonePath(const std::string& zonePath) const;
static AutoSearchPaths* GetForGame(GameId gameId);
protected:
virtual const std::vector<std::string>& RecognizedZoneDirs() const = 0;
virtual const std::vector<std::string>& AdditionalSearchPaths() const = 0;
};
@@ -0,0 +1,31 @@
#include "AutoSearchPathsIW3.h"
const std::vector<std::string>& AutoSearchPathsIW3::RecognizedZoneDirs() const
{
static std::vector<std::string> recognizedZoneDirs = {
"zone/english",
"zone/french",
"zone/german",
"zone/italian",
"zone/spanish",
"zone/british",
"zone/russian",
"zone/polish",
"zone/korean",
"zone/taiwanese",
"zone/japanese",
"zone/chinese",
"zone/thai",
"zone/leet",
"zone/czech",
};
return recognizedZoneDirs;
}
const std::vector<std::string>& AutoSearchPathsIW3::AdditionalSearchPaths() const
{
static std::vector<std::string> additionalSearchPaths = {
"main",
};
return additionalSearchPaths;
}
@@ -0,0 +1,10 @@
#pragma once
#include "Game/AutoSearchPaths.h"
class AutoSearchPathsIW3 final : public AutoSearchPaths
{
protected:
[[nodiscard]] const std::vector<std::string>& RecognizedZoneDirs() const override;
[[nodiscard]] const std::vector<std::string>& AdditionalSearchPaths() const override;
};
@@ -0,0 +1,37 @@
#include "AutoSearchPathsIW4.h"
const std::vector<std::string>& AutoSearchPathsIW4::RecognizedZoneDirs() const
{
static std::vector<std::string> recognizedZoneDirs = {
"zone/english",
"zone/french",
"zone/german",
"zone/italian",
"zone/spanish",
"zone/british",
"zone/russian",
"zone/polish",
"zone/korean",
"zone/taiwanese",
"zone/japanese",
"zone/chinese",
"zone/thai",
"zone/leet",
"zone/czech",
// Iw4x specific
"zone/patch",
"zone/dlc",
"zone/zonebuilder",
};
return recognizedZoneDirs;
}
const std::vector<std::string>& AutoSearchPathsIW4::AdditionalSearchPaths() const
{
static std::vector<std::string> additionalSearchPaths = {
"main",
"iw4x",
};
return additionalSearchPaths;
}
@@ -0,0 +1,10 @@
#pragma once
#include "Game/AutoSearchPaths.h"
class AutoSearchPathsIW4 final : public AutoSearchPaths
{
protected:
[[nodiscard]] const std::vector<std::string>& RecognizedZoneDirs() const override;
[[nodiscard]] const std::vector<std::string>& AdditionalSearchPaths() const override;
};
@@ -0,0 +1,32 @@
#include "AutoSearchPathsIW5.h"
const std::vector<std::string>& AutoSearchPathsIW5::RecognizedZoneDirs() const
{
static std::vector<std::string> recognizedZoneDirs = {
"zone/english",
"zone/french",
"zone/german",
"zone/italian",
"zone/spanish",
"zone/british",
"zone/russian",
"zone/polish",
"zone/korean",
"zone/taiwanese",
"zone/japanese",
"zone/chinese",
"zone/thai",
"zone/leet",
"zone/czech",
"zone/dlc",
};
return recognizedZoneDirs;
}
const std::vector<std::string>& AutoSearchPathsIW5::AdditionalSearchPaths() const
{
static std::vector<std::string> additionalSearchPaths = {
"main",
};
return additionalSearchPaths;
}
@@ -0,0 +1,10 @@
#pragma once
#include "Game/AutoSearchPaths.h"
class AutoSearchPathsIW5 final : public AutoSearchPaths
{
protected:
[[nodiscard]] const std::vector<std::string>& RecognizedZoneDirs() const override;
[[nodiscard]] const std::vector<std::string>& AdditionalSearchPaths() const override;
};
@@ -0,0 +1,31 @@
#include "AutoSearchPathsT4.h"
const std::vector<std::string>& AutoSearchPathsT4::RecognizedZoneDirs() const
{
static std::vector<std::string> recognizedZoneDirs = {
"zone/English",
"zone/French",
"zone/German",
"zone/Italian",
"zone/Spanish",
"zone/British",
"zone/Russian",
"zone/Polish",
"zone/Korean",
"zone/Taiwanese",
"zone/Japanese",
"zone/Chinese",
"zone/Thai",
"zone/Leet",
"zone/Czech",
};
return recognizedZoneDirs;
}
const std::vector<std::string>& AutoSearchPathsT4::AdditionalSearchPaths() const
{
static std::vector<std::string> additionalSearchPaths = {
"main",
};
return additionalSearchPaths;
}
@@ -0,0 +1,10 @@
#pragma once
#include "Game/AutoSearchPaths.h"
class AutoSearchPathsT4 final : public AutoSearchPaths
{
protected:
[[nodiscard]] const std::vector<std::string>& RecognizedZoneDirs() const override;
[[nodiscard]] const std::vector<std::string>& AdditionalSearchPaths() const override;
};
@@ -0,0 +1,31 @@
#include "AutoSearchPathsT5.h"
const std::vector<std::string>& AutoSearchPathsT5::RecognizedZoneDirs() const
{
static std::vector<std::string> recognizedZoneDirs = {
"zone/Common",
"zone/English",
"zone/French",
"zone/Frenchcan",
"zone/German",
"zone/Austrian",
"zone/Italian",
"zone/Spanish",
"zone/British",
"zone/Russian",
"zone/Polish",
"zone/Korean",
"zone/Japanese",
"zone/Czech",
"zone/Fulljap",
};
return recognizedZoneDirs;
}
const std::vector<std::string>& AutoSearchPathsT5::AdditionalSearchPaths() const
{
static std::vector<std::string> additionalSearchPaths = {
"main",
};
return additionalSearchPaths;
}
@@ -0,0 +1,10 @@
#pragma once
#include "Game/AutoSearchPaths.h"
class AutoSearchPathsT5 final : public AutoSearchPaths
{
protected:
[[nodiscard]] const std::vector<std::string>& RecognizedZoneDirs() const override;
[[nodiscard]] const std::vector<std::string>& AdditionalSearchPaths() const override;
};
@@ -0,0 +1,34 @@
#include "AutoSearchPathsT6.h"
const std::vector<std::string>& AutoSearchPathsT6::RecognizedZoneDirs() const
{
static std::vector<std::string> recognizedZoneDirs = {
"zone/all",
"zone/english",
"zone/french",
"zone/frenchcan",
"zone/german",
"zone/austrian",
"zone/italian",
"zone/spanish",
"zone/british",
"zone/russian",
"zone/polish",
"zone/korean",
"zone/japanese",
"zone/czech",
"zone/fulljap",
"zone/portuguese",
"zone/mexicanspanish",
};
return recognizedZoneDirs;
}
const std::vector<std::string>& AutoSearchPathsT6::AdditionalSearchPaths() const
{
static std::vector<std::string> additionalSearchPaths = {
"main",
"sound",
};
return additionalSearchPaths;
}
@@ -0,0 +1,10 @@
#pragma once
#include "Game/AutoSearchPaths.h"
class AutoSearchPathsT6 final : public AutoSearchPaths
{
protected:
[[nodiscard]] const std::vector<std::string>& RecognizedZoneDirs() const override;
[[nodiscard]] const std::vector<std::string>& AdditionalSearchPaths() const override;
};