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:
@@ -1,5 +1,10 @@
|
||||
#include "FastFileContext.h"
|
||||
|
||||
#include "Game/AutoSearchPaths.h"
|
||||
#include "IObjLoader.h"
|
||||
#include "SearchPath/IWD.h"
|
||||
#include "SearchPath/SearchPathFilesystem.h"
|
||||
#include "Utils/StringUtils.h"
|
||||
#include "Web/Binds/ZoneBinds.h"
|
||||
#include "Web/UiCommunication.h"
|
||||
#include "ZoneLoading.h"
|
||||
@@ -36,14 +41,82 @@ namespace
|
||||
std::string m_zone_name;
|
||||
double m_last_progress;
|
||||
};
|
||||
|
||||
std::unique_ptr<ISearchPath> CreateSearchPath(const std::string& searchPathStr)
|
||||
{
|
||||
auto searchPath = std::make_unique<SearchPathFilesystem>(searchPathStr);
|
||||
con::debug("Loaded search path \"{}\"", searchPathStr);
|
||||
|
||||
SearchPaths searchPaths;
|
||||
bool hasIwds = false;
|
||||
|
||||
std::filesystem::directory_iterator iterator(searchPathStr);
|
||||
const auto end = fs::end(iterator);
|
||||
for (auto i = fs::begin(iterator); i != end; ++i)
|
||||
{
|
||||
if (!i->is_regular_file())
|
||||
continue;
|
||||
|
||||
auto extension = i->path().extension().string();
|
||||
utils::MakeStringLowerCase(extension);
|
||||
if (extension == ".iwd")
|
||||
{
|
||||
std::string iwdPath = i->path().string();
|
||||
auto iwd = iwd::LoadFromFile(iwdPath);
|
||||
if (iwd)
|
||||
{
|
||||
if (!hasIwds)
|
||||
{
|
||||
searchPaths.CommitSearchPath(std::move(searchPath));
|
||||
hasIwds = true;
|
||||
}
|
||||
|
||||
searchPaths.CommitSearchPath(std::move(iwd));
|
||||
con::debug("Loaded search path \"{}\"", iwdPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasIwds)
|
||||
return std::make_unique<SearchPaths>(std::move(searchPaths));
|
||||
|
||||
return searchPath;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
LoadedZone::LoadedZone(std::unique_ptr<Zone> zone, std::string filePath)
|
||||
: m_zone(std::move(zone)),
|
||||
m_file_path(std::move(filePath))
|
||||
ContextSearchPath::ContextSearchPath(std::unique_ptr<ISearchPath> searchPath)
|
||||
: m_search_path(std::move(searchPath)),
|
||||
m_ref_count(1)
|
||||
{
|
||||
}
|
||||
|
||||
LoadedZone::LoadedZone(std::unique_ptr<Zone> zone, std::string filePath, std::vector<std::string> searchPaths)
|
||||
: m_zone(std::move(zone)),
|
||||
m_file_path(std::move(filePath)),
|
||||
m_search_paths(std::move(searchPaths))
|
||||
{
|
||||
}
|
||||
|
||||
Zone& LoadedZone::GetZone()
|
||||
{
|
||||
return *m_zone;
|
||||
}
|
||||
|
||||
const Zone& LoadedZone::GetZone() const
|
||||
{
|
||||
return *m_zone;
|
||||
}
|
||||
|
||||
const std::string& LoadedZone::GetFilePath() const
|
||||
{
|
||||
return m_file_path;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& LoadedZone::GetSearchPaths() const
|
||||
{
|
||||
return m_search_paths;
|
||||
}
|
||||
|
||||
void FastFileContext::Destroy()
|
||||
{
|
||||
// Unload all zones
|
||||
@@ -56,36 +129,100 @@ std::expected<LoadedZone*, std::string> FastFileContext::LoadFastFile(const std:
|
||||
if (!zone)
|
||||
return std::unexpected(std::move(zone.error()));
|
||||
|
||||
auto loadedZone = std::make_unique<LoadedZone>(std::move(*zone), path);
|
||||
|
||||
LoadedZone* result;
|
||||
auto searchPathsForZone = AutoSearchPaths::GetForGame((*zone)->m_game_id)->GetSearchPathsForZonePath(path);
|
||||
{
|
||||
std::lock_guard lock(m_zone_lock);
|
||||
result = m_loaded_zones.emplace_back(std::move(loadedZone)).get();
|
||||
std::lock_guard lock(m_search_path_lock);
|
||||
for (const auto& searchPathStr : searchPathsForZone)
|
||||
{
|
||||
const auto existingSearchPath = m_context_search_paths.find(searchPathStr);
|
||||
if (existingSearchPath == m_context_search_paths.end())
|
||||
{
|
||||
auto searchPath = CreateSearchPath(searchPathStr);
|
||||
m_search_paths.IncludeSearchPath(searchPath.get());
|
||||
m_context_search_paths.emplace(searchPathStr, std::make_unique<ContextSearchPath>(std::move(searchPath)));
|
||||
}
|
||||
else
|
||||
{
|
||||
existingSearchPath->second->m_ref_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui::NotifyZoneLoaded(*result);
|
||||
auto loadedZone = std::make_unique<LoadedZone>(std::move(*zone), path, std::move(searchPathsForZone));
|
||||
|
||||
return result;
|
||||
LoadedZone* loadedZonePtr;
|
||||
{
|
||||
std::lock_guard lock(m_zone_lock);
|
||||
loadedZonePtr = m_loaded_zones.emplace_back(std::move(loadedZone)).get();
|
||||
}
|
||||
|
||||
{
|
||||
std::shared_lock lock(m_search_path_lock);
|
||||
IObjLoader::GetObjLoaderForGame(loadedZonePtr->GetZone().m_game_id)->LoadReferencedContainersForZone(m_search_paths, loadedZonePtr->GetZone());
|
||||
}
|
||||
|
||||
ui::NotifyZoneLoaded(*loadedZonePtr);
|
||||
|
||||
return loadedZonePtr;
|
||||
}
|
||||
|
||||
std::expected<void, std::string> FastFileContext::UnloadZone(const std::string& zoneName)
|
||||
{
|
||||
std::unique_ptr<LoadedZone> removedLoadedZone;
|
||||
|
||||
{
|
||||
std::lock_guard lock(m_zone_lock);
|
||||
const auto existingZone = std::ranges::find_if(m_loaded_zones,
|
||||
[&zoneName](const std::unique_ptr<LoadedZone>& loadedZone)
|
||||
{
|
||||
return loadedZone->m_zone->m_name == zoneName;
|
||||
return loadedZone->GetZone().m_name == zoneName;
|
||||
});
|
||||
|
||||
if (existingZone != m_loaded_zones.end())
|
||||
if (existingZone == m_loaded_zones.end())
|
||||
return std::unexpected(std::format("No zone with name {} loaded", zoneName));
|
||||
|
||||
removedLoadedZone = std::move(*existingZone);
|
||||
m_loaded_zones.erase(existingZone);
|
||||
|
||||
ui::NotifyZoneUnloaded(zoneName);
|
||||
}
|
||||
|
||||
assert(removedLoadedZone);
|
||||
|
||||
{
|
||||
std::shared_lock lock(m_search_path_lock);
|
||||
IObjLoader::GetObjLoaderForGame(removedLoadedZone->GetZone().m_game_id)->UnloadContainersOfZone(removedLoadedZone->GetZone());
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lock(m_search_path_lock);
|
||||
for (const auto& searchPathStr : removedLoadedZone->GetSearchPaths())
|
||||
{
|
||||
m_loaded_zones.erase(existingZone);
|
||||
ui::NotifyZoneUnloaded(zoneName);
|
||||
return {};
|
||||
const auto existingSearchPath = m_context_search_paths.find(searchPathStr);
|
||||
if (existingSearchPath != m_context_search_paths.end())
|
||||
{
|
||||
assert(existingSearchPath->second->m_ref_count > 0);
|
||||
const auto newRefCount = --existingSearchPath->second->m_ref_count;
|
||||
|
||||
if (newRefCount == 0)
|
||||
{
|
||||
m_search_paths.RemoveSearchPath(existingSearchPath->second->m_search_path.get());
|
||||
m_context_search_paths.erase(existingSearchPath);
|
||||
con::debug("Unloaded search path \"{}\"", searchPathStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::unexpected(std::format("No zone with name {} loaded", zoneName));
|
||||
return {};
|
||||
}
|
||||
|
||||
ReadAccess<const std::vector<std::unique_ptr<LoadedZone>>> FastFileContext::GetLoadedZones()
|
||||
{
|
||||
return ReadAccess<const std::vector<std::unique_ptr<LoadedZone>>>(std::shared_lock(m_zone_lock), m_loaded_zones);
|
||||
}
|
||||
|
||||
ReadAccess<ISearchPath> FastFileContext::GetSearchPaths()
|
||||
{
|
||||
return ReadAccess<ISearchPath>(std::shared_lock(m_search_path_lock), m_search_paths);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "SearchPath/SearchPaths.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class ContextSearchPath
|
||||
{
|
||||
public:
|
||||
explicit ContextSearchPath(std::unique_ptr<ISearchPath> searchPath);
|
||||
|
||||
std::unique_ptr<ISearchPath> m_search_path;
|
||||
unsigned m_ref_count;
|
||||
};
|
||||
|
||||
class LoadedZone
|
||||
{
|
||||
public:
|
||||
LoadedZone(std::unique_ptr<Zone> zone, std::string filePath, std::vector<std::string> searchPaths);
|
||||
|
||||
[[nodiscard]] Zone& GetZone();
|
||||
[[nodiscard]] const Zone& GetZone() const;
|
||||
|
||||
[[nodiscard]] const std::string& GetFilePath() const;
|
||||
[[nodiscard]] const std::vector<std::string>& GetSearchPaths() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Zone> m_zone;
|
||||
std::string m_file_path;
|
||||
std::vector<std::string> m_search_paths;
|
||||
};
|
||||
|
||||
LoadedZone(std::unique_ptr<Zone> zone, std::string filePath);
|
||||
template<class T> class ReadAccess
|
||||
{
|
||||
public:
|
||||
ReadAccess(std::shared_lock<std::shared_mutex> lock, T& data)
|
||||
: m_read_lock(std::move(lock)),
|
||||
m_data(data)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] T& Data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_lock<std::shared_mutex> m_read_lock;
|
||||
T& m_data;
|
||||
};
|
||||
|
||||
class FastFileContext
|
||||
@@ -24,6 +63,14 @@ public:
|
||||
std::expected<LoadedZone*, std::string> LoadFastFile(const std::string& path);
|
||||
std::expected<void, std::string> UnloadZone(const std::string& zoneName);
|
||||
|
||||
ReadAccess<const std::vector<std::unique_ptr<LoadedZone>>> GetLoadedZones();
|
||||
ReadAccess<ISearchPath> GetSearchPaths();
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<LoadedZone>> m_loaded_zones;
|
||||
std::shared_mutex m_zone_lock;
|
||||
|
||||
SearchPaths m_search_paths;
|
||||
std::unordered_map<std::string, std::unique_ptr<ContextSearchPath>> m_context_search_paths;
|
||||
std::shared_mutex m_search_path_lock;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user