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:
parent
d5a40d46bc
commit
f9a9ed4846
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
19
lib/third_party/imgui/fonts/include/fonts/blendericons_font.h
vendored
Normal file
19
lib/third_party/imgui/fonts/include/fonts/blendericons_font.h
vendored
Normal 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
|
@ -1,4 +1,5 @@
|
|||||||
#include "init/tasks.hpp"
|
#include "init/tasks.hpp"
|
||||||
|
#include "misc/freetype/imgui_freetype.h"
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
plugins/builtin/romfs/fonts/blendericons.ttf
Normal file
BIN
plugins/builtin/romfs/fonts/blendericons.ttf
Normal file
Binary file not shown.
@ -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",
|
||||||
|
@ -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;
|
|
||||||
}
|
|
22
plugins/builtin/romfs/shaders/default/lightFragment.glsl
Normal file
22
plugins/builtin/romfs/shaders/default/lightFragment.glsl
Normal 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);
|
||||||
|
}
|
27
plugins/builtin/romfs/shaders/default/lightVertex.glsl
Normal file
27
plugins/builtin/romfs/shaders/default/lightVertex.glsl
Normal 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;
|
||||||
|
}
|
8
plugins/builtin/romfs/shaders/default/lineFragment.glsl
Normal file
8
plugins/builtin/romfs/shaders/default/lineFragment.glsl
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec4 fragColor;
|
||||||
|
out vec4 outColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
outColor = fragColor;
|
||||||
|
}
|
15
plugins/builtin/romfs/shaders/default/lineVertex.glsl
Normal file
15
plugins/builtin/romfs/shaders/default/lineVertex.glsl
Normal 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;
|
||||||
|
}
|
@ -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));
|
||||||
}
|
}
|
@ -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) }
|
||||||
|
@ -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;
|
||||||
|
@ -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));
|
||||||
|
908
plugins/builtin/source/content/pl_visualizers/3d_model.cpp
Normal file
908
plugins/builtin/source/content/pl_visualizers/3d_model.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
116
plugins/builtin/source/content/pl_visualizers/coordinates.cpp
Normal file
116
plugins/builtin/source/content/pl_visualizers/coordinates.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
43
plugins/builtin/source/content/pl_visualizers/hex_viewer.cpp
Normal file
43
plugins/builtin/source/content/pl_visualizers/hex_viewer.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
plugins/builtin/source/content/pl_visualizers/image.cpp
Normal file
61
plugins/builtin/source/content/pl_visualizers/image.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
plugins/builtin/source/content/pl_visualizers/line_plot.cpp
Normal file
30
plugins/builtin/source/content/pl_visualizers/line_plot.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
124
plugins/builtin/source/content/pl_visualizers/sound.cpp
Normal file
124
plugins/builtin/source/content/pl_visualizers/sound.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
107
plugins/builtin/source/content/pl_visualizers/timestamp.cpp
Normal file
107
plugins/builtin/source/content/pl_visualizers/timestamp.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user