From 702b5f28880f4a5bbcffeeb2d864f93bdd08527e Mon Sep 17 00:00:00 2001 From: paxcut <53811119+paxcut@users.noreply.github.com> Date: Tue, 27 May 2025 09:25:20 -0700 Subject: [PATCH] fix: Problems with textures in 3d visualizer with bitmap visualizer. (#2167) The bitmap visualizer has been simplified considerably. The previous version was designed to work with the TIM format which has some peculiarities that are not general enough. The current implementation has the following specifications. . Whether colors are in a lookup table or part of the image itself they are always 32 bit R8G8B8A8. . If using a color LUT the image then has indices as its element. Indices can have 16(32000 colors), 8 (256 colors) or 4(16 colors) bits each. .For the cases 0f 16 and 8 bits, the data should be an array of N*M elements of the given size where N is the number of rows and M is the number of columns of the image. . For the 4 bit case use an array of N*M/2 bytes so that each column contains two indices. ToDo: Documentation, sample patterns and unit tests. The 3-d visualizer can now handle textures from both the command line or the user interface and things should work as expected. A command line entry will be automatically displayed in the user interface, but changes will be applied immediately as you type or use the file picker. If the user interface text is deleted, then the command line texture will be used again. If a texture is invalid for any reason, then the previous one, if any, will be still in use and an error message will be displayed until the problem is cleared. Valid textures are image files that the stb library can open. --- plugins/visualizers/romfs/lang/en_US.json | 1 + .../content/pl_visualizers/3d_model.cpp | 38 +++++++++++---- .../source/content/pl_visualizers/image.cpp | 47 ++++--------------- 3 files changed, 40 insertions(+), 46 deletions(-) diff --git a/plugins/visualizers/romfs/lang/en_US.json b/plugins/visualizers/romfs/lang/en_US.json index caad55509..b19d77291 100644 --- a/plugins/visualizers/romfs/lang/en_US.json +++ b/plugins/visualizers/romfs/lang/en_US.json @@ -19,6 +19,7 @@ "hex.visualizers.pl_visualizer.3d.error_message_index_count": "Error: Index count must be a multiple of 3", "hex.visualizers.pl_visualizer.3d.error_message_invalid_indices": "Error: Indices must be between 0 and the number of vertices minus one. Invalid indices: ", "hex.visualizers.pl_visualizer.3d.error_message_for_vertex_count": " for {} vertices", + "hex.visualizers.pl_visualizer.3d.error_message_invalid_texture_file": "Error: Invalid texture file", "hex.visualizers.pl_visualizer.3d.specular_brightness": "Specular Brightness", "hex.visualizers.pl_visualizer.3d.object_reflectiveness": "Object Reflectiveness", "hex.visualizers.pl_visualizer.3d.light_color": "Light Color", diff --git a/plugins/visualizers/source/content/pl_visualizers/3d_model.cpp b/plugins/visualizers/source/content/pl_visualizers/3d_model.cpp index d87e86df1..7de45c7f8 100644 --- a/plugins/visualizers/source/content/pl_visualizers/3d_model.cpp +++ b/plugins/visualizers/source/content/pl_visualizers/3d_model.cpp @@ -97,6 +97,7 @@ namespace hex::plugin::visualizers { AutoReset s_texture; std::fs::path s_texturePath; + std::string s_errorMessage=""; u32 s_vertexCount; @@ -602,9 +603,16 @@ namespace hex::plugin::visualizers { // Draw more settings if (ImGui::CollapsingHeader("hex.visualizers.pl_visualizer.3d.more_settings"_lang)) { - if (ImGuiExt::InputFilePicker("hex.visualizers.pl_visualizer.3d.texture_file"_lang, s_texturePath, {})) + if (ImGuiExt::InputFilePicker("hex.visualizers.pl_visualizer.3d.texture_file"_lang, s_texturePath, {}) || ImGui::IsItemDeactivatedAfterEdit()) s_shouldUpdateTexture = true; } + if (s_errorMessage != "") { + auto color = ImGui::GetColorU32(ImVec4(1.0F, 0.0F, 0.0F, 1.0F)); + ImGui::PushStyleColor(ImGuiCol_Text,color); + ImGui::TextUnformatted(s_errorMessage.c_str()); + ImGui::PopStyleColor(); + return; + } } } @@ -798,13 +806,22 @@ namespace hex::plugin::visualizers { shader.setUniform("lightColor", s_lightColor); vertexArray.bind(); - if (s_shouldUpdateTexture) { + if (s_shouldUpdateTexture && !s_texturePath.empty()) { s_shouldUpdateTexture = false; - s_modelTexture = ImGuiExt::Texture::fromImage(s_texturePath, ImGuiExt::Texture::Filter::Nearest); - if (s_modelTexture.isValid()) { - s_drawTexture = true; - } - } + AutoReset tempTexture = ImGuiExt::Texture::fromImage(s_texturePath, ImGuiExt::Texture::Filter::Nearest); + if (tempTexture.isValid()) { + const ImGuiExt::Texture &modelTexture = tempTexture.operator*(); + if (modelTexture.isValid()) { + s_modelTexture = std::move(tempTexture.operator*()); + s_drawTexture = true; + s_errorMessage = ""; + } else + s_errorMessage = "hex.visualizers.pl_visualizer.3d.error_message_invalid_texture_file"_lang.get(); + } else + s_errorMessage = "hex.visualizers.pl_visualizer.3d.error_message_invalid_texture_file"_lang.get(); + + } else if (s_texturePath.empty()) + s_errorMessage = ""; if (s_drawTexture) glBindTexture(GL_TEXTURE_2D, *s_modelTexture); @@ -914,8 +931,11 @@ namespace hex::plugin::visualizers { } } } - - s_texturePath = textureFile; + if (!textureFile.empty() && s_texturePath.empty()) { + s_texturePath = textureFile; + s_shouldUpdateTexture = true; + } else if (!s_texturePath.empty()) + textureFile = s_texturePath.string(); s_drawTexture = !textureFile.empty(); if (shouldReset) s_shouldReset = true; diff --git a/plugins/visualizers/source/content/pl_visualizers/image.cpp b/plugins/visualizers/source/content/pl_visualizers/image.cpp index 0389fc1d3..21fa26b94 100644 --- a/plugins/visualizers/source/content/pl_visualizers/image.cpp +++ b/plugins/visualizers/source/content/pl_visualizers/image.cpp @@ -75,11 +75,11 @@ namespace hex::plugin::visualizers { } } - template ImGuiExt::Texture unmapColors(pl::ptrn::Pattern *colorTablePattern, std::vector& indices, u64 width, u64 height) { - std::vector colorTable = patternToArray(colorTablePattern); + ImGuiExt::Texture getTexture(pl::ptrn::Pattern *colorTablePattern, std::vector& indices, u64 width, u64 height) { + std::vector colorTable = patternToArray(colorTablePattern); auto colorCount = colorTable.size(); auto indexCount = indices.size(); - std::vector image(indexCount); + std::vector image(indexCount); for (u32 i = 0; i < indexCount; i++) { auto index = indices[i]; @@ -90,12 +90,12 @@ namespace hex::plugin::visualizers { } void *tmp = image.data(); ImU8 *data = static_cast(tmp); - ImGuiExt::Texture texture = ImGuiExt::Texture::fromBitmap(data, indexCount*sizeof(T), width, height, ImGuiExt::Texture::Filter::Nearest); + ImGuiExt::Texture texture = ImGuiExt::Texture::fromBitmap(data, indexCount*4, width, height, ImGuiExt::Texture::Filter::Nearest); return texture; } std::vector getIndices(pl::ptrn::Pattern *pattern, u64 width, u64 height) { - auto indexCount = 2 * width * height / pattern->getSize(); + auto indexCount = width * height / pattern->getSize(); std::vector indices; auto *iterable = dynamic_cast(pattern); @@ -113,11 +113,9 @@ namespace hex::plugin::visualizers { } else if (bytePerIndex == 2) { auto temp = patternToArray(pattern); indices = std::vector(temp.begin(), temp.end()); - } else if (bytePerIndex == 4) { - auto temp = patternToArray(pattern); - indices = std::vector(temp.begin(), temp.end()); - } - } else if (indexCount != 0) { + } else // 32 bits indices make no sense. + return indices; + } else if (byteCount != 0) { auto indicesPerByte = indexCount / byteCount; auto temp = patternToArray(pattern); @@ -126,34 +124,9 @@ namespace hex::plugin::visualizers { indices.push_back(temp[i] & 0xF); indices.push_back((temp[i] >> 4) & 0xF); } - } else if (indicesPerByte == 4) { - for (u32 i = 0; i < temp.size(); i++) { - indices.push_back(temp[i] & 0x3); - indices.push_back((temp[i] >> 2) & 0x3); - indices.push_back((temp[i] >> 4) & 0x3); - indices.push_back((temp[i] >> 6) & 0x3); - } - } + } else // 2 bits indices are too little + return indices; } return indices; } - - ImGuiExt::Texture getTexture(pl::ptrn::Pattern *colorTablePattern, std::vector& indices, u64 width, u64 height) { - ImGuiExt::Texture texture; - auto iterable = dynamic_cast(colorTablePattern); - - if (iterable == nullptr || iterable->getEntryCount() <= 0) - return texture; - auto content = iterable->getEntry(0); - auto colorTypeSize = content->getSize()*2; - - if (colorTypeSize == 1) { - texture = unmapColors(colorTablePattern, indices, width, height); - } else if (colorTypeSize == 2) { - texture = unmapColors(colorTablePattern, indices, width, height); - } else if (colorTypeSize == 4) { - texture = unmapColors(colorTablePattern, indices, width, height); - } - return texture; - } }