1
0
mirror of synced 2025-02-01 04:05:52 +01:00

impr: Vastly improved 3D Visualizer (#1456)

Based entirely on @paxcut's amazing PR #1443

---------

Co-authored-by: paxcut <paxcut@outlook.com>
Co-authored-by: paxcut <53811119+paxcut@users.noreply.github.com>
This commit is contained in:
Nik 2023-12-05 10:49:51 +01:00 committed by GitHub
parent d5a40d46bc
commit f9a9ed4846
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 3122 additions and 781 deletions

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <hex/helpers/concepts.hpp> #include <hex/helpers/concepts.hpp>
#include <hex/helpers/fs.hpp>
#include <wolv/utils/string.hpp> #include <wolv/utils/string.hpp>
@ -73,6 +74,7 @@ namespace ImGuiExt {
Texture(const ImU8 *buffer, int size, int width = 0, int height = 0); Texture(const ImU8 *buffer, int size, int width = 0, int height = 0);
Texture(std::span<const std::byte> bytes, int width = 0, int height = 0); Texture(std::span<const std::byte> bytes, int width = 0, int height = 0);
explicit Texture(const char *path); explicit Texture(const char *path);
explicit Texture(const std::fs::path &path);
Texture(unsigned int texture, int width, int height); Texture(unsigned int texture, int width, int height);
Texture(const Texture&) = delete; Texture(const Texture&) = delete;
Texture(Texture&& other) noexcept; Texture(Texture&& other) noexcept;
@ -86,10 +88,14 @@ namespace ImGuiExt {
return this->m_textureId != nullptr; return this->m_textureId != nullptr;
} }
[[nodiscard]] constexpr operator ImTextureID() const noexcept { [[nodiscard]] operator ImTextureID() const noexcept {
return this->m_textureId; return this->m_textureId;
} }
[[nodiscard]] operator intptr_t() const noexcept {
return reinterpret_cast<intptr_t>(this->m_textureId);
}
[[nodiscard]] auto getSize() const noexcept { [[nodiscard]] auto getSize() const noexcept {
return ImVec2(this->m_width, this->m_height); return ImVec2(this->m_width, this->m_height);
} }
@ -122,7 +128,7 @@ namespace ImGuiExt {
void Header(const char *label, bool firstEntry = false); void Header(const char *label, bool firstEntry = false);
void HeaderColored(const char *label, ImColor color, bool firstEntry); void HeaderColored(const char *label, ImColor color, bool firstEntry);
bool InfoTooltip(const char *text = ""); bool InfoTooltip(const char *text = "",bool = false);
bool TitleBarButton(const char *label, ImVec2 size_arg); bool TitleBarButton(const char *label, ImVec2 size_arg);
bool ToolBarButton(const char *symbol, ImVec4 color); bool ToolBarButton(const char *symbol, ImVec4 color);
@ -258,6 +264,7 @@ namespace ImGuiExt {
bool DimmedIconButton(const char *symbol, ImVec4 color, ImVec2 size = ImVec2(0, 0)); bool DimmedIconButton(const char *symbol, ImVec4 color, ImVec2 size = ImVec2(0, 0));
bool DimmedButtonToggle(const char *icon, bool *v, ImVec2 size); bool DimmedButtonToggle(const char *icon, bool *v, ImVec2 size);
bool DimmedIconToggle(const char *icon, bool *v); bool DimmedIconToggle(const char *icon, bool *v);
bool DimmedIconToggle(const char *iconOn, const char *iconOff, bool *v);
void TextOverlay(const char *text, ImVec2 pos); void TextOverlay(const char *text, ImVec2 pos);
@ -277,6 +284,11 @@ namespace ImGuiExt {
if (ImGui::Button(textRight, ImVec2(width / 3, 0))) if (ImGui::Button(textRight, ImVec2(width / 3, 0)))
rightButtonCallback(); rightButtonCallback();
} }
bool VSliderAngle(const char* label, ImVec2& size, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags);
bool InputFilePicker(const char *label, std::fs::path &path, const std::vector<hex::fs::ItemFilter> &validExtensions);
template<typename T> template<typename T>
constexpr ImGuiDataType getImGuiDataType() { constexpr ImGuiDataType getImGuiDataType() {
if constexpr (std::same_as<T, u8>) return ImGuiDataType_U8; if constexpr (std::same_as<T, u8>) return ImGuiDataType_U8;

View File

@ -6,9 +6,60 @@
#include <wolv/utils/guards.hpp> #include <wolv/utils/guards.hpp>
#include <numbers>
namespace hex::gl { namespace hex::gl {
Matrix<float,4,4> GetOrthographicMatrix( float viewWidth, float viewHeight, float nearVal,float farVal, bool actionType)
{
int sign =1;
if (actionType)
sign=-1;
//float left = leftRight.x;
//float right = leftRight.y;
//float down = upDown.x;
//float up = upDown.y;
Matrix<float,4,4> result(0);
//result.updateElement(0,0,sign /(right-left))
result.updateElement(0,0,sign / viewWidth);
//result.updateElement(0,3,sign * (right + left)/(right - left));
//result.updateElement(1,1, sign /(up-down));
result.updateElement(1,1, sign / viewHeight);
//result.updateElement(1,3, sign * (up + down)/(up - down));
result.updateElement(2,2,-sign * 2/(farVal - nearVal));
result.updateElement(3,2,-sign * (farVal + nearVal)/(farVal - nearVal));
result.updateElement(3,3,sign);
return result;
}
Matrix<float,4,4> GetPerspectiveMatrix( float viewWidth, float viewHeight, float nearVal,float farVal, bool actionType)
{
int sign =1;
if (actionType)
sign=-1;
//float left = leftRight.x;
//float right = leftRight.y;
//float down = upDown.x;
//float up = upDown.y;
//T aspect=(right-left)/(top-bottom);
//T f = nearVal/top;
Matrix<float,4,4> result(0);
// T f = 1.0 / tan(fovy / 2.0); tan(fovy / 2.0) = top / near; fovy = 2 * atan2(top,near)
//result.updateElement(0,0,sign * nearVal/(right-left));
//result.updateElement(1,1, sign * nearVal/(up-down));
result.updateElement(0,0,sign * nearVal/viewWidth);
result.updateElement(1,1, sign * nearVal/viewHeight);
result.updateElement(2,2,-sign * (farVal + nearVal)/(farVal - nearVal));
result.updateElement(3,2,-sign * 2*farVal*nearVal/(farVal - nearVal));
result.updateElement(2,3,-sign);
return result;
}
Shader::Shader(std::string_view vertexSource, std::string_view fragmentSource) { Shader::Shader(std::string_view vertexSource, std::string_view fragmentSource) {
auto vertexShader = glCreateShader(GL_VERTEX_SHADER); auto vertexShader = glCreateShader(GL_VERTEX_SHADER);
this->compile(vertexShader, vertexSource); this->compile(vertexShader, vertexSource);
@ -57,13 +108,14 @@ namespace hex::gl {
glUseProgram(0); glUseProgram(0);
} }
void Shader::setUniform(std::string_view name, const int &value) {
glUniform1i(getUniformLocation(name), value);
}
void Shader::setUniform(std::string_view name, const float &value) { void Shader::setUniform(std::string_view name, const float &value) {
glUniform1f(getUniformLocation(name), value); glUniform1f(getUniformLocation(name), value);
} }
void Shader::setUniform(std::string_view name, const Vector<float, 3> &value) {
glUniform3f(getUniformLocation(name), value[0], value[1], value[2]);
}
GLint Shader::getUniformLocation(std::string_view name) { GLint Shader::getUniformLocation(std::string_view name) {
auto uniform = this->m_uniforms.find(name.data()); auto uniform = this->m_uniforms.find(name.data());
@ -81,7 +133,7 @@ namespace hex::gl {
return uniform->second; return uniform->second;
} }
void Shader::compile(GLuint shader, std::string_view source) { void Shader::compile(GLuint shader, std::string_view source) const {
auto sourcePtr = source.data(); auto sourcePtr = source.data();
glShaderSource(shader, 1, &sourcePtr, nullptr); glShaderSource(shader, 1, &sourcePtr, nullptr);
@ -98,7 +150,7 @@ namespace hex::gl {
template<typename T> template<typename T>
Buffer<T>::Buffer(BufferType type, std::span<T> data) : m_size(data.size()), m_type(GLuint(type)) { Buffer<T>::Buffer(BufferType type, std::span<const T> data) : m_size(data.size()), m_type(GLuint(type)) {
glGenBuffers(1, &this->m_buffer); glGenBuffers(1, &this->m_buffer);
glBindBuffer(this->m_type, this->m_buffer); glBindBuffer(this->m_type, this->m_buffer);
glBufferData(this->m_type, data.size_bytes(), data.data(), GL_STATIC_DRAW); glBufferData(this->m_type, data.size_bytes(), data.data(), GL_STATIC_DRAW);
@ -143,20 +195,28 @@ namespace hex::gl {
} }
template<typename T> template<typename T>
void Buffer<T>::draw() const { void Buffer<T>::draw(unsigned primitive) const {
switch (this->m_type) { switch (this->m_type) {
case GL_ARRAY_BUFFER: case GL_ARRAY_BUFFER:
glDrawArrays(GL_TRIANGLES, 0, this->m_size); glDrawArrays(primitive, 0, this->m_size);
break; break;
case GL_ELEMENT_ARRAY_BUFFER: case GL_ELEMENT_ARRAY_BUFFER:
glDrawElements(GL_TRIANGLES, this->m_size, impl::getType<T>(), nullptr); glDrawElements(primitive, this->m_size, impl::getType<T>(), nullptr);
break; break;
} }
} }
template<typename T>
void Buffer<T>::update(std::span<const T> data) {
glBindBuffer(this->m_type, this->m_buffer);
glBufferSubData(this->m_type, 0, data.size_bytes(), data.data());
glBindBuffer(this->m_type, 0);
}
template class Buffer<float>; template class Buffer<float>;
template class Buffer<u32>; template class Buffer<u32>;
template class Buffer<u16>;
template class Buffer<u8>;
VertexArray::VertexArray() { VertexArray::VertexArray() {
glGenVertexArrays(1, &this->m_array); glGenVertexArrays(1, &this->m_array);
@ -244,14 +304,13 @@ namespace hex::gl {
return copy; return copy;
} }
FrameBuffer::FrameBuffer(u32 width, u32 height) {
FrameBuffer::FrameBuffer() {
glGenFramebuffers(1, &this->m_frameBuffer); glGenFramebuffers(1, &this->m_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, this->m_frameBuffer); glBindFramebuffer(GL_FRAMEBUFFER, this->m_frameBuffer);
glGenRenderbuffers(1, &this->m_renderBuffer); glGenRenderbuffers(1, &this->m_renderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, this->m_renderBuffer); glBindRenderbuffer(GL_RENDERBUFFER, this->m_renderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 1280, 720); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, this->m_renderBuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, this->m_renderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, 0); glBindRenderbuffer(GL_RENDERBUFFER, 0);
@ -295,4 +354,335 @@ namespace hex::gl {
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
} AxesVectors::AxesVectors() {
m_vertices.resize(36);
m_colors.resize(48);
m_indices.resize(18);
// Vertices are x,y,z. Colors are RGBA. Indices are for the ends of each segment
// Entries with value zero are unneeded but kept to help keep track of location
// x-axis
//vertices[0]=0.0F; vertices[1]= 0.0F vertices[2] = 0.0F; // shaft base
m_vertices[3] = 1.0F;//vertices[4]= 0.0F vertices[5] = 0.0F; // shaft tip
m_vertices[6] = 0.9F; m_vertices[8] = 0.05F; // arrow base
m_vertices[9] = 0.9F; m_vertices[11]=-0.05F; // arrow base
// y-axis
//vertices[12]=0.0F; vertices[13] = 0.0F; vertices[14]=0.0F;// shaft base
m_vertices[16] = 1.0F;//vertices[17]=0.0F;// shaft tip
m_vertices[18] = 0.05F; m_vertices[19] = 0.9F;//vertices[20]=0.0F;// arrow base
m_vertices[21] =-0.05F; m_vertices[22] = 0.9F;//vertices[23]=0.0F;// arrow base
// z-axis
//vertices[24]=0.0F; vertices[25]=0.0F vertices[26] = 0.0F; // shaft base
m_vertices[29] = 1.0F; // shaft tip
m_vertices[30] = 0.05F; m_vertices[32] = 0.9F; // arrow base
m_vertices[33] =-0.05F; m_vertices[35] = 0.9F; // arrow base
// x-axis colors
m_colors[0] = 0.7F; m_colors[3] = 1.0F;
m_colors[4] = 0.7F; m_colors[7] = 1.0F;
m_colors[8] = 0.7F; m_colors[11] = 1.0F;
m_colors[12] = 0.7F; m_colors[15] = 1.0F;
// y-axis colors
m_colors[17] = 0.7F; m_colors[19] = 1.0F;
m_colors[21] = 0.7F; m_colors[23] = 1.0F;
m_colors[25] = 0.7F; m_colors[27] = 1.0F;
m_colors[29] = 0.7F; m_colors[31] = 1.0F;
// z-axis colors
m_colors[34] = 0.7F; m_colors[35] = 1.0F;
m_colors[38] = 0.7F; m_colors[39] = 1.0F;
m_colors[42] = 0.7F; m_colors[43] = 1.0F;
m_colors[46] = 0.7F; m_colors[47] = 1.0F;
// indices for x
m_indices[0] = 0; m_indices[1] = 1;
m_indices[2] = 2; m_indices[3] = 1;
m_indices[4] = 3; m_indices[5] = 1;
// indices for y
m_indices[6] = 4; m_indices[7] = 5;
m_indices[8] = 6; m_indices[9] = 5;
m_indices[10] = 7; m_indices[11] = 5;
// indices for z
m_indices[12] = 8; m_indices[13] = 9;
m_indices[14] = 10; m_indices[15] = 9;
m_indices[16] = 11; m_indices[17] = 9;
}
AxesBuffers::AxesBuffers(const VertexArray& axesVertexArray, const AxesVectors &axesVectors) {
m_vertices = {};
m_colors = {};
m_indices = {};
axesVertexArray.bind();
m_vertices = gl::Buffer<float>(gl::BufferType::Vertex, axesVectors.getVertices());
m_colors = gl::Buffer<float>(gl::BufferType::Vertex, axesVectors.getColors());
m_indices = gl::Buffer<u8>(gl::BufferType::Index, axesVectors.getIndices());
axesVertexArray.addBuffer(0, m_vertices);
axesVertexArray.addBuffer(1, m_colors, 4);
m_vertices.unbind();
m_colors.unbind();
m_indices.unbind();
axesVertexArray.unbind();
}
GridVectors::GridVectors(int sliceCount) {
m_slices = sliceCount;
m_vertices.resize((m_slices + 1) * (m_slices + 1) * 3);
m_colors.resize((m_slices + 1) * (m_slices + 1) * 4);
m_indices.resize(m_slices * m_slices * 6 + m_slices * 2);
int k = 0;
int l = 0;
for (u32 j = 0; j <= m_slices; ++j) {
float z = 2.0f * float(j) / float(m_slices) - 1.0f;
for (u32 i = 0; i <= m_slices; ++i) {
m_vertices[k ] = 2.0f * float(i) / float(m_slices) - 1.0f;
m_vertices[k + 1] = 0.0f;
m_vertices[k + 2] = z;
k += 3;
m_colors[l ] = 0.5f;
m_colors[l + 1] = 0.5f;
m_colors[l + 2] = 0.5f;
m_colors[l + 3] = 0.3f;
l += 4;
}
}
k = 0;
for (u32 j = 0; j < m_slices; ++j) {
int row1 = j * (m_slices + 1);
int row2 = (j + 1) * (m_slices + 1);
for (u32 i = 0; i < m_slices; ++i) {
m_indices[k ] = row1 + i;
m_indices[k + 1] = row1 + i + 1;
m_indices[k + 2] = row1 + i + 1;
m_indices[k + 3] = row2 + i + 1;
m_indices[k + 4] = row2 + i + 1;
m_indices[k + 5] = row2 + i;
k += 6;
if (i == 0) {
m_indices[k ] = row2 + i;
m_indices[k + 1] = row1 + i;
k += 2;
}
}
}
}
GridBuffers::GridBuffers(const VertexArray& gridVertexArray, const GridVectors &gridVectors) {
m_vertices = {};
m_colors = {};
m_indices = {};
gridVertexArray.bind();
m_vertices = gl::Buffer<float>(gl::BufferType::Vertex, gridVectors.getVertices());
m_indices = gl::Buffer<u8>(gl::BufferType::Index, gridVectors.getIndices());
m_colors = gl::Buffer<float>(gl::BufferType::Vertex, gridVectors.getColors());
gridVertexArray.addBuffer(0, m_vertices);
gridVertexArray.addBuffer(1, m_colors,4);
m_vertices.unbind();
m_colors.unbind();
m_indices.unbind();
gridVertexArray.unbind();
}
hex::gl::LightSourceVectors::LightSourceVectors(int res) {
m_resolution = res;
auto res_sq = m_resolution * m_resolution;
m_radius = 0.05f;
m_vertices.resize((res_sq + 2) * 3);
m_normals.resize((res_sq + 2) * 3);
m_colors.resize((res_sq + 2) * 4);
m_indices.resize(res_sq * 6);
constexpr auto TwoPi = std::numbers::pi_v<float> * 2.0F;
constexpr auto HalfPi = std::numbers::pi_v<float> / 2.0F;
const auto dv = TwoPi / m_resolution;
const auto du = std::numbers::pi_v<float> / (m_resolution + 1);
m_normals[0] = 0;
m_normals[1] = 0;
m_normals[2] = 1;
m_vertices[0] = 0;
m_vertices[1] = 0;
m_vertices[2] = m_radius;
m_colors[0] = 1.0;
m_colors[1] = 1.0;
m_colors[2] = 1.0;
m_colors[3] = 1.0;
// Vertical: pi/2 to -pi/2
for (int i = 0; i < m_resolution; i += 1) {
float u = HalfPi - (i + 1) * du;
float z = std::sin(u);
float xy = std::cos(u);
// Horizontal: 0 to 2pi
for (int j = 0; j < m_resolution; j += 1) {
float v = j * dv;
float x = xy * std::cos(v);
float y = xy * std::sin(v);
i32 n = (i * m_resolution + j + 1) * 3;
m_normals[n] = x;
m_normals[n + 1] = y;
m_normals[n + 2] = z;
m_vertices[n] = m_radius * x;
m_vertices[n + 1] = m_radius * y;
m_vertices[n + 2] = m_radius * z;
n = (i * m_resolution + j + 1) * 4;
m_colors[n] = 1.0f;
m_colors[n + 1] = 1.0f;
m_colors[n + 2] = 1.0f;
m_colors[n + 3] = 1.0f;
}
}
i32 n = ((res_sq + 1) * 3);
m_normals[n ] = 0;
m_normals[n + 1] = 0;
m_normals[n + 2] = -1;
m_vertices[n ] = 0;
m_vertices[n + 1] = 0;
m_vertices[n + 2] = -m_radius;
n = ((res_sq + 1) * 4);
m_colors[n ] = 1.0;
m_colors[n + 1] = 1.0;
m_colors[n + 2] = 1.0;
m_colors[n + 3] = 1.0;
// that was the easy part, indices are a bit more complicated
// and may need some explaining. The RxR grid slices the globe
// into longitudes which are the vertical slices and latitudes
// which are the horizontal slices. The latitudes are all full
// circles except for the poles, so we don't count them as part
// of the grid. That means that there are R+2 latitudes and R
// longitudes.Between consecutive latitudes we have 2*R triangles.
// Since we have R true latitudes there are R-1 spaces between them so
// between the top and the bottom we have 2*R*(R-1) triangles.
// the top and bottom have R triangles each, so we have a total of
// 2*R*(R-1) + 2*R = 2*R*R triangles. Each triangle has 3 vertices,
// so we have 6*R*R indices.
// The North Pole is index 0 and the South Pole is index 6*res*res -1
// The first row of vertices is 1 to res, the second row is res+1 to 2*res etc.
// First, the North Pole
for (int i = 0; i < m_resolution; i += 1) {
m_indices[i * 3] = 0;
m_indices[i * 3 + 1] = i + 1;
if (i == m_resolution - 1)
m_indices[i * 3 + 2] = 1;
else
m_indices[i * 3 + 2] = (i + 2);
}
// Now the spaces between true latitudes
for (int i = 0; i < m_resolution - 1; i += 1) {
// k is the index of the first vertex of the i-th latitude
i32 k = i * m_resolution + 1;
// When we go a full circle we need to connect the last vertex to the first, so
// we do R-1 first because their indices can be computed easily
for (int j = 0; j < m_resolution - 1; j += 1) {
// We store the indices of the array where the vertices were store
// in the triplets that make the triangles. These triplets are stored in
// an array that has indices itself which can be confusing.
// l keeps track of the indices of the array that stores the triplets
// each i brings 6R and each j 6. 3R from the North Pole.
i32 l = (i * m_resolution + j) * 6 + 3 * m_resolution;
m_indices[l ] = k + j;
m_indices[l + 1] = k + j + m_resolution + 1;
m_indices[l + 2] = k + j + 1;
m_indices[l + 3] = k + j;
m_indices[l + 4] = k + j + m_resolution;
m_indices[l + 5] = k + j + m_resolution + 1;
}
// Now the last vertex of the i-th latitude is connected to the first
i32 l = (( i + 1) * m_resolution - 1) * 6 + 3 * m_resolution;
m_indices[l ] = k + m_resolution - 1;
m_indices[l + 1] = k + m_resolution;
m_indices[l + 2] = k;
m_indices[l + 3] = k + m_resolution - 1;
m_indices[l + 4] = k + 2 * m_resolution - 1;
m_indices[l + 5] = k + m_resolution;
}
// Now the South Pole
i32 k = (m_resolution-1) * m_resolution + 1;
i32 l = 3 * m_resolution * ( 2 * m_resolution - 1);
for (int i = 0; i < m_resolution; i += 1) {
if (i == m_resolution -1)
m_indices[l + i * 3] = k;
else
m_indices[l + i * 3] = k + i + 1;
m_indices[l + i * 3 + 1] = k + i;
m_indices[l + i * 3 + 2] = k + m_resolution;
}
}
void LightSourceVectors::moveTo(const Vector<float, 3> &positionVector) {
auto vertexCount = m_vertices.size();
for (unsigned k = 0; k < vertexCount; k += 3) {
m_vertices[k ] = m_radius * m_normals[k ] + positionVector[0];
m_vertices[k + 1] = m_radius * m_normals[k + 1] + positionVector[1];
m_vertices[k + 2] = m_radius * m_normals[k + 2] + positionVector[2];
}
}
LightSourceBuffers::LightSourceBuffers(const VertexArray &sourceVertexArray, const LightSourceVectors &sourceVectors) {
sourceVertexArray.bind();
m_vertices = gl::Buffer<float>(gl::BufferType::Vertex, sourceVectors.getVertices());
m_indices = gl::Buffer<u16>(gl::BufferType::Index, sourceVectors.getIndices());
m_normals = gl::Buffer<float>(gl::BufferType::Vertex, sourceVectors.getNormals());
m_colors = gl::Buffer<float>(gl::BufferType::Vertex, sourceVectors.getColors());
sourceVertexArray.addBuffer(0, m_vertices);
sourceVertexArray.addBuffer(1, m_normals);
sourceVertexArray.addBuffer(2, m_colors, 4);
m_vertices.unbind();
m_normals.unbind();
m_colors.unbind();
m_indices.unbind();
sourceVertexArray.unbind();
}
void LightSourceBuffers::moveVertices(const VertexArray& sourceVertexArray, const LightSourceVectors& sourceVectors) {
sourceVertexArray.bind();
m_vertices.update(sourceVectors.getVertices());
sourceVertexArray.addBuffer(0, m_vertices);
sourceVertexArray.unbind();
}
void LightSourceBuffers::updateColors(const VertexArray& sourceVertexArray, const LightSourceVectors& sourceVectors) {
sourceVertexArray.bind();
m_colors.update(sourceVectors.getColors());
sourceVertexArray.addBuffer(2, m_colors, 4);
sourceVertexArray.unbind();
}
}

View File

@ -56,6 +56,8 @@ namespace ImGuiExt {
Texture::Texture(std::span<const std::byte> bytes, int width, int height) : Texture(reinterpret_cast<const ImU8*>(bytes.data()), bytes.size(), width, height) { } Texture::Texture(std::span<const std::byte> bytes, int width, int height) : Texture(reinterpret_cast<const ImU8*>(bytes.data()), bytes.size(), width, height) { }
Texture::Texture(const std::fs::path &path) : Texture(reinterpret_cast<const char *>(path.u8string().c_str())) { }
Texture::Texture(const char *path) { Texture::Texture(const char *path) {
unsigned char *imageData = stbi_load(path, &this->m_width, &this->m_height, nullptr, 4); unsigned char *imageData = stbi_load(path, &this->m_width, &this->m_height, nullptr, 4);
if (imageData == nullptr) if (imageData == nullptr)
@ -382,7 +384,7 @@ namespace ImGuiExt {
Separator(); Separator();
} }
bool InfoTooltip(const char *text) { bool InfoTooltip(const char *text, bool isSeparator) {
static double lastMoveTime; static double lastMoveTime;
static ImGuiID lastHoveredID; static ImGuiID lastHoveredID;
@ -393,7 +395,10 @@ namespace ImGuiExt {
if (IsItemHovered() && (currTime - lastMoveTime) >= 0.5 && hoveredID == lastHoveredID) { if (IsItemHovered() && (currTime - lastMoveTime) >= 0.5 && hoveredID == lastHoveredID) {
if (!std::string_view(text).empty()) { if (!std::string_view(text).empty()) {
BeginTooltip(); BeginTooltip();
TextUnformatted(text); if (isSeparator)
SeparatorText(text);
else
TextUnformatted(text);
EndTooltip(); EndTooltip();
} }
@ -893,6 +898,26 @@ namespace ImGuiExt {
return toggled; return toggled;
} }
bool DimmedIconToggle(const char *iconOn, const char *iconOff, bool *v) {
bool pushed = false;
bool toggled = false;
if (*v) {
PushStyleColor(ImGuiCol_Border, GetStyleColorVec4(ImGuiCol_ButtonActive));
pushed = true;
}
if (DimmedIconButton(*v ? iconOn : iconOff, GetStyleColorVec4(ImGuiCol_Text))) {
*v = !*v;
toggled = true;
}
if (pushed)
PopStyleColor();
return toggled;
}
void TextOverlay(const char *text, ImVec2 pos) { void TextOverlay(const char *text, ImVec2 pos) {
const auto textSize = CalcTextSize(text); const auto textSize = CalcTextSize(text);
const auto textPos = pos - textSize / 2; const auto textPos = pos - textSize / 2;
@ -937,6 +962,47 @@ namespace ImGuiExt {
ImGui::EndChild(); ImGui::EndChild();
} }
bool VSliderAngle(const char* label, ImVec2& size, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags) {
if (format == NULL)
format = "%.0f deg";
float v_deg = (*v_rad) * 360.0f / (2 * IM_PI);
bool value_changed = ImGui::VSliderFloat(label, size, &v_deg, v_degrees_min, v_degrees_max, format, flags);
*v_rad = v_deg * (2 * IM_PI) / 360.0f;
return value_changed;
}
bool InputFilePicker(const char *label, std::fs::path &path, const std::vector<hex::fs::ItemFilter> &validExtensions) {
bool picked = false;
ImGui::PushID(label);
const auto buttonSize = ImGui::CalcTextSize(ICON_VS_FOLDER) + ImGui::GetStyle().FramePadding * 2;
ImGui::PushItemWidth(ImGui::CalcItemWidth() - buttonSize.x - ImGui::GetStyle().FramePadding.x);
std::string string = wolv::util::toUTF8String(path);
if (ImGui::InputText("##pathInput", string, ImGuiInputTextFlags_AutoSelectAll)) {
path = std::u8string(string.begin(), string.end());
picked = true;
}
ImGui::PopItemWidth();
ImGui::SameLine();
if (ImGui::Button(ICON_VS_FOLDER, buttonSize)) {
hex::fs::openFileBrowser(hex::fs::DialogMode::Open, validExtensions, [&](const std::fs::path &pickedPath) {
path = pickedPath;
picked = true;
});
}
ImGui::SameLine();
ImGui::TextUnformatted(label);
ImGui::PopID();
return picked;
}
} }
namespace ImGui { namespace ImGui {

View File

@ -0,0 +1,19 @@
#pragma once
// Generated: 2023-11-12 08:44:48.641532
extern const unsigned int blendericons_compressed_size;
extern const unsigned int blendericons_compressed_data[];
#define ICON_MIN_BI 0xea00
#define ICON_MAX_BI 0xea08
#define ICON_BI_CUBE "\xee\xa8\x80" //< U+ea00
#define ICON_BI_EMPTY_ARROWS "\xee\xa8\x81" //< U+ea01
#define ICON_BI_GRID "\xee\xa8\x82" //< U+ea02
#define ICON_BI_MESH_GRID "\xee\xa8\x83" //< U+ea03
#define ICON_BI_MOD_SOLIDIFY "\xee\xa8\x84" //< U+ea04
#define ICON_BI_ORIENTATION_GLOBAL "\xee\xa8\x85" //< U+ea05
#define ICON_BI_ORIENTATION_LOCAL "\xee\xa8\x86" //< U+ea06
#define ICON_BI_VIEW_ORTHO "\xee\xa8\x87" //< U+ea07
#define ICON_BI_VIEW_PERSPECTIVE "\xee\xa8\x88" //< U+ea08

View File

@ -1,4 +1,5 @@
#include "init/tasks.hpp" #include "init/tasks.hpp"
#include "misc/freetype/imgui_freetype.h"
#include <imgui.h> #include <imgui.h>

View File

@ -80,6 +80,17 @@ add_imhex_plugin(
source/content/tools/tcp_client_server.cpp source/content/tools/tcp_client_server.cpp
source/content/tools/wiki_explainer.cpp source/content/tools/wiki_explainer.cpp
source/content/pl_visualizers/line_plot.cpp
source/content/pl_visualizers/scatter_plot.cpp
source/content/pl_visualizers/image.cpp
source/content/pl_visualizers/disassembler.cpp
source/content/pl_visualizers/3d_model.cpp
source/content/pl_visualizers/sound.cpp
source/content/pl_visualizers/chunk_entropy.cpp
source/content/pl_visualizers/hex_viewer.cpp
source/content/pl_visualizers/coordinates.cpp
source/content/pl_visualizers/timestamp.cpp
source/content/views/view_hex_editor.cpp source/content/views/view_hex_editor.cpp
source/content/views/view_pattern_editor.cpp source/content/views/view_pattern_editor.cpp
source/content/views/view_pattern_data.cpp source/content/views/view_pattern_data.cpp

View File

@ -0,0 +1,20 @@
#pragma once
#include <pl/pattern_language.hpp>
#include <pl/patterns/pattern.hpp>
namespace hex::plugin::builtin {
template<typename T>
std::vector<T> patternToArray(pl::ptrn::Pattern *pattern){
const auto bytes = pattern->getBytes();
std::vector<T> result;
result.resize(bytes.size() / sizeof(T));
for (size_t i = 0; i < result.size(); i++)
std::memcpy(&result[i], &bytes[i * sizeof(T)], sizeof(T));
return result;
}
}

Binary file not shown.

View File

@ -455,8 +455,14 @@
"hex.builtin.pattern_drawer.var_name": "Name", "hex.builtin.pattern_drawer.var_name": "Name",
"hex.builtin.pattern_drawer.visualizer.unknown": "Unknown visualizer", "hex.builtin.pattern_drawer.visualizer.unknown": "Unknown visualizer",
"hex.builtin.pattern_drawer.visualizer.invalid_parameter_count": "Invalid parameter count", "hex.builtin.pattern_drawer.visualizer.invalid_parameter_count": "Invalid parameter count",
"hex.builtin.pl_visualizer.3d.rotation": "Rotation", "hex.builtin.pl_visualizer.3d.light_position": "Light Position",
"hex.builtin.pl_visualizer.3d.scale": "Scale", "hex.builtin.pl_visualizer.3d.ambient_brightness": "Ambient Brightness",
"hex.builtin.pl_visualizer.3d.diffuse_brightness": "Diffuse Brightness",
"hex.builtin.pl_visualizer.3d.specular_brightness": "Specular Brightness",
"hex.builtin.pl_visualizer.3d.object_reflectiveness": "Object Reflectiveness",
"hex.builtin.pl_visualizer.3d.light_color": "Light Color",
"hex.builtin.pl_visualizer.3d.more_settings": "More Settings",
"hex.builtin.pl_visualizer.3d.texture_file": "Texture File Path",
"hex.builtin.pl_visualizer.coordinates.latitude": "Latitude", "hex.builtin.pl_visualizer.coordinates.latitude": "Latitude",
"hex.builtin.pl_visualizer.coordinates.longitude": "Longitude", "hex.builtin.pl_visualizer.coordinates.longitude": "Longitude",
"hex.builtin.pl_visualizer.coordinates.query": "Find address", "hex.builtin.pl_visualizer.coordinates.query": "Find address",

View File

@ -1,12 +1,40 @@
#version 330 core #version 330 core
in vec3 normal;
out vec4 color; in VertexData {
vec3 normal;
vec4 fragColor;
vec2 texCoord;
vec3 lightPosition;
vec3 fragPosition;
vec4 lightBrightness;
vec3 lightColor;
} vertexData;
out vec4 outColor;
uniform sampler2D modelTexture;
void main() { void main() {
vec3 norm = normalize(normal); vec3 ambientLightColor = vec3(1.0, 1.0, 1.0);
vec3 lightDir = normalize(vec3(0, 0, -1));
float diff = max(dot(norm, lightDir), 0.0); // Ambient lighting
vec3 diffuse = diff * vec3(1.0, 1.0, 1.0); vec3 ambient = vertexData.lightBrightness.x * ambientLightColor;
// Diffuse lighting
vec3 normalVector = normalize(vertexData.normal);
vec3 lightDirection = normalize(vertexData.lightPosition - vertexData.fragPosition);
float diffuse = vertexData.lightBrightness.y * max(dot(normalVector, lightDirection), 0.0);
// Specular lighting
vec3 viewDirection = normalize(-vertexData.fragPosition);
vec3 reflectDirection = normalize(-reflect(lightDirection, normalVector));
float reflectionIntensity = pow(max(dot(viewDirection, reflectDirection), 0.0), vertexData.lightBrightness.w);
float specular = vertexData.lightBrightness.z * reflectionIntensity;
float dst = distance(vertexData.lightPosition, vertexData.fragPosition);
float attn = 1./(1.0f + 0.1f*dst + 0.01f*dst*dst) ;
vec3 color = ((diffuse + specular)*attn + ambient) * vertexData.lightColor;
outColor = (texture(modelTexture, vertexData.texCoord) + vertexData.fragColor) * vec4(color, 1.0);
}
color = vec4(1.0f, 0.5f, 0.2f, 1.0f) * vec4(diffuse, 1.0) + 0.1;
}

View File

@ -0,0 +1,22 @@
#version 330
in VertexData {
vec3 normal;
vec4 color;
vec3 fragPosition;
} vertexData;
out vec4 outColor;
void main() {
vec3 nLight = normalize(-vertexData.fragPosition);
vec3 nNormal = normalize(vertexData.normal);
float dotLN = dot(nLight, nNormal);
float diffuse = dotLN * 0.5;
vec3 color = (diffuse+0.7)*vertexData.color.xyz;
outColor = vec4(color, 1.0f);
}

View File

@ -0,0 +1,27 @@
#version 330
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
layout (location = 0) in vec3 in_Position;
layout (location = 1) in vec3 in_Normal;
layout (location = 2) in vec4 in_Color;
out VertexData {
vec3 normal;
vec4 color;
vec3 fragPosition;
} vertexData;
void main() {
vertexData.normal = (modelMatrix * vec4(in_Normal,0)).xyz;
//vertexData.normal = mat3(transpose(inverse(modelMatrix))) * in_Normal;
//vertexData.fragPosition = (viewMatrix * modelMatrix * vec4(in_Position, 1.0)).xyz;
vertexData.fragPosition = (modelMatrix * vec4(in_Position, 1.0)).xyz;
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(in_Position, 1.0);
vertexData.color = in_Color;
}

View File

@ -0,0 +1,8 @@
#version 330 core
in vec4 fragColor;
out vec4 outColor;
void main() {
outColor = fragColor;
}

View File

@ -0,0 +1,15 @@
#version 330 core
layout (location = 0) in vec3 in_Position;
layout (location = 1) in vec4 in_Color;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
out vec4 fragColor;
void main() {
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(in_Position, 1.0);
fragColor = in_Color;
}

View File

@ -1,36 +1,39 @@
#version 330 core #version 330 core
layout (location = 0) in vec3 in_Position; layout (location = 0) in vec3 in_Position;
layout (location = 1) in vec3 in_Normal; layout (location = 1) in vec4 in_Color;
layout (location = 2) in vec3 in_Normal;
layout (location = 3) in vec2 in_TexCoord;
/*uniform float time;*/ uniform mat4 modelScale;
uniform float scale;
uniform vec3 rotation;
uniform vec3 translation;
out vec3 normal; uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
mat4 rotationMatrix(vec3 axis, float angle) { uniform vec3 lightPosition;
axis = normalize(axis); uniform vec4 lightBrightness;
float s = sin(angle); uniform vec3 lightColor;
float c = cos(angle);
float oc = 1.0 - c;
return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, out VertexData {
oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, vec3 normal;
oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, vec4 fragColor;
0.0, 0.0, 0.0, 1.0); vec2 texCoord;
} vec3 lightPosition;
vec3 fragPosition;
mat4 viewMatrix(vec3 rotation) { vec4 lightBrightness;
mat4 rotationX = rotationMatrix(vec3(1, 0, 0), rotation.x); vec3 lightColor;
mat4 rotationY = rotationMatrix(vec3(0, 1, 0), rotation.y); } vertexData;
mat4 rotationZ = rotationMatrix(vec3(0, 0, 1), rotation.z);
return rotationX * rotationY * rotationZ;
}
void main() { void main() {
mat4 view = viewMatrix(rotation); gl_Position = projectionMatrix * viewMatrix * modelScale * vec4(in_Position, 1.0);
normal = (vec4(in_Normal, 1.0) * view).xyz * -1;
gl_Position = vec4((in_Position + translation) * -scale, 1.0) * view; vertexData.normal = mat3(transpose(inverse(modelScale))) * in_Normal;
vertexData.fragPosition = vec3(viewMatrix * modelScale * vec4(in_Position, 1.0));
vertexData.fragColor = in_Color;
vertexData.texCoord = in_TexCoord;
vertexData.lightBrightness = lightBrightness;
vertexData.lightColor = lightColor;
// Transform world-space light position to view-space light position
vertexData.lightPosition = vec3(viewMatrix * modelMatrix * vec4(lightPosition, 1.0));
} }

View File

@ -7,6 +7,7 @@
#include <fonts/fontawesome_font.h> #include <fonts/fontawesome_font.h>
#include <fonts/codicons_font.h> #include <fonts/codicons_font.h>
#include <fonts/blendericons_font.h>
#include <imgui_freetype.h> #include <imgui_freetype.h>
@ -21,6 +22,7 @@ namespace hex::plugin::builtin {
* efficient when packing the glyphs into the font atlas and therefor make the atlas much smaller. * efficient when packing the glyphs into the font atlas and therefor make the atlas much smaller.
*/ */
ImHexApi::Fonts::loadFont("Blender Icons", romfs::get("fonts/blendericons.ttf").span<u8>(),{ { ICON_MIN_BI, ICON_MAX_BI } }, { 0, -3_scaled });
ImHexApi::Fonts::loadFont("Font Awesome 5", romfs::get("fonts/fontawesome.otf").span<u8>(), ImHexApi::Fonts::loadFont("Font Awesome 5", romfs::get("fonts/fontawesome.otf").span<u8>(),
{ {
{ glyph(ICON_FA_BACKSPACE), glyph(ICON_FA_INFINITY), glyph(ICON_FA_TACHOMETER_ALT), glyph(ICON_FA_MICROCHIP), glyph(ICON_FA_CODE_BRANCH) } { glyph(ICON_FA_BACKSPACE), glyph(ICON_FA_INFINITY), glyph(ICON_FA_TACHOMETER_ALT), glyph(ICON_FA_MICROCHIP), glyph(ICON_FA_CODE_BRANCH) }

View File

@ -209,12 +209,14 @@ namespace hex::plugin::builtin {
if (fontFile.empty()) if (fontFile.empty())
fonts->Clear(); fonts->Clear();
if (ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_bold", false)) if (ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font_enable", false).get<bool>()) {
cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bold; if (ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_bold", false))
if (ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_italic", false)) cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bold;
cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Oblique; if (ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_italic", false))
if (!ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_antialias", false)) cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Oblique;
cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting; if (!ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_antialias", false))
cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting;
}
auto loadDefaultFont = [&](const char *fontName, u32 flags = 0) { auto loadDefaultFont = [&](const char *fontName, u32 flags = 0) {
ImFontConfig defaultConfig = cfg; ImFontConfig defaultConfig = cfg;
@ -266,7 +268,7 @@ namespace hex::plugin::builtin {
ranges.push_back(fontRange); ranges.push_back(fontRange);
cfg.FontBuilderFlags = startFlags | font.flags; cfg.FontBuilderFlags = font.flags;
float descent = [&] { float descent = [&] {
ImFontAtlas atlas; ImFontAtlas atlas;
@ -297,6 +299,7 @@ namespace hex::plugin::builtin {
cfg.GlyphOffset = { font.offset.x, font.offset.y - defaultFont->Descent + descent }; cfg.GlyphOffset = { font.offset.x, font.offset.y - defaultFont->Descent + descent };
fonts->AddFontFromMemoryTTF(font.fontData.data(), int(font.fontData.size()), 0, &cfg, ranges.back().Data); fonts->AddFontFromMemoryTTF(font.fontData.data(), int(font.fontData.size()), 0, &cfg, ranges.back().Data);
} }
cfg.FontBuilderFlags = startFlags;
// Create bold and italic font // Create bold and italic font
cfg.MergeMode = false; cfg.MergeMode = false;

View File

@ -1,717 +1,21 @@
#include <hex/api/content_registry.hpp> #include <hex/api/content_registry.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/helpers/http_requests.hpp>
#include <hex/helpers/disassembler.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/opengl.hpp>
#include <hex/helpers/fmt.hpp>
#include <fmt/chrono.h>
#include <imgui.h>
#include <implot.h>
#include <opengl_support.h>
#include <GLFW/glfw3.h>
#include <hex/ui/imgui_imhex_extensions.h>
#include <fonts/codicons_font.h>
#include <pl/patterns/pattern.hpp> #include <pl/patterns/pattern.hpp>
#include <miniaudio.h>
#include <romfs/romfs.hpp>
#include <numeric>
#include <numbers>
#include <content/helpers/diagrams.hpp>
#include <ui/hex_editor.hpp>
#include <content/providers/memory_file_provider.hpp>
namespace hex::plugin::builtin { namespace hex::plugin::builtin {
namespace { void drawLinePlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments);
void drawScatterPlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments);
template<typename T> void drawImageVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments);
std::vector<T> patternToArray(pl::ptrn::Pattern *pattern){ void drawBitmapVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments);
const auto bytes = pattern->getBytes(); void drawDisassemblyVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments);
void draw3DVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments);
std::vector<T> result; void drawSoundVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments);
result.resize(bytes.size() / sizeof(T)); void drawChunkBasedEntropyVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments);
for (size_t i = 0; i < result.size(); i++) void drawHexVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments);
std::memcpy(&result[i], &bytes[i * sizeof(T)], sizeof(T)); void drawCoordinateVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments);
void drawTimestampVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments);
return result;
}
}
namespace {
void drawLinePlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static std::vector<float> values;
auto dataPattern = arguments[0].toPattern();
if (ImPlot::BeginPlot("##plot", ImVec2(400, 250), ImPlotFlags_CanvasOnly)) {
ImPlot::SetupAxes("X", "Y", ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit);
if (shouldReset) {
values.clear();
values = sampleData(patternToArray<float>(dataPattern.get()), ImPlot::GetPlotSize().x * 4);
}
ImPlot::PlotLine("##line", values.data(), values.size());
ImPlot::EndPlot();
}
}
void drawScatterPlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static std::vector<float> xValues, yValues;
auto xPattern = arguments[0].toPattern();
auto yPattern = arguments[1].toPattern();
if (ImPlot::BeginPlot("##plot", ImVec2(400, 250), ImPlotFlags_CanvasOnly)) {
ImPlot::SetupAxes("X", "Y", ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit);
if (shouldReset) {
xValues.clear(); yValues.clear();
xValues = sampleData(patternToArray<float>(xPattern.get()), ImPlot::GetPlotSize().x * 4);
yValues = sampleData(patternToArray<float>(yPattern.get()), ImPlot::GetPlotSize().x * 4);
}
ImPlot::PlotScatter("##scatter", xValues.data(), yValues.data(), xValues.size());
ImPlot::EndPlot();
}
}
void drawImageVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static ImGuiExt::Texture texture;
static float scale = 1.0F;
if (shouldReset) {
auto pattern = arguments[0].toPattern();
auto data = pattern->getBytes();
texture = ImGuiExt::Texture(data.data(), data.size());
scale = 200_scaled / texture.getSize().x;
}
if (texture.isValid())
ImGui::Image(texture, texture.getSize() * scale);
if (ImGui::IsWindowHovered()) {
auto scrollDelta = ImGui::GetIO().MouseWheel;
if (scrollDelta != 0.0F) {
scale += scrollDelta * 0.1F;
scale = std::clamp(scale, 0.1F, 10.0F);
}
}
}
void drawBitmapVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static ImGuiExt::Texture texture;
static float scale = 1.0F;
if (shouldReset) {
auto pattern = arguments[0].toPattern();
auto width = arguments[1].toUnsigned();
auto height = arguments[2].toUnsigned();
auto data = pattern->getBytes();
texture = ImGuiExt::Texture(data.data(), data.size(), width, height);
}
if (texture.isValid())
ImGui::Image(texture, texture.getSize() * scale);
if (ImGui::IsWindowHovered()) {
auto scrollDelta = ImGui::GetIO().MouseWheel;
if (scrollDelta != 0.0F) {
scale += scrollDelta * 0.1F;
scale = std::clamp(scale, 0.1F, 10.0F);
}
}
}
void drawDisassemblyVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
struct Disassembly {
u64 address;
std::vector<u8> bytes;
std::string instruction;
};
static std::vector<Disassembly> disassembly;
if (shouldReset) {
auto pattern = arguments[0].toPattern();
auto baseAddress = arguments[1].toUnsigned();
auto architecture = arguments[2].toUnsigned();
auto mode = arguments[3].toUnsigned();
disassembly.clear();
csh capstone;
if (cs_open(static_cast<cs_arch>(architecture), static_cast<cs_mode>(mode), &capstone) == CS_ERR_OK) {
cs_option(capstone, CS_OPT_SKIPDATA, CS_OPT_ON);
auto data = pattern->getBytes();
cs_insn *instructions = nullptr;
size_t instructionCount = cs_disasm(capstone, data.data(), data.size(), baseAddress, 0, &instructions);
for (size_t i = 0; i < instructionCount; i++) {
disassembly.push_back({ instructions[i].address, { instructions[i].bytes, instructions[i].bytes + instructions[i].size }, hex::format("{} {}", instructions[i].mnemonic, instructions[i].op_str) });
}
cs_free(instructions, instructionCount);
cs_close(&capstone);
}
}
if (ImGui::BeginTable("##disassembly", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY, scaled(ImVec2(0, 300)))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.builtin.common.address"_lang);
ImGui::TableSetupColumn("hex.builtin.common.bytes"_lang);
ImGui::TableSetupColumn("hex.builtin.common.instruction"_lang);
ImGui::TableHeadersRow();
for (auto &entry : disassembly) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGuiExt::TextFormatted("0x{0:08X}", entry.address);
ImGui::TableNextColumn();
std::string bytes;
for (auto byte : entry.bytes)
bytes += hex::format("{0:02X} ", byte);
ImGui::TextUnformatted(bytes.c_str());
ImGui::TableNextColumn();
ImGui::TextUnformatted(entry.instruction.c_str());
}
ImGui::EndTable();
}
}
void draw3DVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
auto verticesPattern = arguments[0].toPattern();
auto indicesPattern = arguments[1].toPattern();
static ImGuiExt::Texture texture;
static gl::Vector<float, 3> translation;
static gl::Vector<float, 3> rotation = { { 1.0F, -1.0F, 0.0F } };
static float scaling = 0.1F;
static std::vector<float> vertices, normals;
static std::vector<u32> indices;
static gl::Shader shader;
static gl::VertexArray vertexArray;
static gl::Buffer<float> vertexBuffer, normalBuffer;
static gl::Buffer<u32> indexBuffer;
{
auto dragDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Middle);
rotation[0] += -dragDelta.y * 0.0075F;
rotation[1] += -dragDelta.x * 0.0075F;
ImGui::ResetMouseDragDelta(ImGuiMouseButton_Middle);
dragDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
translation[0] += -dragDelta.x * 0.1F;
translation[1] += -dragDelta.y * 0.1F;
ImGui::ResetMouseDragDelta(ImGuiMouseButton_Right);
auto scrollDelta = ImGui::GetIO().MouseWheel;
scaling += scrollDelta * 0.01F;
if (scaling < 0.01F)
scaling = 0.01F;
}
if (shouldReset) {
vertices = patternToArray<float>(verticesPattern.get());
indices = patternToArray<u32>(indicesPattern.get());
normals.clear();
normals.resize(vertices.size());
for (u32 i = 0; i < normals.size(); i += 9) {
auto v1 = gl::Vector<float, 3>({ vertices[i] , vertices[i + 1], vertices[i + 2] });
auto v2 = gl::Vector<float, 3>({ vertices[i + 3], vertices[i + 4], vertices[i + 5] });
auto v3 = gl::Vector<float, 3>({ vertices[i + 6], vertices[i + 7], vertices[i + 8] });
auto normal = ((v2 - v1).cross(v3 - v1)).normalize();
normals[i] = normal[0];
normals[i + 1] = normal[1];
normals[i + 2] = normal[2];
normals[i + 3] = normal[0];
normals[i + 4] = normal[1];
normals[i + 5] = normal[2];
normals[i + 6] = normal[0];
normals[i + 7] = normal[1];
normals[i + 8] = normal[2];
}
shader = gl::Shader(romfs::get("shaders/default/vertex.glsl").string(), romfs::get("shaders/default/fragment.glsl").string());
vertexArray = gl::VertexArray();
vertexBuffer = {};
normalBuffer = {};
indexBuffer = {};
vertexArray.bind();
vertexBuffer = gl::Buffer<float>(gl::BufferType::Vertex, vertices);
normalBuffer = gl::Buffer<float>(gl::BufferType::Vertex, normals);
indexBuffer = gl::Buffer<u32>(gl::BufferType::Index, indices);
vertexArray.addBuffer(0, vertexBuffer);
vertexArray.addBuffer(1, normalBuffer);
if (!indices.empty())
vertexArray.addBuffer(2, indexBuffer);
vertexBuffer.unbind();
normalBuffer.unbind();
indexBuffer.unbind();
vertexArray.unbind();
}
{
gl::FrameBuffer frameBuffer;
gl::Texture renderTexture(400_scaled, 400_scaled);
frameBuffer.attachTexture(renderTexture);
frameBuffer.bind();
glEnable(GL_DEPTH_TEST);
shader.bind();
shader.setUniform("scale", scaling);
shader.setUniform("rotation", rotation);
shader.setUniform("translation", translation);
vertexArray.bind();
glViewport(0, 0, renderTexture.getWidth(), renderTexture.getHeight());
glClearColor(0.00F, 0.00F, 0.00F, 0.00f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (indices.empty())
vertexBuffer.draw();
else
indexBuffer.draw();
vertexArray.unbind();
shader.unbind();
frameBuffer.unbind();
texture = ImGuiExt::Texture(renderTexture.release(), renderTexture.getWidth(), renderTexture.getHeight());
}
auto textureSize = texture.getSize();
if (ImGui::BeginTable("##3DVisualizer", 2, ImGuiTableFlags_SizingFixedFit)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
if (ImGui::BeginChild("##image", textureSize, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
ImGui::Image(texture, textureSize, ImVec2(0, 1), ImVec2(1, 0));
}
ImGui::EndChild();
ImGui::PopStyleVar();
ImGui::TableNextColumn();
ImGui::TextUnformatted("hex.builtin.pl_visualizer.3d.rotation"_lang);
ImGui::VSliderFloat("##X", ImVec2(18_scaled, textureSize.y), &rotation.data()[0], 0, std::numbers::pi * 2, "", ImGuiSliderFlags_AlwaysClamp);
ImGui::SameLine();
ImGui::VSliderFloat("##Y", ImVec2(18_scaled, textureSize.y), &rotation.data()[1], 0, std::numbers::pi * 2, "", ImGuiSliderFlags_AlwaysClamp);
ImGui::SameLine();
ImGui::VSliderFloat("##Z", ImVec2(18_scaled, textureSize.y), &rotation.data()[2], 0, std::numbers::pi * 2, "", ImGuiSliderFlags_AlwaysClamp);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("hex.builtin.pl_visualizer.3d.scale"_lang);
ImGui::SameLine();
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::SliderFloat("##Scale", &scaling, 0.0001F, 0.2F, "");
ImGui::PopItemWidth();
for (u8 i = 0; i < 3; i++) {
while (rotation.data()[i] > std::numbers::pi * 2)
rotation.data()[i] -= std::numbers::pi * 2;
while (rotation.data()[i] < 0)
rotation.data()[i] += std::numbers::pi * 2;
}
ImGui::TableNextColumn();
if (ImGui::Button("hex.builtin.common.reset"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
translation = gl::Vector<float, 3>({ 0.0F, 0.0F, 0.0F });
rotation = gl::Vector<float, 3>({ 0.0F, 0.0F, 0.0F });
scaling = 0.1F;
}
ImGui::EndTable();
}
}
void drawSoundVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
auto wavePattern = arguments[0].toPattern();
auto channels = arguments[1].toUnsigned();
auto sampleRate = arguments[2].toUnsigned();
static std::vector<i16> waveData, sampledData;
static ma_device audioDevice;
static ma_device_config deviceConfig;
static bool shouldStop = false;
static u64 index = 0;
static TaskHolder resetTask;
if (sampleRate == 0)
throw std::logic_error(hex::format("Invalid sample rate: {}", sampleRate));
else if (channels == 0)
throw std::logic_error(hex::format("Invalid channel count: {}", channels));
if (shouldReset) {
waveData.clear();
resetTask = TaskManager::createTask("Visualizing...", TaskManager::NoProgress, [=](Task &) {
ma_device_stop(&audioDevice);
waveData = patternToArray<i16>(wavePattern.get());
sampledData = sampleData(waveData, 300_scaled * 4);
index = 0;
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.format = ma_format_s16;
deviceConfig.playback.channels = channels;
deviceConfig.sampleRate = sampleRate;
deviceConfig.pUserData = &waveData;
deviceConfig.dataCallback = [](ma_device *device, void *pOutput, const void *, ma_uint32 frameCount) {
if (index >= waveData.size()) {
index = 0;
shouldStop = true;
return;
}
ma_copy_pcm_frames(pOutput, waveData.data() + index, frameCount, device->playback.format, device->playback.channels);
index += frameCount;
};
ma_device_init(nullptr, &deviceConfig, &audioDevice);
});
}
ImGui::BeginDisabled(resetTask.isRunning());
ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0, 0));
if (ImPlot::BeginPlot("##amplitude_plot", scaled(ImVec2(300, 80)), ImPlotFlags_CanvasOnly | ImPlotFlags_NoFrame | ImPlotFlags_NoInputs)) {
ImPlot::SetupAxes("##time", "##amplitude", ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoMenus, ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoMenus);
ImPlot::SetupAxesLimits(0, waveData.size(), std::numeric_limits<i16>::min(), std::numeric_limits<i16>::max(), ImGuiCond_Always);
double dragPos = index;
if (ImPlot::DragLineX(1, &dragPos, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
if (dragPos < 0) dragPos = 0;
if (dragPos >= waveData.size()) dragPos = waveData.size() - 1;
index = dragPos;
}
ImPlot::PlotLine("##audio", sampledData.data(), sampledData.size());
ImPlot::EndPlot();
}
ImPlot::PopStyleVar();
{
const u64 min = 0, max = waveData.size();
ImGui::PushItemWidth(300_scaled);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::SliderScalar("##index", ImGuiDataType_U64, &index, &min, &max, "");
ImGui::PopStyleVar();
ImGui::PopItemWidth();
}
if (shouldStop) {
shouldStop = false;
ma_device_stop(&audioDevice);
}
bool playing = ma_device_is_started(&audioDevice);
if (ImGuiExt::IconButton(playing ? ICON_VS_DEBUG_PAUSE : ICON_VS_PLAY, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen))) {
if (playing)
ma_device_stop(&audioDevice);
else
ma_device_start(&audioDevice);
}
ImGui::SameLine();
if (ImGuiExt::IconButton(ICON_VS_DEBUG_STOP, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) {
index = 0;
ma_device_stop(&audioDevice);
}
ImGui::EndDisabled();
ImGui::SameLine();
if (resetTask.isRunning())
ImGuiExt::TextSpinner("");
else
ImGuiExt::TextFormatted("{:02d}:{:02d} / {:02d}:{:02d}",
(index / sampleRate) / 60, (index / sampleRate) % 60,
(waveData.size() / sampleRate) / 60, (waveData.size() / sampleRate) % 60);
}
void drawChunkBasedEntropyVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
// Variable used to store the result to avoid having to recalculate the result at each frame
static DiagramChunkBasedEntropyAnalysis analyzer;
// Compute data
if (shouldReset) {
auto pattern = arguments[0].toPattern();
auto chunkSize = arguments[1].toUnsigned();
analyzer.process(pattern->getBytes(), chunkSize);
}
// Show results
analyzer.draw(ImVec2(400, 250), ImPlotFlags_CanvasOnly);
}
void drawHexVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static ui::HexEditor editor;
static std::unique_ptr<MemoryFileProvider> dataProvider;
if (shouldReset) {
auto pattern = arguments[0].toPattern();
std::vector<u8> data;
dataProvider = std::make_unique<MemoryFileProvider>();
try {
data = pattern->getBytes();
} catch (const std::exception &) {
dataProvider->resize(0);
throw;
}
dataProvider->resize(data.size());
dataProvider->writeRaw(0x00, data.data(), data.size());
dataProvider->setReadOnly(true);
editor.setProvider(dataProvider.get());
}
if (ImGui::BeginChild("##editor", scaled(ImVec2(600, 400)), false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
editor.draw();
ImGui::EndChild();
}
}
void drawCoordinateVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static ImVec2 coordinate;
static double latitude, longitude;
static std::string address;
static std::mutex addressMutex;
static TaskHolder addressTask;
static auto mapTexture = ImGuiExt::Texture(romfs::get("assets/common/map.jpg").span());
static ImVec2 mapSize = scaled(ImVec2(500, 500 / mapTexture.getAspectRatio()));
if (shouldReset) {
std::scoped_lock lock(addressMutex);
address.clear();
latitude = arguments[0].toFloatingPoint();
longitude = arguments[1].toFloatingPoint();
// Convert latitude and longitude to X/Y coordinates on the image
coordinate.x = float((longitude + 180) / 360 * mapSize.x);
coordinate.y = float((-latitude + 90) / 180 * mapSize.y);
}
const auto startPos = ImGui::GetWindowPos() + ImGui::GetCursorPos();
// Draw background image
ImGui::Image(mapTexture, mapSize);
// Draw Longitude / Latitude text below image
ImGui::PushTextWrapPos(startPos.x + mapSize.x);
ImGuiExt::TextFormattedWrapped("{}: {:.0f}° {:.0f}' {:.4f}\" {} | {}: {:.0f}° {:.0f}' {:.4f}\" {}",
"hex.builtin.pl_visualizer.coordinates.latitude"_lang,
std::floor(std::abs(latitude)),
std::floor(std::abs(latitude - std::floor(latitude)) * 60),
(std::abs(latitude - std::floor(latitude)) * 60 - std::floor(std::abs(latitude - std::floor(latitude)) * 60)) * 60,
latitude >= 0 ? "N" : "S",
"hex.builtin.pl_visualizer.coordinates.longitude"_lang,
std::floor(std::abs(longitude)),
std::floor(std::abs(longitude - std::floor(longitude)) * 60),
(std::abs(longitude - std::floor(longitude)) * 60 - std::floor(std::abs(longitude - std::floor(longitude)) * 60)) * 60,
longitude >= 0 ? "E" : "W"
);
ImGui::PopTextWrapPos();
if (addressTask.isRunning()) {
ImGuiExt::TextSpinner("hex.builtin.pl_visualizer.coordinates.querying"_lang);
} else if (address.empty()) {
if (ImGuiExt::DimmedButton("hex.builtin.pl_visualizer.coordinates.query"_lang)) {
addressTask = TaskManager::createBackgroundTask("hex.builtin.pl_visualizer.coordinates.querying"_lang, [lat = latitude, lon = longitude](auto &) {
constexpr static auto ApiURL = "https://geocode.maps.co/reverse?lat={}&lon={}&format=jsonv2";
HttpRequest request("GET", hex::format(ApiURL, lat, lon));
auto response = request.execute().get();
if (!response.isSuccess())
return;
try {
auto json = nlohmann::json::parse(response.getData());
auto jsonAddr = json["address"];
std::scoped_lock lock(addressMutex);
if (jsonAddr.contains("village")) {
address = hex::format("{} {}, {} {}",
jsonAddr["village"].get<std::string>(),
jsonAddr["county"].get<std::string>(),
jsonAddr["state"].get<std::string>(),
jsonAddr["country"].get<std::string>());
} else if (jsonAddr.contains("city")) {
address = hex::format("{}, {} {}, {} {}",
jsonAddr["road"].get<std::string>(),
jsonAddr["quarter"].get<std::string>(),
jsonAddr["city"].get<std::string>(),
jsonAddr["state"].get<std::string>(),
jsonAddr["country"].get<std::string>());
}
} catch (std::exception &) {
address = std::string("hex.builtin.pl_visualizer.coordinates.querying_no_address"_lang);
}
});
}
} else {
ImGui::PushTextWrapPos(startPos.x + mapSize.x);
ImGuiExt::TextFormattedWrapped("{}", address);
ImGui::PopTextWrapPos();
}
// Draw crosshair pointing to the coordinates
{
constexpr static u32 CrossHairColor = 0xFF00D0D0;
constexpr static u32 BorderColor = 0xFF000000;
auto drawList = ImGui::GetWindowDrawList();
drawList->AddLine(startPos + ImVec2(coordinate.x, 0), startPos + ImVec2(coordinate.x, mapSize.y), CrossHairColor, 2_scaled);
drawList->AddLine(startPos + ImVec2(0, coordinate.y), startPos + ImVec2(mapSize.x, coordinate.y), CrossHairColor, 2_scaled);
drawList->AddCircleFilled(startPos + coordinate, 5, CrossHairColor);
drawList->AddCircle(startPos + coordinate, 5, BorderColor);
}
}
void drawTimestampVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments) {
time_t timestamp = arguments[0].toUnsigned();
auto tm = fmt::gmtime(timestamp);
auto date = std::chrono::year_month_day(std::chrono::year(tm.tm_year + 1900), std::chrono::month(tm.tm_mon + 1), std::chrono::day(tm.tm_mday));
auto lastMonthDay = std::chrono::year_month_day_last(date.year(), date.month() / std::chrono::last);
auto firstWeekDay = std::chrono::weekday(std::chrono::year_month_day(date.year(), date.month(), std::chrono::day(1)));
const auto scale = 1_scaled * (ImHexApi::Fonts::getFontSize() / ImHexApi::Fonts::DefaultFontSize);
// Draw calendar
if (ImGui::BeginTable("##month_table", 2)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
// Draw centered month name and year
ImGuiExt::TextFormattedCenteredHorizontal("{:%B %Y}", tm);
if (ImGui::BeginTable("##days_table", 7, ImGuiTableFlags_Borders | ImGuiTableFlags_NoHostExtendX, ImVec2(160, 120) * scale)) {
constexpr static auto ColumnFlags = ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoReorder | ImGuiTableColumnFlags_NoHide;
ImGui::TableSetupColumn("M", ColumnFlags);
ImGui::TableSetupColumn("T", ColumnFlags);
ImGui::TableSetupColumn("W", ColumnFlags);
ImGui::TableSetupColumn("T", ColumnFlags);
ImGui::TableSetupColumn("F", ColumnFlags);
ImGui::TableSetupColumn("S", ColumnFlags);
ImGui::TableSetupColumn("S", ColumnFlags);
ImGui::TableHeadersRow();
ImGui::TableNextRow();
// Skip days before the first day of the month
for (u8 i = 0; i < firstWeekDay.c_encoding() - 1; ++i)
ImGui::TableNextColumn();
// Draw days
for (u8 i = 1; i <= u32(lastMonthDay.day()); ++i) {
ImGui::TableNextColumn();
ImGuiExt::TextFormatted("{:02}", i);
if (std::chrono::day(i) == date.day())
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarRed));
if (std::chrono::weekday(std::chrono::year_month_day(date.year(), date.month(), std::chrono::day(i))) == std::chrono::Sunday)
ImGui::TableNextRow();
}
ImGui::EndTable();
}
ImGui::TableNextColumn();
// Draw analog clock
const auto size = ImVec2(120, 120) * scale;
if (ImGui::BeginChild("##clock", size + ImVec2(0, ImGui::GetTextLineHeightWithSpacing()))) {
// Draw centered digital hour, minute and seconds
ImGuiExt::TextFormattedCenteredHorizontal("{:%H:%M:%S}", tm);
auto drawList = ImGui::GetWindowDrawList();
const auto center = ImGui::GetWindowPos() + ImVec2(0, ImGui::GetTextLineHeightWithSpacing()) + size / 2;
// Draw clock face
drawList->AddCircle(center, size.x / 2, ImGui::GetColorU32(ImGuiCol_TextDisabled), 0);
auto sectionPos = [](float i) {
return ImVec2(std::sin(-i * 30.0F * std::numbers::pi / 180.0F + std::numbers::pi / 2), std::cos(-i * 30.0F * std::numbers::pi / 180.0F + std::numbers::pi / 2));
};
// Draw clock sections and numbers
for (u8 i = 0; i < 12; ++i) {
auto text = hex::format("{}", (((i + 2) % 12) + 1));
drawList->AddLine(center + sectionPos(i) * size / 2.2, center + sectionPos(i) * size / 2, ImGui::GetColorU32(ImGuiCol_TextDisabled), 1_scaled);
drawList->AddText(center + sectionPos(i) * size / 3 - ImGui::CalcTextSize(text.c_str()) / 2, ImGui::GetColorU32(ImGuiCol_Text), text.c_str());
}
// Draw hour hand
drawList->AddLine(center, center + sectionPos((tm.tm_hour + 9) % 12 + float(tm.tm_min) / 60.0) * size / 3.5, ImGui::GetColorU32(ImGuiCol_TextDisabled), 3_scaled);
// Draw minute hand
drawList->AddLine(center, center + sectionPos((float(tm.tm_min) / 5.0F) - 3) * size / 2.5, ImGui::GetColorU32(ImGuiCol_TextDisabled), 3_scaled);
// Draw second hand
drawList->AddLine(center, center + sectionPos((float(tm.tm_sec) / 5.0F) - 3) * size / 2.5, ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarRed), 2_scaled);
}
ImGui::EndChild();
ImGui::EndTable();
}
}
}
void registerPatternLanguageVisualizers() { void registerPatternLanguageVisualizers() {
using ParamCount = pl::api::FunctionParameterCount; using ParamCount = pl::api::FunctionParameterCount;
@ -721,7 +25,7 @@ namespace hex::plugin::builtin {
ContentRegistry::PatternLanguage::addVisualizer("image", drawImageVisualizer, ParamCount::exactly(1)); ContentRegistry::PatternLanguage::addVisualizer("image", drawImageVisualizer, ParamCount::exactly(1));
ContentRegistry::PatternLanguage::addVisualizer("bitmap", drawBitmapVisualizer, ParamCount::exactly(3)); ContentRegistry::PatternLanguage::addVisualizer("bitmap", drawBitmapVisualizer, ParamCount::exactly(3));
ContentRegistry::PatternLanguage::addVisualizer("disassembler", drawDisassemblyVisualizer, ParamCount::exactly(4)); ContentRegistry::PatternLanguage::addVisualizer("disassembler", drawDisassemblyVisualizer, ParamCount::exactly(4));
ContentRegistry::PatternLanguage::addVisualizer("3d", draw3DVisualizer, ParamCount::exactly(2)); ContentRegistry::PatternLanguage::addVisualizer("3d", draw3DVisualizer, ParamCount::between(2, 6));
ContentRegistry::PatternLanguage::addVisualizer("sound", drawSoundVisualizer, ParamCount::exactly(3)); ContentRegistry::PatternLanguage::addVisualizer("sound", drawSoundVisualizer, ParamCount::exactly(3));
ContentRegistry::PatternLanguage::addVisualizer("chunk_entropy", drawChunkBasedEntropyVisualizer, ParamCount::exactly(2)); ContentRegistry::PatternLanguage::addVisualizer("chunk_entropy", drawChunkBasedEntropyVisualizer, ParamCount::exactly(2));
ContentRegistry::PatternLanguage::addVisualizer("hex_viewer", drawHexVisualizer, ParamCount::exactly(1)); ContentRegistry::PatternLanguage::addVisualizer("hex_viewer", drawHexVisualizer, ParamCount::exactly(1));

View File

@ -0,0 +1,908 @@
#include <hex/helpers/utils.hpp>
#include <content/pl_visualizers/visualizer_helpers.hpp>
#include <fonts/codicons_font.h>
#include <fonts/blendericons_font.h>
#include <imgui.h>
#include <imgui_internal.h>
#include <hex/helpers/opengl.hpp>
#include <opengl_support.h>
#include <GLFW/glfw3.h>
#include <numbers>
#include <hex/ui/imgui_imhex_extensions.h>
#include <hex/api/imhex_api.hpp>
#include <hex/api/localization_manager.hpp>
#include <romfs/romfs.hpp>
namespace hex::plugin::builtin {
namespace {
enum class IndexType {
U8,
U16,
U32,
Invalid,
};
struct Vectors {
std::vector<float> vertices;
std::vector<float> normals;
std::vector<float> colors;
std::vector<float> uv1;
std::vector<u8> indices8;
std::vector<u16> indices16;
std::vector<u32> indices32;
};
struct LineVectors {
std::vector<float> vertices;
std::vector<float> colors;
std::vector<u8> indices8;
std::vector<u16> indices16;
std::vector<u32> indices32;
};
struct Buffers {
gl::Buffer<float> vertices;
gl::Buffer<float> normals;
gl::Buffer<float> colors;
gl::Buffer<float> uv1;
gl::Buffer<u8> indices8;
gl::Buffer<u16> indices16;
gl::Buffer<u32> indices32;
};
struct LineBuffers {
gl::Buffer<float> vertices;
gl::Buffer<float> colors;
gl::Buffer<u8> indices8;
gl::Buffer<u16> indices16;
gl::Buffer<u32> indices32;
};
ImVec2 s_renderingWindowSize;
int s_drawMode = GL_TRIANGLES;
float s_nearLimit = 0.9F;
float s_farLimit = 100.0F;
float s_scaling = 1.0F;
float s_max;
bool s_isPerspective = true;
bool s_drawAxes = true;
bool s_drawGrid = true;
bool s_drawLightSource = true;
bool s_drawTexture = false;
bool s_shouldReset = false;
bool s_shouldUpdateLightSource = true;
bool s_shouldUpdateTexture = false;
IndexType s_indexType;
ImGuiExt::Texture s_modelTexture;
gl::Vector<float, 3> s_translation = { { 0.0F, 0.0F, -3.0F } };
gl::Vector<float, 3> s_rotation = { { 0.0F, 0.0F, 0.0F } };
gl::Vector<float, 3> s_lightPosition = { { -0.7F, 0.0F, 0.0F } };
gl::Vector<float, 4> s_lightBrightness = { { 0.5F, 0.5F, 0.5F, 32.0F } };
gl::Vector<float, 3> s_lightColor = { { 1.0F, 1.0F, 1.0f } };
gl::Matrix<float, 4, 4> s_rotate = gl::Matrix<float, 4, 4>::identity();
ImGuiExt::Texture s_texture;
std::fs::path s_texturePath;
template<typename T>
void indicesForLines(std::vector<T> &vertexIndices) {
std::vector<u32> indices;
u32 vertexCount = vertexIndices.size() / 3;
indices.resize(vertexCount * 6);
for (u32 i = 0; i < vertexCount; ++i) {
indices[i * 6] = vertexIndices[3 * i];
indices[i * 6 + 1] = vertexIndices[3 * i + 1];
indices[i * 6 + 2] = vertexIndices[3 * i + 1];
indices[i * 6 + 3] = vertexIndices[3 * i + 2];
indices[i * 6 + 4] = vertexIndices[3 * i + 2];
indices[i * 6 + 5] = vertexIndices[3 * i];
}
vertexIndices.resize(indices.size());
for (u32 i = 0; i < indices.size(); ++i)
vertexIndices[i] = indices[i];
}
float getBoundingBox(const std::vector<float> &vertices) {
gl::Vector<float, 4> minWorld(std::numeric_limits<float>::infinity()), maxWorld(-std::numeric_limits<float>::infinity());
for (u32 i = 0; i < vertices.size(); i += 3) {
if (vertices[i] < minWorld[0]) minWorld[0] = vertices[i];
if (vertices[i + 1] < minWorld[1]) minWorld[1] = vertices[i + 1];
if (vertices[i + 2] < minWorld[2]) minWorld[2] = vertices[i + 2];
if (vertices[i] > maxWorld[0]) maxWorld[0] = vertices[i];
if (vertices[i + 1] > maxWorld[1]) maxWorld[1] = vertices[i + 1];
if (vertices[i + 2] > maxWorld[2]) maxWorld[2] = vertices[i + 2];
}
minWorld[3] = 1;
maxWorld[3] = 1;
gl::Vector<float, 4> minCamera = minWorld, maxCamera = maxWorld;
if (maxCamera[3] != 0)
maxCamera = maxCamera * (1.0f / maxCamera[3]);
if (minCamera[3] != 0)
minCamera = minCamera * (1.0f / minCamera[3]);
float maxx = std::max(std::fabs(minCamera[0]), std::fabs(maxCamera[0]));
float maxy = std::max(std::fabs(minCamera[1]), std::fabs(maxCamera[1]));
return std::max(maxx, maxy);
}
void setDefaultColors(std::vector<float> &colors, float size, u32 color) {
colors.resize(size / 3 * 4);
float red = float((color >> 0) & 0xFF) / 255.0F;
float green = float((color >> 8) & 0xFF) / 255.0F;
float blue = float((color >> 16) & 0xFF) / 255.0F;
float alpha = float((color >> 24) & 0xFF) / 255.0F;
for (u32 i = 0; i < colors.size(); i += 4) {
colors[i] = red;
colors[i + 1] = green;
colors[i + 2] = blue;
colors[i + 3] = alpha;
}
}
void setNormals(const std::vector<float> &vertices, std::vector<float> &normals) {
for (u32 i = 0; i < normals.size(); i += 9) {
auto v1 = gl::Vector<float, 3>({vertices[i], vertices[i + 1], vertices[i + 2]});
auto v2 = gl::Vector<float, 3>({vertices[i + 3], vertices[i + 4], vertices[i + 5]});
auto v3 = gl::Vector<float, 3>({vertices[i + 6], vertices[i + 7], vertices[i + 8]});
auto normal = ((v2 - v1).cross(v3 - v1));
normals[i] += normal[0];
normals[i + 1] += normal[1];
normals[i + 2] += normal[2];
normals[i + 3] += normal[0];
normals[i + 4] += normal[1];
normals[i + 5] += normal[2];
normals[i + 6] += normal[0];
normals[i + 7] += normal[1];
normals[i + 8] += normal[2];
}
for (u32 i = 0; i < normals.size(); i += 3) {
auto normal = gl::Vector<float, 3>({normals[i], normals[i + 1], normals[i + 2]});
normal.normalize();
normals[i] = normal[0];
normals[i + 1] = normal[1];
normals[i + 2] = normal[2];
}
}
void setNormalsWithIndices(const std::vector<float> &vertices, std::vector<float> &normals, const std::vector<u32> &indices) {
for (u32 i = 0; i < indices.size(); i += 3) {
auto idx = indices[i];
auto idx1 = indices[i + 1];
auto idx2 = indices[i + 2];
auto v1 = gl::Vector<float, 3>({vertices[3 * idx], vertices[3 * idx + 1], vertices[3 * idx + 2]});
auto v2 = gl::Vector<float, 3>(
{vertices[3 * idx1], vertices[3 * idx1 + 1], vertices[3 * idx1 + 2]});
auto v3 = gl::Vector<float, 3>(
{vertices[3 * idx2], vertices[3 * idx2 + 1], vertices[3 * idx2 + 2]});
auto weighted = ((v2 - v1).cross(v3 - v1));
normals[3 * idx] += weighted[0];
normals[3 * idx + 1] += weighted[1];
normals[3 * idx + 2] += weighted[2];
normals[3 * idx1] += weighted[0];
normals[3 * idx1 + 1] += weighted[1];
normals[3 * idx1 + 2] += weighted[2];
normals[3 * idx2] += weighted[0];
normals[3 * idx2 + 1] += weighted[1];
normals[3 * idx2 + 2] += weighted[2];
}
for (u32 i = 0; i < normals.size(); i += 3) {
auto normal = gl::Vector<float, 3>({normals[i], normals[i + 1], normals[i + 2]});
auto mag = normal.magnitude();
if (mag > 0.001F) {
normals[i] = normal[0] / mag;
normals[i + 1] = normal[1] / mag;
normals[i + 2] = normal[2] / mag;
}
}
}
void loadVectors(Vectors &vectors, IndexType indexType) {
s_max = getBoundingBox(vectors.vertices);
if (s_drawTexture)
setDefaultColors(vectors.colors, vectors.vertices.size(), 0x00000000);
else if (vectors.colors.empty())
setDefaultColors(vectors.colors, vectors.vertices.size(), 0xFF337FFF);
if (vectors.normals.empty()) {
vectors.normals.resize(vectors.vertices.size());
if ((indexType == IndexType::U8 && vectors.indices8.empty()) || (indexType == IndexType::Invalid) ||
(indexType == IndexType::U16 && vectors.indices16.empty()) ||
(indexType == IndexType::U32 && vectors.indices32.empty())) {
setNormals(vectors.vertices, vectors.normals);
} else {
std::vector<u32> indices;
if (indexType == IndexType::U16) {
indices.resize(vectors.indices16.size());
for (u32 i = 0; i < vectors.indices16.size(); ++i)
indices[i] = vectors.indices16[i];
} else if (indexType == IndexType::U8) {
indices.resize(vectors.indices8.size());
for (u32 i = 0; i < vectors.indices8.size(); ++i)
indices[i] = vectors.indices8[i];
} else {
indices.resize(vectors.indices32.size());
for (u32 i = 0; i < vectors.indices32.size(); ++i)
indices[i] = vectors.indices32[i];
}
setNormalsWithIndices(vectors.vertices, vectors.normals, indices);
}
}
}
void loadLineVectors(LineVectors &lineVectors, IndexType indexType) {
s_max = getBoundingBox(lineVectors.vertices);
if (lineVectors.colors.empty())
setDefaultColors(lineVectors.colors, lineVectors.vertices.size(), 0xFF337FFF);
std::vector<u32> indices;
if (indexType == IndexType::U8)
indicesForLines(lineVectors.indices8);
else if (indexType == IndexType::U16)
indicesForLines(lineVectors.indices16);
else
indicesForLines(lineVectors.indices32);
}
void processKeyEvent(ImGuiKey key, float &variable, float incr, float accel) {
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(key))) {
auto temp = variable + incr * accel;
if (variable * temp < 0.0F)
variable = 0.0F;
else
variable = temp;
}
}
void processInputEvents(gl::Vector<float, 3> &rotation, gl::Vector<float, 3> &translation, float &scaling, float &nearLimit, float &farLimit) {
auto accel = 1.0F;
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_LeftShift)) ||
ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_RightShift)))
accel = 10.0F;
auto dragDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Middle);
if (dragDelta.x != 0) {
rotation[1] += dragDelta.x * 0.0075F * accel;
}
if (dragDelta.y != 0) {
rotation[0] += dragDelta.y * 0.0075F * accel;
}
ImGui::ResetMouseDragDelta(ImGuiMouseButton_Middle);
dragDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
translation[0] += dragDelta.x * 0.0075F * accel;
translation[1] -= dragDelta.y * 0.0075F * accel;
ImGui::ResetMouseDragDelta(ImGuiMouseButton_Right);
auto scrollDelta = ImGui::GetIO().MouseWheel;
scaling += scrollDelta * 0.1F * accel;
if (scaling < 0.01F)
scaling = 0.01F;
processKeyEvent(ImGuiKey_Keypad4, translation[0], -0.1F, accel);
processKeyEvent(ImGuiKey_Keypad6, translation[0], 0.1F, accel);
processKeyEvent(ImGuiKey_Keypad8, translation[1], 0.1F, accel);
processKeyEvent(ImGuiKey_Keypad2, translation[1], -0.1F, accel);
processKeyEvent(ImGuiKey_Keypad1, translation[2], 0.1F, accel);
processKeyEvent(ImGuiKey_Keypad7, translation[2], -0.1F, accel);
processKeyEvent(ImGuiKey_Keypad9, nearLimit, -0.01F, accel);
processKeyEvent(ImGuiKey_Keypad3, nearLimit, 0.01F, accel);
if (ImHexApi::System::isDebugBuild()) {
processKeyEvent(ImGuiKey_KeypadDivide, farLimit, -1.0F, accel);
processKeyEvent(ImGuiKey_KeypadMultiply, farLimit, 1.0F, accel);
}
processKeyEvent(ImGuiKey_KeypadAdd, rotation[2], -0.075F, accel);
processKeyEvent(ImGuiKey_KeypadSubtract, rotation[2], 0.075F, accel);
rotation[2] = std::fmod(rotation[2], 2 * std::numbers::pi);
}
void bindBuffers(Buffers &buffers, const gl::VertexArray &vertexArray, Vectors vectors, IndexType indexType) {
buffers.vertices = {};
buffers.normals = {};
buffers.colors = {};
buffers.uv1 = {};
buffers.indices8 = {};
buffers.indices16 = {};
buffers.indices32 = {};
vertexArray.bind();
buffers.vertices = gl::Buffer<float>(gl::BufferType::Vertex, vectors.vertices);
buffers.colors = gl::Buffer<float>(gl::BufferType::Vertex, vectors.colors);
buffers.normals = gl::Buffer<float>(gl::BufferType::Vertex, vectors.normals);
if (indexType == IndexType::U8)
buffers.indices8 = gl::Buffer<u8>(gl::BufferType::Index, vectors.indices8);
else if (indexType == IndexType::U16)
buffers.indices16 = gl::Buffer<u16>(gl::BufferType::Index, vectors.indices16);
else
buffers.indices32 = gl::Buffer<u32>(gl::BufferType::Index, vectors.indices32);
if (!vectors.uv1.empty())
buffers.uv1 = gl::Buffer<float>(gl::BufferType::Vertex, vectors.uv1);
vertexArray.addBuffer(0, buffers.vertices);
vertexArray.addBuffer(1, buffers.colors, 4);
vertexArray.addBuffer(2, buffers.normals);
if (!vectors.uv1.empty())
vertexArray.addBuffer(3, buffers.uv1, 2);
buffers.vertices.unbind();
buffers.colors.unbind();
buffers.normals.unbind();
if (!vectors.uv1.empty())
buffers.uv1.unbind();
if (indexType == IndexType::U8)
buffers.indices8.unbind();
else if (indexType == IndexType::U16)
buffers.indices16.unbind();
else if (indexType == IndexType::U32)
buffers.indices32.unbind();
vertexArray.unbind();
}
void bindLineBuffers(LineBuffers &lineBuffers, const gl::VertexArray &vertexArray, const LineVectors &lineVectors, IndexType indexType) {
lineBuffers.vertices = {};
lineBuffers.colors = {};
lineBuffers.indices8 = {};
lineBuffers.indices16 = {};
lineBuffers.indices32 = {};
vertexArray.bind();
lineBuffers.vertices = gl::Buffer<float>(gl::BufferType::Vertex, lineVectors.vertices);
lineBuffers.colors = gl::Buffer<float>(gl::BufferType::Vertex, lineVectors.colors);
if (indexType == IndexType::U8)
lineBuffers.indices8 = gl::Buffer<u8>(gl::BufferType::Index, lineVectors.indices8);
else if (indexType == IndexType::U16)
lineBuffers.indices16 = gl::Buffer<u16>(gl::BufferType::Index, lineVectors.indices16);
else
lineBuffers.indices32 = gl::Buffer<u32>(gl::BufferType::Index, lineVectors.indices32);
vertexArray.addBuffer(0, lineBuffers.vertices);
vertexArray.addBuffer(1, lineBuffers.colors, 4);
lineBuffers.vertices.unbind();
lineBuffers.colors.unbind();
if (indexType == IndexType::U8)
lineBuffers.indices8.unbind();
else if (indexType == IndexType::U16)
lineBuffers.indices16.unbind();
else if (indexType == IndexType::U32)
lineBuffers.indices32.unbind();
vertexArray.unbind();
}
void drawWindow(const ImGuiExt::Texture &texture, ImVec2 &renderingWindowSize, const gl::Matrix<float, 4, 4> &mvp) {
auto textureSize = texture.getSize();
auto textureWidth = textureSize.x;
auto textureHeight = textureSize.y;
ImVec2 screenPos = ImGui::GetCursorScreenPos();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::SetNextWindowSizeConstraints(scaled({ 350, 350 }), ImVec2(FLT_MAX, FLT_MAX));
if (ImGui::BeginChild("##image", textureSize, ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_Border, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
renderingWindowSize = ImGui::GetContentRegionAvail();
ImGui::Image(texture, textureSize, ImVec2(0, 1), ImVec2(1, 0));
if (s_drawAxes) {
gl::Matrix<float, 4, 4> axes = gl::Matrix<float, 4, 4>::identity();
axes(0, 3) = 1.0f;
axes(1, 3) = 1.0f;
axes(2, 3) = 1.0f;
axes = axes * mvp;
bool showX = axes(0, 3) > 0.0f;
bool showY = axes(1, 3) > 0.0f;
bool showZ = axes(2, 3) > 0.0f;
axes.updateRow(0, axes.getRow(0) * (1.0f / axes(0, 3)));
axes.updateRow(1, axes.getRow(1) * (1.0f / axes(1, 3)));
axes.updateRow(2, axes.getRow(2) * (1.0f / axes(2, 3)));
auto axesPosx = (axes.getColumn(0) + 1.0f) * (textureWidth / 2.0f);
auto axesPosy = (axes.getColumn(1) + 1.0f) * (-textureHeight / 2.0f) + textureHeight;
ImDrawList *drawList = ImGui::GetWindowDrawList();
if (showX)
drawList->AddText(ImVec2(axesPosx[0], axesPosy[0]) + screenPos, IM_COL32(255, 0, 0, 255), "X");
if (showY)
drawList->AddText(ImVec2(axesPosx[1], axesPosy[1]) + screenPos, IM_COL32(0, 255, 0, 255), "Y");
if (showZ)
drawList->AddText(ImVec2(axesPosx[2], axesPosy[2]) + screenPos, IM_COL32(0, 0, 255, 255), "Z");
}
if (ImHexApi::System::isDebugBuild()) {
auto mousePos = ImClamp(ImGui::GetMousePos() - screenPos, { 0, 0 }, textureSize);
ImDrawList *drawList = ImGui::GetWindowDrawList();
drawList->AddText(
screenPos + scaled({ 5, 5 }),
ImGui::GetColorU32(ImGuiCol_Text),
hex::format("X: {:.5}\nY: {:.5}", mousePos.x, mousePos.y).c_str());
}
}
ImGui::EndChild();
ImGui::PopStyleVar();
// Draw axis arrows toggle
{
ImGui::PushID(1);
if (ImGuiExt::DimmedIconToggle(ICON_BI_EMPTY_ARROWS, &s_drawAxes))
s_shouldReset = true;
ImGui::PopID();
}
ImGui::SameLine();
// Draw grid toggle
{
ImGui::PushID(2);
if (ImGuiExt::DimmedIconToggle(s_isPerspective ? ICON_BI_GRID : ICON_VS_SYMBOL_NUMBER, &s_drawGrid))
s_shouldReset = true;
ImGui::PopID();
}
ImGui::SameLine();
// Draw light source toggle
{
ImGui::PushID(3);
if (ImGuiExt::DimmedIconToggle(ICON_VS_LIGHTBULB, &s_drawLightSource))
s_shouldReset = true;
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
ImGui::OpenPopup("LightSettings");
}
if (ImGui::BeginPopup("LightSettings")) {
if (ImGui::DragFloat3("hex.builtin.pl_visualizer.3d.light_position"_lang, s_lightPosition.data(), 0.05F)) {
s_shouldUpdateLightSource = true;
}
ImGui::SliderFloat("hex.builtin.pl_visualizer.3d.ambient_brightness"_lang, &s_lightBrightness.data()[0], 0, 2);
ImGui::SliderFloat("hex.builtin.pl_visualizer.3d.diffuse_brightness"_lang, &s_lightBrightness.data()[1], 0, 2);
ImGui::SliderFloat("hex.builtin.pl_visualizer.3d.specular_brightness"_lang, &s_lightBrightness.data()[2], 0, 2);
ImGui::SliderFloat("hex.builtin.pl_visualizer.3d.object_reflectiveness"_lang, &s_lightBrightness.data()[3], 0, 64);
if (ImGui::ColorEdit3("hex.builtin.pl_visualizer.3d.light_color"_lang, s_lightColor.data()))
s_shouldUpdateLightSource = true;
ImGui::EndPopup();
}
ImGui::PopID();
}
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
// Draw projection toggle
{
ImGui::PushID(4);
if (ImGuiExt::DimmedIconToggle(ICON_BI_VIEW_PERSPECTIVE, ICON_BI_VIEW_ORTHO, &s_isPerspective)) {
s_shouldReset = true;
}
ImGui::PopID();
}
ImGui::SameLine();
// Draw solid / line mode toggle
{
ImGui::PushID(4);
bool isSolid = s_drawMode == GL_TRIANGLES;
if (ImGuiExt::DimmedIconToggle(ICON_BI_MOD_SOLIDIFY, ICON_BI_CUBE , &isSolid)) {
s_shouldReset = true;
s_drawMode = isSolid ? GL_TRIANGLES : GL_LINES;
}
ImGui::PopID();
}
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
if (ImGuiExt::DimmedButton("hex.builtin.common.reset"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
s_translation = { { 0.0F, 0.0F, -3.0F } };
s_rotation = { { 0.0F, 0.0F, 0.0F } };
s_scaling = 1.0F;
}
// Draw more settings
if (ImGui::CollapsingHeader("hex.builtin.pl_visualizer.3d.more_settings"_lang)) {
if (ImGuiExt::InputFilePicker("hex.builtin.pl_visualizer.3d.texture_file"_lang, s_texturePath, {}))
s_shouldUpdateTexture = true;
}
}
}
void draw3DVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static gl::LightSourceVectors sourceVectors(20);
static gl::VertexArray sourceVertexArray = gl::VertexArray();
static gl::LightSourceBuffers sourceBuffers(sourceVertexArray, sourceVectors);
static gl::VertexArray gridVertexArray = gl::VertexArray();
static gl::GridVectors gridVectors(9);
static gl::GridBuffers gridBuffers(gridVertexArray, gridVectors);
static gl::VertexArray axesVertexArray = gl::VertexArray();
static gl::AxesVectors axesVectors;
static gl::AxesBuffers axesBuffers(axesVertexArray, axesVectors);
static gl::VertexArray vertexArray = gl::VertexArray();
static Buffers buffers;
static LineBuffers lineBuffers;
std::shared_ptr<pl::ptrn::Pattern> verticesPattern = arguments[0].toPattern();
std::shared_ptr<pl::ptrn::Pattern> indicesPattern = arguments[1].toPattern();
std::shared_ptr<pl::ptrn::Pattern> normalsPattern = nullptr;
std::shared_ptr<pl::ptrn::Pattern> colorsPattern = nullptr;
std::shared_ptr<pl::ptrn::Pattern> uvPattern1 = nullptr;
std::string textureFile;
if (arguments.size() > 2) {
normalsPattern = arguments[2].toPattern();
if (arguments.size() > 3) {
colorsPattern = arguments[3].toPattern();
if (arguments.size() > 4) {
uvPattern1 = arguments[4].toPattern();
if (arguments.size() > 5)
textureFile = arguments[5].toString();
}
}
}
if (shouldReset)
s_shouldReset = true;
const auto fontSize = ImGui::GetFontSize();
const auto framePad = ImGui::GetStyle().FramePadding;
float minSize = fontSize * 8_scaled + framePad.x * 20_scaled;
minSize = minSize > 200_scaled ? minSize : 200_scaled;
if (s_renderingWindowSize.x <= 0 || s_renderingWindowSize.y <= 0)
s_renderingWindowSize = { minSize, minSize };
if (!textureFile.empty())
s_texturePath = textureFile;
else
s_drawTexture = false;
if (s_renderingWindowSize.x < minSize)
s_renderingWindowSize.x = minSize;
if (s_renderingWindowSize.y < minSize)
s_renderingWindowSize.y = minSize;
gl::Matrix<float, 4, 4> mvp(0);
processInputEvents(s_rotation, s_translation, s_scaling, s_nearLimit, s_farLimit);
if (s_shouldReset) {
s_shouldReset = false;
auto *iterable = dynamic_cast<pl::ptrn::IIterable*>(indicesPattern.get());
if (iterable != nullptr && iterable->getEntryCount() > 0) {
const auto &content = iterable->getEntry(0);
if (content->getSize() == 1) {
s_indexType = IndexType::U8;
} else if (content->getSize() == 2) {
s_indexType = IndexType::U16;
} else if (content->getSize() == 4) {
s_indexType = IndexType::U32;
} else {
s_indexType = IndexType::Invalid;
}
}
if (s_drawMode == GL_TRIANGLES) {
Vectors vectors;
vectors.vertices = patternToArray<float>(verticesPattern.get());
if (s_indexType == IndexType::U16)
vectors.indices16 = patternToArray<u16>(indicesPattern.get());
else if (s_indexType == IndexType::U32)
vectors.indices32 = patternToArray<u32>(indicesPattern.get());
else if (s_indexType == IndexType::U8)
vectors.indices8 = patternToArray<u8>(indicesPattern.get());
if (colorsPattern != nullptr)
vectors.colors = patternToArray<float>(colorsPattern.get());
if (normalsPattern != nullptr)
vectors.normals = patternToArray<float>(normalsPattern.get());
if (uvPattern1 != nullptr)
vectors.uv1 = patternToArray<float>(uvPattern1.get());
loadVectors(vectors, s_indexType);
bindBuffers(buffers, vertexArray, vectors, s_indexType);
} else {
LineVectors lineVectors;
lineVectors.vertices = patternToArray<float>(verticesPattern.get());
if (s_indexType == IndexType::U16)
lineVectors.indices16 = patternToArray<u16>(indicesPattern.get());
else if (s_indexType == IndexType::U32)
lineVectors.indices32 = patternToArray<u32>(indicesPattern.get());
else if (s_indexType == IndexType::U8)
lineVectors.indices8 = patternToArray<u8>(indicesPattern.get());
if (colorsPattern != nullptr)
lineVectors.colors = patternToArray<float>(colorsPattern.get());
loadLineVectors(lineVectors, s_indexType);
bindLineBuffers(lineBuffers, vertexArray, lineVectors, s_indexType);
}
}
if (s_shouldUpdateLightSource) {
s_shouldUpdateLightSource = false;
sourceVectors.moveTo(s_lightPosition);
sourceVectors.setColor(s_lightColor[0], s_lightColor[1], s_lightColor[2]);
sourceBuffers.moveVertices(sourceVertexArray, sourceVectors);
sourceBuffers.updateColors(sourceVertexArray, sourceVectors);
}
{
gl::Matrix<float, 4, 4> model(0);
gl::Matrix<float, 4, 4> scaledModel(0);
gl::Matrix<float, 4, 4> view(0);
gl::Matrix<float, 4, 4> projection(0);
unsigned width = std::floor(s_renderingWindowSize.x);
unsigned height = std::floor(s_renderingWindowSize.y);
gl::FrameBuffer frameBuffer(width, height);
gl::Texture renderTexture(width, height);
frameBuffer.attachTexture(renderTexture);
frameBuffer.bind();
s_rotate = gl::getRotationMatrix<float>(s_rotation, true, gl::RotationSequence::ZYX);
gl::Matrix<float, 4, 4> scale = gl::Matrix<float, 4, 4>::identity();
gl::Matrix<float, 4, 4> scaleForVertices = gl::Matrix<float, 4, 4>::identity();
gl::Matrix<float, 4, 4> translate = gl::Matrix<float, 4, 4>::identity();
float totalScale;
float viewWidth = s_renderingWindowSize.x / 500.0f;
float viewHeight = s_renderingWindowSize.y / 500.0f;
glViewport(0,0 , GLsizei(renderTexture.getWidth()), GLsizei(renderTexture.getHeight()));
glDepthRangef(s_nearLimit, s_farLimit);
glClearColor(0.00F, 0.00F, 0.00F, 0.00f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
if (s_isPerspective == 0) {
projection = gl::GetOrthographicMatrix(viewWidth, viewHeight, s_nearLimit, s_farLimit, false);
totalScale = s_scaling / (std::fabs(s_translation[2]));
scale(0, 0) = totalScale;
scale(1, 1) = totalScale;
scale(2, 2) = totalScale;
translate(3, 0) = s_translation[0] / std::fabs(s_translation[2]);
translate(3, 1) = s_translation[1] / std::fabs(s_translation[2]);
translate(3, 2) = s_translation[2];
} else {
projection = gl::GetPerspectiveMatrix(viewWidth, viewHeight, s_nearLimit, s_farLimit, false);
totalScale = s_scaling;
scale(0, 0) = totalScale;
scale(1, 1) = totalScale;
scale(2, 2) = totalScale;
translate(3, 0) = s_translation[0];
translate(3, 1) = s_translation[1];
translate(3, 2) = s_translation[2];
}
totalScale /= (3.0f * s_max);
scaleForVertices(0, 0) = totalScale;
scaleForVertices(1, 1) = totalScale;
scaleForVertices(2, 2) = totalScale;
model = s_rotate * scale;
scaledModel = s_rotate * scaleForVertices;
view = translate;
mvp = model * view * projection;
if (s_drawMode == GL_TRIANGLES) {
static gl::Shader shader = gl::Shader(romfs::get("shaders/default/vertex.glsl").string(),
romfs::get("shaders/default/fragment.glsl").string());
shader.bind();
shader.setUniform("modelScale", scaledModel);
shader.setUniform("modelMatrix", model);
shader.setUniform("viewMatrix", view);
shader.setUniform("projectionMatrix",projection);
shader.setUniform("lightPosition", s_lightPosition);
shader.setUniform("lightBrightness", s_lightBrightness);
shader.setUniform("lightColor", s_lightColor);
vertexArray.bind();
if (s_shouldUpdateTexture) {
s_shouldUpdateTexture = false;
s_modelTexture = ImGuiExt::Texture(s_texturePath);
}
if (s_drawTexture)
glBindTexture(GL_TEXTURE_2D, s_modelTexture);
if (s_indexType == IndexType::U8) {
buffers.indices8.bind();
if (buffers.indices8.getSize() == 0)
buffers.vertices.draw(s_drawMode);
else
buffers.indices8.draw(s_drawMode);
buffers.indices8.unbind();
} else if (s_indexType == IndexType::U16) {
buffers.indices16.bind();
if (buffers.indices16.getSize() == 0)
buffers.vertices.draw(s_drawMode);
else
buffers.indices16.draw(s_drawMode);
buffers.indices16.unbind();
} else {
buffers.indices32.bind();
if (buffers.indices32.getSize() == 0)
buffers.vertices.draw(s_drawMode);
else
buffers.indices32.draw(s_drawMode);
buffers.indices32.unbind();
}
} else {
static gl::Shader lineShader = gl::Shader(
romfs::get("shaders/default/lineVertex.glsl").string(),
romfs::get("shaders/default/lineFragment.glsl").string());
lineShader.bind();
lineShader.setUniform("modelMatrix", scaledModel);
lineShader.setUniform("viewMatrix", view);
lineShader.setUniform("projectionMatrix", projection);
vertexArray.bind();
if (s_indexType == IndexType::U8) {
lineBuffers.indices8.bind();
if (lineBuffers.indices8.getSize() == 0)
lineBuffers.vertices.draw(s_drawMode);
else
lineBuffers.indices8.draw(s_drawMode);
lineBuffers.indices8.unbind();
} else if (s_indexType == IndexType::U16) {
lineBuffers.indices16.bind();
if (lineBuffers.indices16.getSize() == 0)
lineBuffers.vertices.draw(s_drawMode);
else
lineBuffers.indices16.draw(s_drawMode);
lineBuffers.indices16.unbind();
} else {
lineBuffers.indices32.bind();
if (lineBuffers.indices32.getSize() == 0)
lineBuffers.vertices.draw(s_drawMode);
else
lineBuffers.indices32.draw(s_drawMode);
lineBuffers.indices32.unbind();
}
}
if (s_drawGrid || s_drawAxes) {
static auto gridAxesShader = gl::Shader(
romfs::get("shaders/default/lineVertex.glsl").string(),
romfs::get("shaders/default/lineFragment.glsl").string());
gridAxesShader.bind();
gridAxesShader.setUniform("modelMatrix", model);
gridAxesShader.setUniform("viewMatrix", view);
gridAxesShader.setUniform("projectionMatrix", projection);
if (s_drawGrid) {
gridVertexArray.bind();
gridBuffers.getIndices().bind();
gridBuffers.getIndices().draw(GL_LINES);
gridBuffers.getIndices().unbind();
gridVertexArray.unbind();
}
if (s_drawAxes) {
axesVertexArray.bind();
axesBuffers.getIndices().bind();
axesBuffers.getIndices().draw(GL_LINES);
axesBuffers.getIndices().unbind();
axesVertexArray.unbind();
}
gridAxesShader.unbind();
}
if (s_drawLightSource) {
static auto sourceShader = gl::Shader(
romfs::get("shaders/default/lightVertex.glsl").string(),
romfs::get("shaders/default/lightFragment.glsl").string());
sourceShader.bind();
sourceShader.setUniform("modelMatrix", model);
sourceShader.setUniform("viewMatrix", view);
sourceShader.setUniform("projectionMatrix", projection);
sourceVertexArray.bind();
sourceBuffers.getIndices().bind();
sourceBuffers.getIndices().draw(GL_TRIANGLES);
sourceBuffers.getIndices().unbind();
sourceVertexArray.unbind();
sourceShader.unbind();
}
vertexArray.unbind();
frameBuffer.unbind();
s_texture = ImGuiExt::Texture(renderTexture.release(), GLsizei(renderTexture.getWidth()), GLsizei(renderTexture.getHeight()));
drawWindow(s_texture, s_renderingWindowSize, mvp);
}
}
}

View File

@ -0,0 +1,28 @@
#include <hex/helpers/utils.hpp>
#include <content/pl_visualizers/visualizer_helpers.hpp>
#include <implot.h>
#include <imgui.h>
#include <content/helpers/diagrams.hpp>
#include <hex/ui/imgui_imhex_extensions.h>
namespace hex::plugin::builtin {
void drawChunkBasedEntropyVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
// Variable used to store the result to avoid having to recalculate the result at each frame
static DiagramChunkBasedEntropyAnalysis analyzer;
// Compute data
if (shouldReset) {
auto pattern = arguments[0].toPattern();
auto chunkSize = arguments[1].toUnsigned();
analyzer.process(pattern->getBytes(), chunkSize);
}
// Show results
analyzer.draw(ImVec2(400, 250), ImPlotFlags_CanvasOnly);
}
}

View File

@ -0,0 +1,116 @@
#include <hex/helpers/utils.hpp>
#include <content/pl_visualizers/visualizer_helpers.hpp>
#include <imgui.h>
#include <hex/api/task_manager.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/helpers/http_requests.hpp>
#include <nlohmann/json.hpp>
#include <hex/ui/imgui_imhex_extensions.h>
#include <romfs/romfs.hpp>
namespace hex::plugin::builtin {
void drawCoordinateVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static ImVec2 coordinate;
static double latitude, longitude;
static std::string address;
static std::mutex addressMutex;
static TaskHolder addressTask;
static auto mapTexture = ImGuiExt::Texture(romfs::get("assets/common/map.jpg").span());
static ImVec2 mapSize = scaled(ImVec2(500, 500 / mapTexture.getAspectRatio()));
if (shouldReset) {
std::scoped_lock lock(addressMutex);
address.clear();
latitude = arguments[0].toFloatingPoint();
longitude = arguments[1].toFloatingPoint();
// Convert latitude and longitude to X/Y coordinates on the image
coordinate.x = float((longitude + 180) / 360 * mapSize.x);
coordinate.y = float((-latitude + 90) / 180 * mapSize.y);
}
const auto startPos = ImGui::GetWindowPos() + ImGui::GetCursorPos();
// Draw background image
ImGui::Image(mapTexture, mapSize);
// Draw Longitude / Latitude text below image
ImGui::PushTextWrapPos(startPos.x + mapSize.x);
ImGuiExt::TextFormattedWrapped("{}: {:.0f}° {:.0f}' {:.4f}\" {} | {}: {:.0f}° {:.0f}' {:.4f}\" {}",
"hex.builtin.pl_visualizer.coordinates.latitude"_lang,
std::floor(std::abs(latitude)),
std::floor(std::abs(latitude - std::floor(latitude)) * 60),
(std::abs(latitude - std::floor(latitude)) * 60 - std::floor(std::abs(latitude - std::floor(latitude)) * 60)) * 60,
latitude >= 0 ? "N" : "S",
"hex.builtin.pl_visualizer.coordinates.longitude"_lang,
std::floor(std::abs(longitude)),
std::floor(std::abs(longitude - std::floor(longitude)) * 60),
(std::abs(longitude - std::floor(longitude)) * 60 - std::floor(std::abs(longitude - std::floor(longitude)) * 60)) * 60,
longitude >= 0 ? "E" : "W"
);
ImGui::PopTextWrapPos();
if (addressTask.isRunning()) {
ImGuiExt::TextSpinner("hex.builtin.pl_visualizer.coordinates.querying"_lang);
} else if (address.empty()) {
if (ImGuiExt::DimmedButton("hex.builtin.pl_visualizer.coordinates.query"_lang)) {
addressTask = TaskManager::createBackgroundTask("hex.builtin.pl_visualizer.coordinates.querying"_lang, [lat = latitude, lon = longitude](auto &) {
constexpr static auto ApiURL = "https://geocode.maps.co/reverse?lat={}&lon={}&format=jsonv2";
HttpRequest request("GET", hex::format(ApiURL, lat, lon));
auto response = request.execute().get();
if (!response.isSuccess())
return;
try {
auto json = nlohmann::json::parse(response.getData());
auto jsonAddr = json["address"];
std::scoped_lock lock(addressMutex);
if (jsonAddr.contains("village")) {
address = hex::format("{} {}, {} {}",
jsonAddr["village"].get<std::string>(),
jsonAddr["county"].get<std::string>(),
jsonAddr["state"].get<std::string>(),
jsonAddr["country"].get<std::string>());
} else if (jsonAddr.contains("city")) {
address = hex::format("{}, {} {}, {} {}",
jsonAddr["road"].get<std::string>(),
jsonAddr["quarter"].get<std::string>(),
jsonAddr["city"].get<std::string>(),
jsonAddr["state"].get<std::string>(),
jsonAddr["country"].get<std::string>());
}
} catch (std::exception &) {
address = std::string("hex.builtin.pl_visualizer.coordinates.querying_no_address"_lang);
}
});
}
} else {
ImGui::PushTextWrapPos(startPos.x + mapSize.x);
ImGuiExt::TextFormattedWrapped("{}", address);
ImGui::PopTextWrapPos();
}
// Draw crosshair pointing to the coordinates
{
constexpr static u32 CrossHairColor = 0xFF00D0D0;
constexpr static u32 BorderColor = 0xFF000000;
auto drawList = ImGui::GetWindowDrawList();
drawList->AddLine(startPos + ImVec2(coordinate.x, 0), startPos + ImVec2(coordinate.x, mapSize.y), CrossHairColor, 2_scaled);
drawList->AddLine(startPos + ImVec2(0, coordinate.y), startPos + ImVec2(mapSize.x, coordinate.y), CrossHairColor, 2_scaled);
drawList->AddCircleFilled(startPos + coordinate, 5, CrossHairColor);
drawList->AddCircle(startPos + coordinate, 5, BorderColor);
}
}
}

View File

@ -0,0 +1,70 @@
#include <hex/helpers/utils.hpp>
#include <content/pl_visualizers/visualizer_helpers.hpp>
#include <imgui.h>
#include <capstone/capstone.h>
#include <hex/ui/imgui_imhex_extensions.h>
#include <hex/api/localization_manager.hpp>
namespace hex::plugin::builtin {
void drawDisassemblyVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
struct Disassembly {
u64 address;
std::vector<u8> bytes;
std::string instruction;
};
static std::vector<Disassembly> disassembly;
if (shouldReset) {
auto pattern = arguments[0].toPattern();
auto baseAddress = arguments[1].toUnsigned();
auto architecture = arguments[2].toUnsigned();
auto mode = arguments[3].toUnsigned();
disassembly.clear();
csh capstone;
if (cs_open(static_cast<cs_arch>(architecture), static_cast<cs_mode>(mode), &capstone) == CS_ERR_OK) {
cs_option(capstone, CS_OPT_SKIPDATA, CS_OPT_ON);
auto data = pattern->getBytes();
cs_insn *instructions = nullptr;
size_t instructionCount = cs_disasm(capstone, data.data(), data.size(), baseAddress, 0, &instructions);
for (size_t i = 0; i < instructionCount; i++) {
disassembly.push_back({ instructions[i].address, { instructions[i].bytes, instructions[i].bytes + instructions[i].size }, hex::format("{} {}", instructions[i].mnemonic, instructions[i].op_str) });
}
cs_free(instructions, instructionCount);
cs_close(&capstone);
}
}
if (ImGui::BeginTable("##disassembly", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY, scaled(ImVec2(0, 300)))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.builtin.common.address"_lang);
ImGui::TableSetupColumn("hex.builtin.common.bytes"_lang);
ImGui::TableSetupColumn("hex.builtin.common.instruction"_lang);
ImGui::TableHeadersRow();
for (auto &entry : disassembly) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGuiExt::TextFormatted("0x{0:08X}", entry.address);
ImGui::TableNextColumn();
std::string bytes;
for (auto byte : entry.bytes)
bytes += hex::format("{0:02X} ", byte);
ImGui::TextUnformatted(bytes.c_str());
ImGui::TableNextColumn();
ImGui::TextUnformatted(entry.instruction.c_str());
}
ImGui::EndTable();
}
}
}

View File

@ -0,0 +1,43 @@
#include <hex/helpers/utils.hpp>
#include <content/pl_visualizers/visualizer_helpers.hpp>
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
#include <ui/hex_editor.hpp>
#include <content/providers/memory_file_provider.hpp>
namespace hex::plugin::builtin {
void drawHexVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static ui::HexEditor editor;
static std::unique_ptr<MemoryFileProvider> dataProvider;
if (shouldReset) {
auto pattern = arguments[0].toPattern();
std::vector<u8> data;
dataProvider = std::make_unique<MemoryFileProvider>();
try {
data = pattern->getBytes();
} catch (const std::exception &) {
dataProvider->resize(0);
throw;
}
dataProvider->resize(data.size());
dataProvider->writeRaw(0x00, data.data(), data.size());
dataProvider->setReadOnly(true);
editor.setProvider(dataProvider.get());
}
if (ImGui::BeginChild("##editor", scaled(ImVec2(600, 400)), false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
editor.draw();
ImGui::EndChild();
}
}
}

View File

@ -0,0 +1,61 @@
#include <hex/helpers/utils.hpp>
#include <content/pl_visualizers/visualizer_helpers.hpp>
#include <implot.h>
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
namespace hex::plugin::builtin {
void drawImageVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static ImGuiExt::Texture texture;
static float scale = 1.0F;
if (shouldReset) {
auto pattern = arguments[0].toPattern();
auto data = pattern->getBytes();
texture = ImGuiExt::Texture(data.data(), data.size());
scale = 200_scaled / texture.getSize().x;
}
if (texture.isValid())
ImGui::Image(texture, texture.getSize() * scale);
if (ImGui::IsWindowHovered()) {
auto scrollDelta = ImGui::GetIO().MouseWheel;
if (scrollDelta != 0.0F) {
scale += scrollDelta * 0.1F;
scale = std::clamp(scale, 0.1F, 10.0F);
}
}
}
void drawBitmapVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static ImGuiExt::Texture texture;
static float scale = 1.0F;
if (shouldReset) {
auto pattern = arguments[0].toPattern();
auto width = arguments[1].toUnsigned();
auto height = arguments[2].toUnsigned();
auto data = pattern->getBytes();
texture = ImGuiExt::Texture(data.data(), data.size(), width, height);
}
if (texture.isValid())
ImGui::Image(texture, texture.getSize() * scale);
if (ImGui::IsWindowHovered()) {
auto scrollDelta = ImGui::GetIO().MouseWheel;
if (scrollDelta != 0.0F) {
scale += scrollDelta * 0.1F;
scale = std::clamp(scale, 0.1F, 10.0F);
}
}
}
}

View File

@ -0,0 +1,30 @@
#include <hex/helpers/utils.hpp>
#include <content/pl_visualizers/visualizer_helpers.hpp>
#include <implot.h>
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
namespace hex::plugin::builtin {
void drawLinePlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static std::vector<float> values;
auto dataPattern = arguments[0].toPattern();
if (ImPlot::BeginPlot("##plot", ImVec2(400, 250), ImPlotFlags_CanvasOnly)) {
ImPlot::SetupAxes("X", "Y", ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit);
if (shouldReset) {
values.clear();
values = sampleData(patternToArray<float>(dataPattern.get()), ImPlot::GetPlotSize().x * 4);
}
ImPlot::PlotLine("##line", values.data(), values.size());
ImPlot::EndPlot();
}
}
}

View File

@ -0,0 +1,33 @@
#include <hex/helpers/utils.hpp>
#include <content/pl_visualizers/visualizer_helpers.hpp>
#include <implot.h>
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
namespace hex::plugin::builtin {
void drawScatterPlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
static std::vector<float> xValues, yValues;
auto xPattern = arguments[0].toPattern();
auto yPattern = arguments[1].toPattern();
if (ImPlot::BeginPlot("##plot", ImVec2(400, 250), ImPlotFlags_CanvasOnly)) {
ImPlot::SetupAxes("X", "Y", ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit);
if (shouldReset) {
xValues.clear(); yValues.clear();
xValues = sampleData(patternToArray<float>(xPattern.get()), ImPlot::GetPlotSize().x * 4);
yValues = sampleData(patternToArray<float>(yPattern.get()), ImPlot::GetPlotSize().x * 4);
}
ImPlot::PlotScatter("##scatter", xValues.data(), yValues.data(), xValues.size());
ImPlot::EndPlot();
}
}
}

