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
+44 -16
View File
@@ -6,6 +6,10 @@
#include "Web/ViteAssets.h"
#include "Web/WebWindowedLib.h"
// Assets
#include "Asset/Image/DynamicAssetsImage.h"
#include "Asset/XModel/DynamicAssetsXModel.h"
#include <format>
#include <iostream>
#include <string>
@@ -19,7 +23,7 @@ using namespace std::string_literals;
namespace
{
#ifdef _DEBUG
void SpawnDevToolsWindow()
void CreateDevToolsWindow()
{
con::debug("Creating dev tools window");
@@ -31,11 +35,19 @@ namespace
newWindow.set_title("Devtools");
newWindow.set_window_size(640, 480);
newWindow.set_window_min(480, 320);
(void)newWindow.navigate(std::format("http://localhost:{}/__devtools__/", VITE_DEV_SERVER_PORT));
const auto result = newWindow.navigate(std::format("http://localhost:{}/__devtools__/", VITE_DEV_SERVER_PORT));
if (!result.has_value())
con::error("Dev tools window navigation failed: {}", result.error().message());
}
#endif
int SpawnMainWindow()
void RegisterDynamicAssets(webwindowed::asset_handler_plugin& assetHandler)
{
image::RegisterDynamicAssets(assetHandler);
xmodel::RegisterDynamicAssets(assetHandler);
}
int RunModManApp()
{
con::debug("Creating main window");
@@ -48,12 +60,21 @@ namespace
#endif
newWindow.set_title("OpenAssetTools ModMan");
// newWindow.set_window_min(640, 480);
newWindow.set_window_min(640, 480);
newWindow.set_window_size(1280, 640);
const auto assetHandlerPlugin = std::make_shared<webwindowed::asset_handler_plugin>(VITE_ASSETS, std::extent_v<decltype(VITE_ASSETS)>);
const auto assetHandlerPlugin = std::make_shared<webwindowed::asset_handler_plugin>();
assetHandlerPlugin->set_protocol_name("modman");
newWindow.register_plugin(assetHandlerPlugin);
#ifdef _DEBUG
// Allow assets from dev server to access dynamic assets
assetHandlerPlugin->set_allow_all_origins(true);
#endif
for (const auto& asset : VITE_ASSETS)
assetHandlerPlugin->add_static_asset(webwindowed::static_asset(asset.filename, asset.data, asset.dataSize));
RegisterDynamicAssets(*assetHandlerPlugin);
webwindowed::commands_builder commands;
ui::RegisterAllBinds(commands);
@@ -62,23 +83,30 @@ namespace
#ifdef _DEBUG
auto result = newWindow.navigate(VITE_DEV_SERVER ? std::format("http://localhost:{}", VITE_DEV_SERVER_PORT)
: assetHandlerPlugin->get_url_for_asset("index.html"));
if (VITE_DEV_SERVER)
{
newWindow.dispatch(
[]
{
SpawnDevToolsWindow();
});
}
#else
auto result = newWindow.navigate(assetHandlerPlugin->get_url_for_asset("index.html"));
#endif
if (!result.has_value())
con::error("Main window navigation failed: {}", result.error().message());
webwindowed::app app;
app.register_plugin(assetHandlerPlugin);
app.register_plugin(std::make_shared<webwindowed::favicon_handler_plugin>());
app.register_plugin(std::make_shared<webwindowed::title_handler_plugin>());
(void)app.run(context.m_main_window);
#ifdef _DEBUG
if (VITE_DEV_SERVER)
{
CreateDevToolsWindow();
result = app.open_window(context.m_dev_tools_window);
if (!result.has_value())
con::error("Failed to open dev tools window: {}", result.error().message());
}
#endif
result = app.run(context.m_main_window);
if (!result.has_value())
con::error("Error while running app: {}", result.error().message());
return 0;
}
@@ -128,7 +156,7 @@ int main(int argc, const char** argv)
ModManContext::Get().Startup();
const auto result = SpawnMainWindow();
const auto result = RunModManApp();
ModManContext::Get().Destroy();