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

Add ability to reload model textures at runtime #3599

Merged
merged 2 commits into from
Jan 14, 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
2 changes: 2 additions & 0 deletions lib/ivis_opengl/imd.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,6 @@ const WzString &modelName(const iIMDShape *model);

iIMDBaseShape *modelGet(const WzString &filename);

void modelReloadAllModelTextures();

#endif
25 changes: 25 additions & 0 deletions lib/ivis_opengl/imdload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include <string>
#include <unordered_map>
#include <unordered_set>
#include <array>

#include "lib/framework/frame.h"
Expand Down Expand Up @@ -115,6 +116,30 @@ void modelShutdown()
models.clear();
}

void modelReloadAllModelTextures()
{
std::unordered_set<size_t> texPagesToReloadFromDisk;
enumerateLoadedModels([&texPagesToReloadFromDisk](const std::string &modelName, iIMDBaseShape &s){
for (iIMDShape *pDisplayShape = s.displayModel(); pDisplayShape != nullptr; pDisplayShape = pDisplayShape->next.get())
{
const iIMDShapeTextures& textures = pDisplayShape->getTextures();
if (!textures.initialized)
{
continue;
}
std::array<size_t, 4> pages = {textures.texpage, textures.tcmaskpage, textures.normalpage, textures.specularpage};
for (auto page : pages)
{
if (page != iV_TEX_INVALID)
{
texPagesToReloadFromDisk.insert(page);
}
}
}
});
debugReloadTexturesFromDisk(texPagesToReloadFromDisk);
}

