Add experimental Assets window

This commit is contained in:
Luke Street 2021-06-03 17:35:56 -04:00
parent 4df59e3e39
commit 2c032ada13
7 changed files with 148 additions and 22 deletions

View File

@ -76,6 +76,7 @@ public:
u32 GetFakeStaticSize() const { return 0; }
void AsyncIdle();
CAssetId GetMLVLId() const { return m_mlvlId; }
const std::vector<SResInfo>& GetResList() const { return x74_resList; }
};
} // namespace metaforce

View File

@ -37,7 +37,22 @@ void ImGuiTextCenter(std::string_view text) {
static std::unordered_map<CAssetId, std::unique_ptr<CDummyWorld>> dummyWorlds;
static std::unordered_map<CAssetId, TCachedToken<CStringTable>> stringTables;
std::string ImGuiLoadStringTable(CAssetId stringId, int idx) {
std::vector<std::string> ImGuiLoadStringTable(CAssetId stringId) {
if (!stringId.IsValid()) {
return {};
}
if (!stringTables.contains(stringId)) {
stringTables[stringId] = g_SimplePool->GetObj(SObjectTag{SBIG('STRG'), stringId});
}
std::vector<std::string> result;
CStringTable* pTable = stringTables[stringId].GetObj();
for (u32 i = 0; i < pTable->GetStringCount(); ++i) {
result.push_back(hecl::Char16ToUTF8(pTable->GetString(s32(i))));
}
return result;
}
std::string ImGuiLoadStringTableIndex(CAssetId stringId, int idx) {
if (!stringId.IsValid()) {
return ""s;
}
@ -71,7 +86,7 @@ static std::vector<std::pair<std::string, CAssetId>> ListWorlds() {
if (!stringId.IsValid()) {
continue;
}
worlds.emplace_back(ImGuiLoadStringTable(stringId, 0), worldId);
worlds.emplace_back(ImGuiLoadStringTableIndex(stringId, 0), worldId);
}
return worlds;
}
@ -88,7 +103,7 @@ static std::vector<std::pair<std::string, TAreaId>> ListAreas(CAssetId worldId)
if (!stringId.IsValid()) {
continue;
}
areas.emplace_back(ImGuiLoadStringTable(stringId, 0), i);
areas.emplace_back(ImGuiLoadStringTableIndex(stringId, 0), i);
}
return areas;
}
@ -783,7 +798,7 @@ void ImGuiConsole::ShowDebugOverlay() {
}
hasPrevious = true;
const std::string name = ImGuiLoadStringTable(g_StateManager->GetWorld()->IGetStringTableAssetId(), 0);
const std::string name = ImGuiLoadStringTableIndex(g_StateManager->GetWorld()->IGetStringTableAssetId(), 0);
ImGuiStringViewText(
fmt::format(FMT_STRING("World Asset ID: 0x{}, Name: {}\n"), g_GameState->CurrentWorldAssetId(), name));
}
@ -810,7 +825,7 @@ void ImGuiConsole::ShowDebugOverlay() {
CAssetId stringId = pArea->IGetStringTableAssetId();
ImGuiStringViewText(
fmt::format(FMT_STRING("Area Asset ID: 0x{}, Name: {}\nArea ID: {}, Active Layer bits: {}\n"),
pArea->GetAreaAssetId(), ImGuiLoadStringTable(stringId, 0), pArea->GetAreaId(), layerBits));
pArea->GetAreaAssetId(), ImGuiLoadStringTableIndex(stringId, 0), pArea->GetAreaId(), layerBits));
}
}
if (m_layerInfo && g_StateManager != nullptr) {
@ -1031,6 +1046,7 @@ void ImGuiConsole::ShowAppMainMenuBar(bool canInspect) {
ImGui::MenuItem("Items", nullptr, &m_showItemsWindow, canInspect && m_developer && m_cheats);
ImGui::MenuItem("Layers", nullptr, &m_showLayersWindow, canInspect && m_developer);
ImGui::MenuItem("Console Variables", nullptr, &m_showConsoleVariablesWindow);
ImGui::MenuItem("Assets", nullptr, &m_showAssetsWindow, canInspect);
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Debug")) {
@ -1152,6 +1168,9 @@ void ImGuiConsole::PreUpdate() {
if (canInspect && m_showLayersWindow) {
ShowLayersWindow();
}
if (canInspect && m_showAssetsWindow) {
ShowAssetsWindow();
}
if (m_showAboutWindow) {
ShowAboutWindow(true);
}
@ -1479,6 +1498,52 @@ void ImGuiConsole::ShowLayersWindow() {
ImGui::End();
}
void ImGuiConsole::ShowAssetsWindow() {
if (ImGui::Begin("Assets", &m_showAssetsWindow)) {
for (const auto& pak : g_ResFactory->GetResLoader()->GetPaks()) {
const std::vector<std::pair<std::string, SObjectTag>>& vector = pak->GetNameList();
const std::string_view pakName = pak->GetPath();
if (ImGui::TreeNode(pakName.data())) {
for (const auto& item : pak->GetResList()) {
// TODO(encounter): store reverse lookup map
const auto found = std::find_if(vector.begin(), vector.end(), [&](const std::pair<std::string, SObjectTag>& v) {
return v.second.id == item.GetId();
});
std::string label;
if (found != vector.end()) {
label = fmt::format(FMT_STRING("0x{:08X}: {} {}"), item.GetId().Value(), item.GetType().toString().c_str(), found->first.c_str());
} else {
label = fmt::format(FMT_STRING("0x{:08X}: {}"), item.GetId().Value(), item.GetType().toString().c_str());
}
ImGui::Selectable(label.c_str()); // TODO(encounter): open separate asset viewer window on click
if (ImGui::IsItemHovered()) {
if (item.GetType() == SBIG('TXTR')) {
ImGui::BeginTooltip();
float imgSize = 128.f * ImGui::GetIO().DisplayFramebufferScale.x;
// TODO(encounter): size based on CTexture dimensions
ImGui::Image(SObjectTag{item.GetType(), item.GetId()}, ImVec2{imgSize, imgSize}, ImVec2{0, 1},
ImVec2{1, 0});
// TODO(encounter): show CTexture information
ImGui::EndTooltip();
} else if (item.GetType() == SBIG('STRG')) {
ImGui::BeginTooltip();
auto vec = ImGuiLoadStringTable(item.GetId());
for (int i = 0; i < vec.size(); ++i) {
// TODO(encounter): parse format strings or use CGuiTextSupport
ImGuiStringViewText(fmt::format(FMT_STRING("{}: {}"), i, vec[i]));
}
ImGui::EndTooltip();
}
// TODO(encounter): preview more types
}
}
ImGui::TreePop();
}
}
}
ImGui::End();
}
void ImGuiConsole::ShowMenuHint() {
if (m_menuHintTime <= 0.f) {
return;

View File

@ -14,7 +14,8 @@
namespace metaforce {
void ImGuiStringViewText(std::string_view text);
void ImGuiTextCenter(std::string_view text);
std::string ImGuiLoadStringTable(CAssetId stringId, int idx);
std::vector<std::string> ImGuiLoadStringTable(CAssetId stringId);
std::string ImGuiLoadStringTableIndex(CAssetId stringId, int idx);
struct ImGuiEntityEntry {
TUniqueId uid = kInvalidUniqueId;
@ -57,6 +58,7 @@ private:
bool m_showItemsWindow = false;
bool m_showLayersWindow = false;
bool m_showConsoleVariablesWindow = false;
bool m_showAssetsWindow = false;
bool m_paused = false;
bool m_stepFrame = false;
@ -102,5 +104,6 @@ private:
void ShowLayersWindow();
void ShowConsoleVariablesWindow();
void ShowMenuHint();
void ShowAssetsWindow();
};
} // namespace metaforce

View File

@ -767,7 +767,7 @@ IMGUI_ENTITY_INSPECT(CScriptDock, CPhysicsActor, ScriptDock, {
auto areaId = dock->GetConnectedAreaId(dock->GetReferenceCount());
if (areaId != kInvalidAreaId) {
CAssetId stringId = g_StateManager->GetWorld()->GetArea(areaId)->IGetStringTableAssetId();
ImGuiStringViewText(fmt::format(FMT_STRING("Connected Area: {}"), ImGuiLoadStringTable(stringId, 0)));
ImGuiStringViewText(fmt::format(FMT_STRING("Connected Area: {}"), ImGuiLoadStringTableIndex(stringId, 0)));
}
}
}

View File

@ -12,6 +12,8 @@
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/Input/CInputGenerator.hpp"
#include "Runtime/CSimplePool.hpp"
#include "Runtime/CToken.hpp"
#include <zeus/CMatrix4f.hpp>
extern "C" const uint8_t NOTO_MONO_FONT[];
@ -33,6 +35,7 @@ static boo::ObjToken<boo::IGraphicsBufferD> IndexBuffer;
static boo::ObjToken<boo::IGraphicsBufferD> UniformBuffer;
static std::array<boo::ObjToken<boo::IShaderDataBinding>, ImGuiUserTextureID_MAX> ShaderDataBindings;
static std::array<boo::ObjToken<boo::ITextureS>, ImGuiUserTextureID_MAX> Textures;
static std::unordered_map<CAssetId, boo::ObjToken<boo::IShaderDataBinding>> ResourceBindings;
static boo::SWindowRect WindowRect;
static std::string IniFilePath;
static std::string LogFilePath;
@ -157,6 +160,7 @@ void ImGuiEngine::Shutdown() {
for (auto& item : Textures) {
item.reset();
}
ResourceBindings.clear();
VertexBuffer.reset();
IndexBuffer.reset();
UniformBuffer.reset();
@ -296,6 +300,22 @@ void ImGuiEngine::End() {
for (int i = 0; i < drawData->CmdListsCount; ++i) {
const auto* cmdList = drawData->CmdLists[i];
for (const auto& drawCmd : cmdList->CmdBuffer) {
const auto& textureId = drawCmd.TextureId;
if (textureId.textureId == ImGuiUserTextureID_MAX && textureId.objectTag.id.IsValid()) {
if (ResourceBindings.contains(textureId.objectTag.id)) {
continue;
}
if (textureId.objectTag.type == SBIG('TXTR')) {
TLockedToken<CTexture> tex = g_SimplePool->GetObj(textureId.objectTag);
// TODO(encounter): store token until loaded, but right now we force it to load immediately
// if (tex.IsLoaded())
ResourceBindings[textureId.objectTag.id] = BuildShaderDataBinding(ctx, tex->GetBooTexture());
} else {
Log.report(logvisor::Fatal, FMT_STRING("Unknown type to render: {}"), textureId.objectTag.type.toString());
}
}
}
int vtxBufferSz = cmdList->VtxBuffer.size();
int idxBufferSz = cmdList->IdxBuffer.size();
memcpy(vtxBuf, cmdList->VtxBuffer.begin(), vtxBufferSz * sizeof(ImDrawVert));
@ -304,6 +324,8 @@ void ImGuiEngine::End() {
idxBuf += idxBufferSz;
}
// TODO(encounter): clean up unreferenced ResourceBindings
VertexBuffer->unmap();
IndexBuffer->unmap();
return true;
@ -324,18 +346,32 @@ void ImGuiEngine::Draw(boo::IGraphicsCommandQueue* gfxQ) {
size_t idxOffset = 0;
size_t vtxOffset = 0;
size_t currentTextureID = ImGuiUserTextureID_MAX;
ImTextureID currentTextureID = ImGuiUserTextureID_MAX;
for (int i = 0; i < drawData->CmdListsCount; ++i) {
const auto* cmdList = drawData->CmdLists[i];
for (const auto& drawCmd : cmdList->CmdBuffer) {
auto* cmdList = drawData->CmdLists[i];
for (auto& drawCmd : cmdList->CmdBuffer) {
if (drawCmd.UserCallback != nullptr) {
if (drawCmd.UserCallback == ImDrawCallback_ResetRenderState) {
gfxQ->setViewport(viewportRect);
} else {
if (drawCmd.UserCallbackData == nullptr) {
drawCmd.UserCallbackData = gfxQ;
}
drawCmd.UserCallback(cmdList, &drawCmd);
}
continue;
}
if (currentTextureID != drawCmd.TextureId) {
currentTextureID = drawCmd.TextureId;
gfxQ->setShaderDataBinding(ShaderDataBindings[currentTextureID]);
}
if (drawCmd.UserCallback != nullptr) {
drawCmd.UserCallback(cmdList, &drawCmd);
if (currentTextureID.textureId == ImGuiUserTextureID_MAX) {
if (!ResourceBindings.contains(currentTextureID.objectTag.id)) {
continue;
}
gfxQ->setShaderDataBinding(ResourceBindings[currentTextureID.objectTag.id]);
} else {
gfxQ->setShaderDataBinding(ShaderDataBindings[currentTextureID.textureId]);
}
}
ImVec2 pos = drawData->DisplayPos;
int clipX = static_cast<int>(drawCmd.ClipRect.x - pos.x);
int clipY = static_cast<int>(drawCmd.ClipRect.y - pos.y);
@ -354,17 +390,23 @@ void ImGuiEngine::Draw(boo::IGraphicsCommandQueue* gfxQ) {
}
}
void ImGuiEngine::BuildShaderDataBindings(boo::IGraphicsDataFactory::Context& ctx) {
boo::ObjToken<boo::IShaderDataBinding> ImGuiEngine::BuildShaderDataBinding(boo::IGraphicsDataFactory::Context& ctx,
boo::ObjToken<boo::ITexture> texture) {
std::array<boo::ObjToken<boo::IGraphicsBuffer>, 1> uniforms = {UniformBuffer.get()};
std::array<boo::PipelineStage, uniforms.size()> unistages = {boo::PipelineStage::Vertex};
std::array<size_t, uniforms.size()> unioffs{0};
std::array<size_t, uniforms.size()> unisizes{sizeof(Uniform)};
std::array<boo::ObjToken<boo::ITexture>, 1> texs{texture};
return ctx.newShaderDataBinding(ShaderPipeline, VertexBuffer.get(), nullptr, IndexBuffer.get(), uniforms.size(),
uniforms.data(), unistages.data(), unioffs.data(), unisizes.data(), texs.size(),
texs.data(), nullptr, nullptr);
}
void ImGuiEngine::BuildShaderDataBindings(boo::IGraphicsDataFactory::Context& ctx) {
for (int i = 0; i < ImGuiUserTextureID_MAX; ++i) {
std::array<boo::ObjToken<boo::ITexture>, 1> texs{Textures[i].get()};
ShaderDataBindings[i] = ctx.newShaderDataBinding(ShaderPipeline, VertexBuffer.get(), nullptr, IndexBuffer.get(),
uniforms.size(), uniforms.data(), unistages.data(), unioffs.data(),
unisizes.data(), texs.size(), texs.data(), nullptr, nullptr);
ShaderDataBindings[i] = BuildShaderDataBinding(ctx, Textures[i].get());
}
ResourceBindings.clear();
}
bool ImGuiWindowCallback::m_mouseCaptured = false;

View File

@ -29,6 +29,9 @@ public:
static void End();
static void Draw(boo::IGraphicsCommandQueue* gfxQ);
static boo::ObjToken<boo::IShaderDataBinding> BuildShaderDataBinding(boo::IGraphicsDataFactory::Context& ctx,
boo::ObjToken<boo::ITexture> texture);
private:
static void BuildShaderDataBindings(boo::IGraphicsDataFactory::Context& ctx);
};

View File

@ -1,5 +1,6 @@
#include <cstdint>
#include "Runtime/RetroTypes.hpp"
#include <zeus/CVector2f.hpp>
#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@ -7,12 +8,23 @@
// Use 32-bit index type for boo
#define ImDrawIdx uint32_t
enum ImUserTextureID {
enum ImUserTextureID : uint8_t {
ImGuiUserTextureID_Atlas,
ImGuiUserTextureID_MetaforceIcon,
ImGuiUserTextureID_MAX,
};
#define ImTextureID ImUserTextureID
struct ImUserTexture {
ImUserTextureID textureId = ImGuiUserTextureID_MAX;
metaforce::SObjectTag objectTag{};
ImUserTexture() noexcept = default;
ImUserTexture(long /* null */) noexcept {}
ImUserTexture(ImUserTextureID textureId) noexcept : textureId(textureId) {}
ImUserTexture(metaforce::SObjectTag objectTag) noexcept : objectTag(objectTag) {}
bool operator==(const ImUserTexture& rhs) const { return textureId == rhs.textureId && objectTag == rhs.objectTag; }
operator intptr_t() const { return (intptr_t(textureId) << 56) | objectTag.id.Value(); }
};
#define ImTextureID ImUserTexture
#define IM_VEC2_CLASS_EXTRA \
ImVec2(const zeus::CVector2f& v) { \