View File

@ -0,0 +1,124 @@
#include <hex/helpers/utils.hpp>
#include <content/pl_visualizers/visualizer_helpers.hpp>
#include <implot.h>
#include <imgui.h>
#include <miniaudio.h>
#include <fonts/codicons_font.h>
#include <hex/api/task_manager.hpp>
#include <hex/ui/imgui_imhex_extensions.h>
namespace hex::plugin::builtin {
void drawSoundVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
auto wavePattern = arguments[0].toPattern();
auto channels = arguments[1].toUnsigned();
auto sampleRate = arguments[2].toUnsigned();
static std::vector<i16> waveData, sampledData;
static ma_device audioDevice;
static ma_device_config deviceConfig;
static bool shouldStop = false;
static u64 index = 0;
static TaskHolder resetTask;
if (sampleRate == 0)
throw std::logic_error(hex::format("Invalid sample rate: {}", sampleRate));
else if (channels == 0)
throw std::logic_error(hex::format("Invalid channel count: {}", channels));
if (shouldReset) {
waveData.clear();
resetTask = TaskManager::createTask("Visualizing...", TaskManager::NoProgress, [=](Task &) {
ma_device_stop(&audioDevice);
waveData = patternToArray<i16>(wavePattern.get());
sampledData = sampleData(waveData, 300_scaled * 4);
index = 0;
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.format = ma_format_s16;
deviceConfig.playback.channels = channels;
deviceConfig.sampleRate = sampleRate;
deviceConfig.pUserData = &waveData;
deviceConfig.dataCallback = [](ma_device *device, void *pOutput, const void *, ma_uint32 frameCount) {
if (index >= waveData.size()) {
index = 0;
shouldStop = true;
return;
}
ma_copy_pcm_frames(pOutput, waveData.data() + index, frameCount, device->playback.format, device->playback.channels);
index += frameCount;
};
ma_device_init(nullptr, &deviceConfig, &audioDevice);
});
}
ImGui::BeginDisabled(resetTask.isRunning());
ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0, 0));
if (ImPlot::BeginPlot("##amplitude_plot", scaled(ImVec2(300, 80)), ImPlotFlags_CanvasOnly | ImPlotFlags_NoFrame | ImPlotFlags_NoInputs)) {
ImPlot::SetupAxes("##time", "##amplitude", ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoMenus, ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoMenus);
ImPlot::SetupAxesLimits(0, waveData.size(), std::numeric_limits<i16>::min(), std::numeric_limits<i16>::max(), ImGuiCond_Always);
double dragPos = index;
if (ImPlot::DragLineX(1, &dragPos, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
if (dragPos < 0) dragPos = 0;
if (dragPos >= waveData.size()) dragPos = waveData.size() - 1;
index = dragPos;
}
ImPlot::PlotLine("##audio", sampledData.data(), sampledData.size());
ImPlot::EndPlot();
}
ImPlot::PopStyleVar();
{
const u64 min = 0, max = waveData.size();
ImGui::PushItemWidth(300_scaled);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::SliderScalar("##index", ImGuiDataType_U64, &index, &min, &max, "");
ImGui::PopStyleVar();
ImGui::PopItemWidth();
}
if (shouldStop) {
shouldStop = false;
ma_device_stop(&audioDevice);
}
bool playing = ma_device_is_started(&audioDevice);
if (ImGuiExt::IconButton(playing ? ICON_VS_DEBUG_PAUSE : ICON_VS_PLAY, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen))) {
if (playing)
ma_device_stop(&audioDevice);
else
ma_device_start(&audioDevice);
}
ImGui::SameLine();
if (ImGuiExt::IconButton(ICON_VS_DEBUG_STOP, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) {
index = 0;
ma_device_stop(&audioDevice);
}
ImGui::EndDisabled();
ImGui::SameLine();
if (resetTask.isRunning())
ImGuiExt::TextSpinner("");
else
ImGuiExt::TextFormatted("{:02d}:{:02d} / {:02d}:{:02d}",
(index / sampleRate) / 60, (index / sampleRate) % 60,
(waveData.size() / sampleRate) / 60, (waveData.size() / sampleRate) % 60);
}
}