void modelUpdateTilesetIdx(size_t tilesetIdx)
{
if (tilesetIdx == currentTilesetIdx)
Expand Down
99 changes: 56 additions & 43 deletions lib/ivis_opengl/tex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@

struct iTexPage
{
std::string name;
std::string filename;
gfx_api::texture* id = nullptr;
gfx_api::texture_type textureType = gfx_api::texture_type::user_interface;

iTexPage() = default;

iTexPage(iTexPage&& input)
{
std::swap(name, input.name);
std::swap(filename, input.filename);
id = input.id;
input.id = nullptr;
std::swap(textureType, input.textureType);
Expand Down Expand Up @@ -94,13 +94,13 @@ size_t pie_NumberOfPages()
}

// Add a new texture page to the list
size_t pie_ReserveTexture(const char *name, const size_t& width, const size_t& height)
size_t pie_ReserveTexture(const char *filename, const size_t& width, const size_t& height)
{
iTexPage tex;
tex.name = name;
tex.filename = filename;
_TEX_PAGE.push_back(std::move(tex));
size_t page = _TEX_PAGE.size() - 1;
_NAME_TO_TEX_PAGE_MAP[name] = page;
_NAME_TO_TEX_PAGE_MAP[filename] = page;
return page;
}

Expand Down Expand Up @@ -133,7 +133,7 @@ size_t pie_AddTexPage(gfx_api::texture *pTexture, const char *filename, gfx_api:
ASSERT(_NAME_TO_TEX_PAGE_MAP.count(filename) == 0, "tex page %s already exists", filename);
iTexPage tex;
size_t page = _TEX_PAGE.size();
tex.name = filename;
tex.filename = filename;
_TEX_PAGE.push_back(std::move(tex));

return pie_AddTexPage_Impl(pTexture, filename, textureType, page);
Expand All @@ -144,26 +144,12 @@ size_t pie_AddTexPage(gfx_api::texture *pTexture, const char *filename, gfx_api:
ASSERT_OR_RETURN(0, pTexture && filename, "Bad input parameter");

// replace
_NAME_TO_TEX_PAGE_MAP.erase(_TEX_PAGE[page].name);
_TEX_PAGE[page].name = filename;
_NAME_TO_TEX_PAGE_MAP.erase(_TEX_PAGE[page].filename);
_TEX_PAGE[page].filename = filename;

return pie_AddTexPage_Impl(pTexture, filename, textureType, page);
}

/*!
* Turns filename into a pagename if possible
* \param[in,out] filename Filename to pagify
*/
std::string pie_MakeTexPageName(const std::string& filename)
{
size_t c = filename.find(iV_TEXNAME_TCSUFFIX);
if (c != std::string::npos)
{
return filename.substr(0, c + 7);
}
return filename;
}

/*!
* Turns page filename into a pagename + tc mask if possible
* \param[in,out] filename Filename to pagify
Expand All @@ -182,6 +168,22 @@ std::string pie_MakeTexPageTCMaskName(const std::string& filename)
return result;
}

gfx_api::texture* loadTextureHandleGraphicsOverrides(const char *filename, gfx_api::texture_type textureType, int maxWidth = -1, int maxHeight = -1)
{
// First, try to load from the current graphics_overrides (if enabled)
std::string loadPath = WZ_CURRENT_GRAPHICS_OVERRIDES_PREFIX "/texpages/";
loadPath += filename;
gfx_api::texture *pTexture = gfx_api::context::get().loadTextureFromFile(loadPath.c_str(), textureType, maxWidth, maxHeight, true);
if (!pTexture)
{
// Try to load it from the regular path
loadPath = "texpages/";
loadPath += filename;
pTexture = gfx_api::context::get().loadTextureFromFile(loadPath.c_str(), textureType, maxWidth, maxHeight);
}
return pTexture;
}

/** Retrieve the texture number for a given texture resource.
*
* @note We keep textures in a separate data structure _TEX_PAGE apart from the
Expand All @@ -200,39 +202,28 @@ optional<size_t> iV_GetTexture(const char *filename, gfx_api::texture_type textu
ASSERT(filename != nullptr, "filename must not be null");

/* Have we already loaded this one then? */
std::string path = pie_MakeTexPageName(filename);
const auto it = _NAME_TO_TEX_PAGE_MAP.find(path);
const auto it = _NAME_TO_TEX_PAGE_MAP.find(filename);
if (it != _NAME_TO_TEX_PAGE_MAP.end())
{
return it->second;
}

// First, try to load from the current graphics_overrides (if enabled)
std::string loadPath = WZ_CURRENT_GRAPHICS_OVERRIDES_PREFIX "/texpages/";
loadPath += filename;
gfx_api::texture *pTexture = gfx_api::context::get().loadTextureFromFile(loadPath.c_str(), textureType, maxWidth, maxHeight, true);
gfx_api::texture *pTexture = loadTextureHandleGraphicsOverrides(filename, textureType, maxWidth, maxHeight);
if (!pTexture)
{
// Try to load it from the regular path
loadPath = "texpages/";
loadPath += filename;
pTexture = gfx_api::context::get().loadTextureFromFile(loadPath.c_str(), textureType, maxWidth, maxHeight);
if (!pTexture)
{
debug(LOG_ERROR, "Failed to load %s", loadPath.c_str());
return nullopt;
}
debug(LOG_ERROR, "Failed to load %s", filename);
return nullopt;
}

size_t page = pie_AddTexPage(pTexture, path.c_str(), textureType);
size_t page = pie_AddTexPage(pTexture, filename, textureType);
resDoResLoadCallback(); // ensure loading screen doesn't freeze when loading large images
return optional<size_t>(page);
}

bool replaceTexture(const WzString &oldfile, const WzString &newfile)
{
// Load new one to replace it
std::string tmpname = pie_MakeTexPageName(oldfile.toUtf8());
std::string tmpname = oldfile.toUtf8();
// Have we already loaded this one?
const auto it = _NAME_TO_TEX_PAGE_MAP.find(tmpname);
if (it != _NAME_TO_TEX_PAGE_MAP.end())
Expand All @@ -241,15 +232,37 @@ bool replaceTexture(const WzString &oldfile, const WzString &newfile)
size_t page = it->second;
gfx_api::texture_type existingTextureType = _TEX_PAGE[page].textureType;
debug(LOG_TEXTURE, "Replacing texture %s with %s from index %zu (tex id %u)", it->first.c_str(), newfile.toUtf8().c_str(), page, _TEX_PAGE[page].id->id());
gfx_api::texture *pTexture = gfx_api::context::get().loadTextureFromFile(WzString("texpages/" + newfile).toUtf8().c_str(), existingTextureType);
tmpname = pie_MakeTexPageName(newfile.toUtf8());
pie_AddTexPage(pTexture, tmpname.c_str(), existingTextureType, page);
return true;
gfx_api::texture *pTexture = loadTextureHandleGraphicsOverrides(newfile.toUtf8().c_str(), existingTextureType);
if (pTexture)
{
tmpname = newfile.toUtf8();
pie_AddTexPage(pTexture, tmpname.c_str(), existingTextureType, page);
return true;
}
}
debug(LOG_TEXTURE, "Nothing to replace - old (not found): %s, new (not used): %s", oldfile.toUtf8().c_str(), newfile.toUtf8().c_str());
return false;
}

bool debugReloadTexturesFromDisk(const std::unordered_set<size_t>& texPages)
{
std::string filename;
for (auto page : texPages)
{
gfx_api::texture_type existingTextureType = _TEX_PAGE[page].textureType;
filename = _TEX_PAGE[page].filename;
debug(LOG_TEXTURE, "Reloading texture %s from index %zu", filename.c_str(), page);
gfx_api::texture *pTexture = loadTextureHandleGraphicsOverrides(filename.c_str(), existingTextureType);
if (!pTexture)
{
debug(LOG_INFO, "Failed to reload texture: %s", filename.c_str());
continue;
}
pie_AddTexPage(pTexture, filename.c_str(), existingTextureType, page);
}
return true;
}

void pie_TexShutDown()
{
// TODO, lazy deletions for faster loading of next level
Expand Down
3 changes: 3 additions & 0 deletions lib/ivis_opengl/tex.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "png_util.h"

#include <functional>
#include <unordered_set>
#include <nonstd/optional.hpp>
using nonstd::optional;
using nonstd::nullopt;
Expand Down Expand Up @@ -55,6 +56,8 @@ void pie_TexInit();
std::string pie_MakeTexPageName(const std::string& filename);
std::string pie_MakeTexPageTCMaskName(const std::string& filename);

bool debugReloadTexturesFromDisk(const std::unordered_set<size_t>& texPages);

//*************************************************************************

void pie_TexShutDown();
Expand Down
9 changes: 7 additions & 2 deletions src/wzscriptdebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -918,14 +918,19 @@ class WzGraphicsPanel : public W_FORM
{
auto panel = std::make_shared<WzGraphicsPanel>();

auto prevButton = panel->createButton(0, "Reload terrain and water textures", [](){
auto prevButton = panel->createButton(0, "Reload terrain & water textures", [](){
loadTerrainTextures(currentMapTileset);
debug(LOG_INFO, "Done");
});
panel->createButton(0, "Reload decal textures", [](){
prevButton = panel->createButton(0, "Reload decals", [](){
reloadTileTextures();
debug(LOG_INFO, "Done");
}, prevButton);
prevButton = panel->createButton(0, "Reload model textures", [](){
debug(LOG_INFO, "Reloading all model textures");
modelReloadAllModelTextures();
debug(LOG_INFO, "Done");
}, prevButton);

prevButton = panel->createButton(1, "Recompile All Shaders", [](){
debug(LOG_INFO, "Recompiling all shader pipelines");
Expand Down
Loading