View File

@ -0,0 +1,107 @@
#include <hex/helpers/utils.hpp>
#include <content/pl_visualizers/visualizer_helpers.hpp>
#include <numbers>
#include <imgui.h>
#include <hex/api/imhex_api.hpp>
#include <hex/ui/imgui_imhex_extensions.h>
#include <chrono>
#include <fmt/chrono.h>
namespace hex::plugin::builtin {
void drawTimestampVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span<const pl::core::Token::Literal> arguments) {
time_t timestamp = arguments[0].toUnsigned();
auto tm = fmt::gmtime(timestamp);
auto date = std::chrono::year_month_day(std::chrono::year(tm.tm_year + 1900), std::chrono::month(tm.tm_mon + 1), std::chrono::day(tm.tm_mday));
auto lastMonthDay = std::chrono::year_month_day_last(date.year(), date.month() / std::chrono::last);
auto firstWeekDay = std::chrono::weekday(std::chrono::year_month_day(date.year(), date.month(), std::chrono::day(1)));
const auto scale = 1_scaled * (ImHexApi::Fonts::getFontSize() / ImHexApi::Fonts::DefaultFontSize);
// Draw calendar
if (ImGui::BeginTable("##month_table", 2)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
// Draw centered month name and year
ImGuiExt::TextFormattedCenteredHorizontal("{:%B %Y}", tm);
if (ImGui::BeginTable("##days_table", 7, ImGuiTableFlags_Borders | ImGuiTableFlags_NoHostExtendX, ImVec2(160, 120) * scale)) {
constexpr static auto ColumnFlags = ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoReorder | ImGuiTableColumnFlags_NoHide;
ImGui::TableSetupColumn("M", ColumnFlags);
ImGui::TableSetupColumn("T", ColumnFlags);
ImGui::TableSetupColumn("W", ColumnFlags);
ImGui::TableSetupColumn("T", ColumnFlags);
ImGui::TableSetupColumn("F", ColumnFlags);
ImGui::TableSetupColumn("S", ColumnFlags);
ImGui::TableSetupColumn("S", ColumnFlags);
ImGui::TableHeadersRow();
ImGui::TableNextRow();
// Skip days before the first day of the month
for (u8 i = 0; i < firstWeekDay.c_encoding() - 1; ++i)
ImGui::TableNextColumn();
// Draw days
for (u8 i = 1; i <= u32(lastMonthDay.day()); ++i) {
ImGui::TableNextColumn();
ImGuiExt::TextFormatted("{:02}", i);
if (std::chrono::day(i) == date.day())
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarRed));
if (std::chrono::weekday(std::chrono::year_month_day(date.year(), date.month(), std::chrono::day(i))) == std::chrono::Sunday)
ImGui::TableNextRow();
}
ImGui::EndTable();
}
ImGui::TableNextColumn();
// Draw analog clock
const auto size = ImVec2(120, 120) * scale;
if (ImGui::BeginChild("##clock", size + ImVec2(0, ImGui::GetTextLineHeightWithSpacing()))) {
// Draw centered digital hour, minute and seconds
ImGuiExt::TextFormattedCenteredHorizontal("{:%H:%M:%S}", tm);
auto drawList = ImGui::GetWindowDrawList();
const auto center = ImGui::GetWindowPos() + ImVec2(0, ImGui::GetTextLineHeightWithSpacing()) + size / 2;
// Draw clock face
drawList->AddCircle(center, size.x / 2, ImGui::GetColorU32(ImGuiCol_TextDisabled), 0);
auto sectionPos = [](float i) {
return ImVec2(std::sin(-i * 30.0F * std::numbers::pi / 180.0F + std::numbers::pi / 2), std::cos(-i * 30.0F * std::numbers::pi / 180.0F + std::numbers::pi / 2));
};
// Draw clock sections and numbers
for (u8 i = 0; i < 12; ++i) {
auto text = hex::format("{}", (((i + 2) % 12) + 1));
drawList->AddLine(center + sectionPos(i) * size / 2.2, center + sectionPos(i) * size / 2, ImGui::GetColorU32(ImGuiCol_TextDisabled), 1_scaled);
drawList->AddText(center + sectionPos(i) * size / 3 - ImGui::CalcTextSize(text.c_str()) / 2, ImGui::GetColorU32(ImGuiCol_Text), text.c_str());
}
// Draw hour hand
drawList->AddLine(center, center + sectionPos((tm.tm_hour + 9) % 12 + float(tm.tm_min) / 60.0) * size / 3.5, ImGui::GetColorU32(ImGuiCol_TextDisabled), 3_scaled);
// Draw minute hand
drawList->AddLine(center, center + sectionPos((float(tm.tm_min) / 5.0F) - 3) * size / 2.5, ImGui::GetColorU32(ImGuiCol_TextDisabled), 3_scaled);
// Draw second hand
drawList->AddLine(center, center + sectionPos((float(tm.tm_sec) / 5.0F) - 3) * size / 2.5, ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarRed), 2_scaled);
}
ImGui::EndChild();
ImGui::EndTable();
}
}
}