diff --git a/lib/libimhex/include/hex/helpers/opengl.hpp b/lib/libimhex/include/hex/helpers/opengl.hpp index cd41c71ea..69307b381 100644 --- a/lib/libimhex/include/hex/helpers/opengl.hpp +++ b/lib/libimhex/include/hex/helpers/opengl.hpp @@ -5,12 +5,14 @@ #include #include +#include #include #include #include #include #include +#include "imgui.h" namespace hex::gl { @@ -20,6 +22,10 @@ namespace hex::gl { GLuint getType() { if constexpr (std::is_same_v) return GL_FLOAT; + else if constexpr (std::is_same_v) + return GL_UNSIGNED_BYTE; + else if constexpr (std::is_same_v) + return GL_UNSIGNED_SHORT; else if constexpr (std::is_same_v) return GL_UNSIGNED_INT; else { @@ -34,16 +40,74 @@ namespace hex::gl { class Vector { public: Vector() = default; + Vector(const T val) { + for (size_t i = 0; i < Size; i++) + this->m_data[i] = val; + } + Vector(std::array data) : m_data(data) { } + Vector(Vector &&other) noexcept : m_data(std::move(other.m_data)) { } + Vector(const Vector &other) : m_data(other.m_data) { } T &operator[](size_t index) { return this->m_data[index]; } const T &operator[](size_t index) const { return this->m_data[index]; } + std::array &asArray() { return this->m_data; } + T *data() { return this->m_data.data(); } const T *data() const { return this->m_data.data(); } [[nodiscard]] size_t size() const { return this->m_data.size(); } + auto operator=(const Vector& other) { + for (size_t i = 0; i < Size; i++) + this->m_data[i] = other[i]; + return *this; + } + + auto operator+=(const Vector& other) { + for (size_t i = 0; i < Size; i++) + this->m_data[i] += other.m_data[i]; + + return *this; + } + + auto operator+=(const T scalar) { + for (size_t i = 0; i < Size; i++) + this->m_data[i] += scalar; + + return *this; + } + + auto operator-=(Vector other) { + for (size_t i = 0; i < Size; i++) + this->m_data[i] -= other.m_data[i]; + + return *this; + } + + + auto operator-=(const T scalar) { + for (size_t i = 0; i < Size; i++) + this->m_data[i] -= scalar; + + return *this; + } + + Vector operator*=(const T scalar) { + for (size_t i = 0; i < Size; i++) + this->m_data[i] *= scalar; + + return *this; + } + + auto operator*(const T scalar) { + auto copy = *this; + for (size_t i = 0; i < Size; i++) + copy[i] *= scalar; + return copy; + } + auto operator+(const Vector& other) { auto copy = *this; for (size_t i = 0; i < Size; i++) @@ -67,12 +131,18 @@ namespace hex::gl { auto cross(const Vector& other) { static_assert(Size == 3, "Cross product is only defined for 3D vectors"); - return Vector({ this->m_data[1] * other[2] - this->m_data[2] * other[1], this->m_data[2] * other[0] - this->m_data[0] * other[2], this->m_data[0] * other[1] - this->m_data[1] * other[0] }); + return Vector({this->m_data[1] * other[2] - this->m_data[2] * other[1], + this->m_data[2] * other[0] - this->m_data[0] * other[2], + this->m_data[0] * other[1] - this->m_data[1] * other[0]}); + } + + auto magnitude() { + return std::sqrt(this->dot(*this)); } auto normalize() { auto copy = *this; - auto length = std::sqrt(copy.dot(copy)); + auto length = copy.magnitude(); for (size_t i = 0; i < Size; i++) copy[i] /= length; return copy; @@ -89,6 +159,638 @@ namespace hex::gl { std::array m_data; }; + template + class Matrix { + public: + Matrix(const T &init) { + for (size_t i = 0; i < Rows; i++) + for (size_t j = 0; j < Columns; j++) + mat[i * Columns + j] = init; + } + + Matrix(const Matrix &A) { + mat = A.mat; + } + + virtual ~Matrix() {} + + size_t getRows() const { + return Rows; + } + + size_t getColumns() const { + return Columns; + } + + T *data() { return this->mat.data(); } + + const T *data() const { return this->mat.data(); } + + + T &getElement(int row,int col) { + return this->mat[row*Columns+col]; + } + + Vector getColumn(int col) { + Vector result; + for (size_t i = 0; i < Rows; i++) + result[i] = this->mat[i*Columns+col]; + return result; + } + + Vector getRow(int row) { + Vector result; + for (size_t i = 0; i < Columns; i++) + result[i] = this->mat[row*Columns+i]; + return result; + } + + void updateRow(int row, Vector values) { + for (size_t i = 0; i < Columns; i++) + this->mat[row*Columns+i] = values[i]; + } + + void updateColumn(int col, Vector values) { + for (size_t i = 0; i < Rows; i++) + this->mat[i*Columns+col] = values[i]; + } + + void updateElement( int row,int col, T value) { + this->mat[row*Columns + col] = value; + } + + T &operator()( const int &row,const int &col) { + return this->mat[row*Columns + col]; + } + + const T &operator()(const unsigned& row,const unsigned& col ) const { + return this->mat[row*Columns + col]; + } + + Matrix& operator=(const Matrix& A) { + if (&A == this) + return *this; + + for (size_t i = 0; i < Rows; i++) + for (size_t j = 0; j < Columns; j++) + mat[i*Columns+j] = A(i, j); + return *this; + } + + Matrix operator+(const Matrix& A) { + Matrix result(0.0); + + for (size_t i = 0; i < Rows; i++) + for (size_t j = 0; j < Columns; j++) + result(i, j) = this->mat[i*Columns+j] + A(i, j); + return result; + } + + Matrix operator-(const Matrix& A) { + Matrix result(0.0); + + for (size_t i = 0; i < Rows; i++) + for (size_t j = 0; j < Columns; j++) + result(i, j) = this->mat[i*Columns+j] - A(i, j); + return result; + } + + static Matrix identity() { + Matrix I(0); + for (size_t i = 0; i < Rows; i++) + for (size_t j = 0; j < Columns; j++) + if(i == j) + I.updateElement(i, j, 1); + return I; + } + + Matrix transpose() { + Matrix t(0); + for (size_t i = 0; i < Columns; i++) + for (size_t j = 0; j < Rows; j++) + t.updateElement(i, j, this->mat[j*Rows+i]); + + return t; + } + + private: + std::array mat; + }; + + template + Matrix operator*(const Matrix &A, const Matrix &B) { + Matrix result(0.0); + + for (size_t i = 0; i < Rows; i++) + for (size_t j = 0; j < Columns; j++) + for (size_t k = 0; k < OtherDimension; k++) + result(i, j) += A(i,k) * B(k, j); + + return result; + } + + template + Matrix operator*(const Vector &a, const Vector &b) { + Matrix result(0); + + for (size_t i = 0; i < Rows; i++) + for (size_t j = 0; j < Columns; j++) + result.updateElement(i, j, a[i] * b[j]); + + return result; + } + + template + Vector operator*(const Matrix &A, const Vector &b) { + Vector result(0); + + for (size_t i = 0; i < Rows; i++) + for (size_t j = 0; j < Columns; j++) + result[i] += A(i, j) * b[j]; + + return result; + } + + template + Vector operator*(const Vector &b, const Matrix &A) { + Vector result(0); + + for (size_t i = 0; i < Rows; i++) + for (size_t j = 0; j < Columns; j++) + result[j] += b[i] * A(i, j); + + return result; + } + +// Convert horizontal (Xh) vertical (Yv) and spin (Zs) angles to a rotation matrix. +// Xh: Horizontal rotation, also known as heading or yaw. +// Yv: Vertical rotation, also known as pitch or elevation. +// Zs: Spin rotation, also known as intrinsic rotation, roll or bank. +// Each column of the rotation matrix represents left, up and forward axis. +// Angles of rotation are lowercase (x,y,z) in radians and the rotation matrix is uppercase (X,Y,Z). +// S = sin, C = cos +// The order of rotation is Yaw->Pitch->Roll (Zs*Yv*Xh) +// Zs Yv Xh +// | Cz -Sz 0 0| |Cy 0 Sy 0| |1 0 0 0| | Cz -Sz 0 0| | Cy Sy*Sx Sy*Cx 0| +// | Sz Cz 0 0|*| 0 1 0 0|*|0 Cx -Sx 0| = | Sz Cz 0 0|*| 0 Cx -Sx 0| +// | 0 0 1 0| |-Sy 0 Cy 0| |0 Sx Cx 0| | 0 0 1 0| |-Sy Sx*Cy Cx*Cy 0| +// | 0 0 0 1| | 0 0 0 1| |0 0 0 1| | 0 0 0 1| | 0 0 0 1| +// Left Up Forward +// | Cz*Cy Cz*Sy*Sx-Sz*Cx Sz*Sx+Cz*Sy*Cx 0| +// | Sz*Cy Sz*Sy*Sx+Cz*Cx Cz*Sy*Sx-Sz*Cx 0| +// |-Sy*Cx Cy*Cx Sy 0| +// | 0 0 0 1| + +// The order of rotation is Pitch->Yaw->Roll (Zs*Xh*Yv) +// Zs Xh Yv +// | Cz -Sz 0 0| |1 0 0 0| |Cy 0 Sy 0| | Cz -Sz 0 0| | Cy 0 Sy 0| +// | Sz Cz 0 0|*|0 Cx -Sx 0|*| 0 1 0 0| = | Sz Cz 0 0|*| Sx*Sy Cx -Sx*Cy 0|= +// | 0 0 1 0| |0 Sx Cx 0| |-Sy 0 Cy 0| | 0 0 1 0| |-Cx*Sy Sx Cx*Cy 0| +// | 0 0 0 1| |0 0 0 1| | 0 0 0 1| | 0 0 0 1| | 0 0 0 1| +// Left Up Forward +// | Cz*Cy-Sz*Sx*Sy -Sz*Cx Cz*Sy+Sz*Sx*Cy 0| +// | Sz*Cy+Cz*Sx*Sy Cz*Cx Sz*Sy-Cz*Sx*Cy 0| +// |-Cx*Sy Sx Cx*Cy 0| +// | 0 0 0 1| + + +// The order of rotation is Roll->Pitch->Yaw (Xh*Yv*Zs) +// Xh Yv Zs +// |1 0 0 0| | Cy 0 Sy 0| |Cz -Sz 0 0| |1 0 0 0| | Cy*Cz -Cy*Sz Sy 0| +// |0 Cx -Sx 0|*| 0 1 0 0|*|Sz Cz 0 0| = |0 Cx -Sx 0|*| Sz Cz 0 0| +// |0 Sx Cx 0| |-Sy 0 Cy 0| | 0 0 1 0| |0 Sx Cx 0| |-Sy*Cz Sy*Sz Cy 0| +// |0 0 0 1| | 0 0 0 1| | 0 0 0 1| |0 0 0 1| | 0 0 0 1| +// Left Up Forward +// | Cy*Cz -Cy*Sz Sy 0| +// =| Sx*Sy*Cz+Cx*Sz -Sx*Sy*Sz+Cx*Cz -Sx*Cy 0| +// |-Cx*Sy*Cz+Sx*Sz Cx*Sy*Sz+Sx*Cz Cx*Cy 0| +// | 0 0 0 1| +// just write final answer from here on +// The order of rotation is Pitch->Roll->Yaw (Xh*Zs*Yv) +// Left Up Forward +// |Cz*Cy -Sz Cz*Sy 0| +//Xh*Zs*Yv=|Cx*Cy*Sz+Sx*Sy Cx*Cz Cx*Sz*Sy-Cy*Sx 0| +// |Cy*Sx*Sz-Cx*Sy Cz*Sx Sx*Sz*Sy+Cx*Cy 0| +// |0 0 0 1| +// The order of rotation is Roll->Yaw->Pitch (Yv*Xh*Zs) +// Left Up Forward +// |Cy*Cz+Sy*Sx*Sz Cz*Sy*Sx-Cy*Sz Cx*Sy 0| +//Yv*Xh*Zs=|Cx*Sz Cx*Cz -Sx 0| +// |Cy*Sx*Sz-Cz*Sy Cy*Cz*Sx+Sy*Sz Cy*Cx 0| +// |0 0 0 1| +// The order of rotation is Yaw->Roll->Pitch (Yv*Zs*Xh) +// Left Up Forward +// |Cy*Cz Sy*Sx-Cy*Cx*Sz Cx*Sy+Cy*Sz*Sx 0| +//Yv*Zs*Xh= |Sz Cz*Cx -Cz*Sx 0| +// |-Cz*Sy Cy*Sx+Cx*Sy*Sz Cy*Cx-Sy*Sz*Sx 0| +// |0 0 0 1| + + enum RotationSequence { + XYZ, + XZY, + YXZ, + YZX, + ZXY, + ZYX + }; + + template + Matrix getRotationMatrix(Vector ypr, bool radians, RotationSequence rotationSequence) { + Matrix rotation(0); + + T Sx, Cx, Sy, Cy, Sz, Cz; + Vector angles = ypr; + if(!radians) + angles *= M_PI/180; + + Sx = -sin(angles[0]); Cx = cos(angles[0]); + Sy = -sin(angles[1]); Cy = cos(angles[1]); + Sz = -sin(angles[2]); Cz = cos(angles[2]); + + switch (rotationSequence) { + case ZXY: + // | Cz*Cy-Sz*Sx*Sy -Sz*Cx Cz*Sy+Sz*Sx*Cy 0| + // | Sz*Cy+Cz*Sx*Sy Cz*Cx Sz*Sy-Cz*Sx*Cy 0| + // |-Cx*Sy Sx Cx*Cy 0| + // | 0 0 0 1| + rotation.updateElement(0, 0, Cz * Cy - Sz * Sx * Sy); + rotation.updateElement(0, 1, -Sz * Cx); + rotation.updateElement(0, 2, Cz * Sy + Sz * Sx * Cy); + + rotation.updateElement(1, 0, Sz * Cy + Cz * Sx * Sy); + rotation.updateElement(1, 1, Cz * Cx); + rotation.updateElement(1, 2, Sz * Sy - Cz * Sx * Cy); + + rotation.updateElement(2, 0, -Cx * Sy); + rotation.updateElement(2, 1, Sx); + rotation.updateElement(2, 2, Cx * Cy); + break; + case ZYX: + // | Cz*Cy Cz*Sy*Sx-Sz*Cx Sz*Sx+Cz*Sy*Cx 0| + // | Sz*Cy Sz*Sy*Sx+Cz*Cx Sz*Sy*Cx-Cz*Sx 0| + // |-Sy Cy*Sx Cy*Cx 0| + // | 0 0 0 1| + rotation.updateElement(0, 0, Cz * Cy); + rotation.updateElement(0, 1, Sx * Sy * Cz - Sz * Cx); + rotation.updateElement(0, 2, Sz * Sx + Cz * Sy * Cx); + + rotation.updateElement(1, 0, Sz * Cy); + rotation.updateElement(1, 1, Sz * Sy * Sx + Cz * Cx); + rotation.updateElement(1, 2, Sz * Sy * Cx - Cz * Sx); + + rotation.updateElement(2, 0, -Sy); + rotation.updateElement(2, 1, Cy * Sx); + rotation.updateElement(2, 2, Cy*Cx); + break; + case XYZ: + // | Cy*Cz -Cy*Sz Sy 0| + // =| Sx*Sy*Cz+Cx*Sz -Sx*Sy*Sz+Cx*Cz -Sx*Cy 0| + // |-Cx*Sy*Cz+Sx*Sz Cx*Sy*Sz+Sx*Cz Cx*Cy 0| + // | 0 0 0 1| + rotation.updateElement(0, 0, Cy * Cz); + rotation.updateElement(0, 1, -Cy * Sz); + rotation.updateElement(0, 2, Sy); + + rotation.updateElement(1, 0, Sx * Sy * Cz + Cx * Sz); + rotation.updateElement(1, 1, -Sx * Sy * Sz + Cx * Cz); + rotation.updateElement(1, 2, -Sx * Cy); + + rotation.updateElement(2, 0, -Cx * Sy * Cz + Sx * Sz); + rotation.updateElement(2, 1, Cx * Sy * Sz + Sx * Cz); + rotation.updateElement(2, 2, Cx * Cy); + break; + case XZY: + // |Cz*Cy -Sz Cz*Sy 0| + //Xh*Zs*Yv=|Cx*Cy*Sz+Sx*Sy Cx*Cz Cx*Sz*Sy-Cy*Sx 0| + // |Cy*Sx*Sz-Cx*Sy Cz*Sx Sx*Sz*Sy+Cx*Cy 0| + // |0 0 0 1| + rotation.updateElement(0, 0, Cy * Cz); + rotation.updateElement(0, 1, -Sz); + rotation.updateElement(0, 2, Cz * Sy); + + rotation.updateElement(1, 0, Cx * Cy * Sz + Sx * Sy); + rotation.updateElement(1, 1, Cx * Cz); + rotation.updateElement(1, 2, Cx * Sy * Sz - Sx * Cy); + + rotation.updateElement(2, 0, Sx * Cy * Sz - Cx * Sy); + rotation.updateElement(2, 1, Sx * Cz); + rotation.updateElement(2, 2, Sx * Sy * Sz + Cx * Cy); + break; + case YXZ: + // |Cy*Cz+Sy*Sx*Sz Cz*Sy*Sx-Cy*Sz Cx*Sy 0| + //Yv*Xh*Zs=|Cx*Sz Cx*Cz -Sx 0| + // |Cy*Sx*Sz-Cz*Sy Cy*Cz*Sx+Sy*Sz Cy*Cx 0| + // |0 0 0 1| + rotation.updateElement(0, 0, Cy*Cz+Sy*Sx*Sz ); + rotation.updateElement(0, 1, Cz*Sy*Sx-Cy*Sz); + rotation.updateElement(0, 2, Sy*Cx); + + rotation.updateElement(1, 0, Cx*Sz); + rotation.updateElement(1, 1, Cx*Cz); + rotation.updateElement(1, 2, -Sx); + + rotation.updateElement(2, 0, Cy*Sx*Sz-Cz*Sy); + rotation.updateElement(2, 1, Cy*Cz*Sx+Sy*Sz); + rotation.updateElement(2, 2, Cy*Cx); + break; + case YZX: + // |Cy*Cz Sy*Sx-Cy*Cx*Sz Cx*Sy+Cy*Sz*Sx 0| + //Yv*Zs*Xh= |Sz Cz*Cx -Cz*Sx 0| + // |-Cz*Sy Cy*Sx+Cx*Sy*Sz Cy*Cx-Sy*Sz*Sx 0| + // |0 0 0 1| + rotation.updateElement(0, 0, Cy*Cz); + rotation.updateElement(0, 1, Sy*Sx-Cy*Cx*Sz); + rotation.updateElement(0, 2, Cx*Sy+Cy*Sz*Sx); + + rotation.updateElement(1, 0, Sz); + rotation.updateElement(1, 1, Cz*Cx); + rotation.updateElement(1, 2, -Cz*Sx); + + rotation.updateElement(2, 0, -Cz*Sy); + rotation.updateElement(2, 1, Cy*Sx+Cx*Sy*Sz); + rotation.updateElement(2, 2, Cy*Cx-Sy*Sz*Sx); + break; + + + } + + rotation.updateElement(3, 3, 1); + + return rotation; + } + + template + Matrix getRotationMatrixFromVectorAngle(Vector rotationVector, bool radians) { + Vector rotationVector3 = {{rotationVector[0], rotationVector[1], rotationVector[2]}}; + T theta = rotationVector3.magnitude(); + if (!radians) + theta *= M_PI / 180; + Vector axis = rotationVector3; + if (theta != 0) + axis = axis.normalize(); + Matrix rotation = Matrix::identity(); + T S = sin(theta); + T C = cos(theta); + T OMC = 1 - C; + T a00 = axis[0] * axis[0] * OMC; + T a01 = axis[0] * axis[1] * OMC; + T a02 = axis[0] * axis[2] * OMC; + T a10 = axis[1] * axis[0] * OMC; + T a11 = axis[1] * axis[1] * OMC; + T a12 = axis[1] * axis[2] * OMC; + T a20 = axis[2] * axis[0] * OMC; + T a21 = axis[2] * axis[1] * OMC; + T a22 = axis[2] * axis[2] * OMC; + T a0S = axis[0] * S; + T a1S = axis[1] * S; + T a2S = axis[2] * S; + + rotation.updateElement(0, 0, C + a00); + rotation.updateElement(0, 1, a01 - a2S); + rotation.updateElement(0, 2, a02 + a1S); + rotation.updateElement(1, 0, a10 + a2S); + rotation.updateElement(1, 1, C + a11); + rotation.updateElement(1, 2, a12 - a0S); + rotation.updateElement(2, 0, a20 - a1S); + rotation.updateElement(2, 1, a21 + a0S); + rotation.updateElement(2, 2, C + a22); + return rotation; + + } + + enum class MatrixElements { + r00, r01, r02, + r10, r11, r12, + r20, r21, r22, + }; + + template + T findValue(Vector ypr, MatrixElements matrixElement, RotationSequence rotationSequence) { + T Sx, Cx, Sy, Cy, Sz, Cz; + Vector angles = ypr; + + + Sx = sin(angles[0]); Cx = cos(angles[0]); + Sy = sin(angles[1]); Cy = cos(angles[1]); + Sz = sin(angles[2]); Cz = cos(angles[2]); + + switch (rotationSequence) { + case ZXY: + switch (matrixElement) { + case MatrixElements::r00: + return Cz * Cy - Sz * Sx * Sy; + case MatrixElements::r01: + return -Sz * Cx; + case MatrixElements::r02: + return Cz * Sy + Sz * Sx * Cy; + case MatrixElements::r10: + return Sz * Cy + Cz * Sx * Sy; + case MatrixElements::r11: + return Cz * Cx; + case MatrixElements::r12: + return Sz * Sy - Cz * Sx * Cy; + case MatrixElements::r20: + return -Cx * Sy; + case MatrixElements::r21: + return Sx; + case MatrixElements::r22: + return Cx * Cy; + } + break; + case ZYX: + switch (matrixElement) { + case MatrixElements::r00: + return Cz * Cy; + case MatrixElements::r01: + return Sx * Sy * Cz + Cx * Sz; + case MatrixElements::r02: + return -Cx * Sy * Cz + Sx * Sz; + case MatrixElements::r10: + return Cz * Sy; + case MatrixElements::r11: + return Sx * Sy * Sz - Cx * Cz; + case MatrixElements::r12: + return Cx * Sy * Sz + Sx * Cz; + case MatrixElements::r20: + return -Sy; + case MatrixElements::r21: + return Cy * Sx; + case MatrixElements::r22: + return Cy * Cx; + } + break; + case XYZ: + switch (matrixElement) { + case MatrixElements::r00: + return Cy * Cz; + case MatrixElements::r01: + return -Cy * Sz; + case MatrixElements::r02: + return Sy; + case MatrixElements::r10: + return Sx * Sy * Cz + Cx * Sz; + case MatrixElements::r11: + return -Sx * Sy * Sz + Cx * Cz; + case MatrixElements::r12: + return -Sx * Cy; + case MatrixElements::r20: + return -Cx * Sy * Cz + Sx * Sz; + case MatrixElements::r21: + return Cx * Sy * Sz + Sx * Cz; + case MatrixElements::r22: + return Cx * Cy; + } + break; + case XZY: + switch (matrixElement) { + case MatrixElements::r00: + return Cy * Cz; + case MatrixElements::r01: + return -Sz; + case MatrixElements::r02: + return Cz * Sy; + case MatrixElements::r10: + return Cx * Cy * Sz + Sx * Sy; + case MatrixElements::r11: + return Cx * Cz; + case MatrixElements::r12: + return Cx * Sy * Sz - Sx * Cy; + case MatrixElements::r20: + return Sx * Cy * Sz - Cx * Sy; + case MatrixElements::r21: + return Sx * Cz; + case MatrixElements::r22: + return Sx * Sy * Sz + Cx * Cy; + } + break; + case YXZ: + switch (matrixElement) { + case MatrixElements::r00: + return Cy * Cz + Sy * Sx * Sz; + case MatrixElements::r01: + return Cz * Sy * Sx - Cy * Sz; + case MatrixElements::r02: + return Cx * Sy; + case MatrixElements::r10: + return Cx * Sz; + case MatrixElements::r11: + return Cx * Cz; + case MatrixElements::r12: + return -Sx; + case MatrixElements::r20: + return -Cz * Sy + Cy * Sx * Sz; + case MatrixElements::r21: + return Cy * Cz * Sx + Sy * Sz; + case MatrixElements::r22: + return Cy * Cx; + } + break; + case YZX: + switch (matrixElement) { + case MatrixElements::r00: + return Cy * Cz; + case MatrixElements::r01: + return Sy * Sx - Cy * Cx * Sz; + case MatrixElements::r02: + return Cx * Sy + Cy * Sz * Sx; + case MatrixElements::r10: + return Sz; + case MatrixElements::r11: + return Cx * Cz; + case MatrixElements::r12: + return -Cz * Sx; + case MatrixElements::r20: + return -Cz * Sy; + case MatrixElements::r21: + return Cy * Sx + Cx * Sy * Sz; + case MatrixElements::r22: + return Cy * Cx - Sy * Sz * Sx; + } + break; + } + return 0; + } + + template + Matrix getTransformMatrix(Vector xyz, Vector ypr, bool radians) { + Matrix transform( 0); + + Matrix rotation = getRotationMatrix(ypr, radians); + + for(int i=0; i<3; i++) + for(int j=0; j<3; j++) + transform.updateElement(i, j, rotation.getElement(i, j)); + + transform.updateElement(0,3, xyz[0]); + transform.updateElement(1,3, xyz[1]); + transform.updateElement(2,3, xyz[2]); + transform.updateElement(3,3, 1); + + return transform; + } + + template + Vector getTranslationVector(Matrix transform_matrix) { + Vector xyz; + + xyz.push_back(transform_matrix.getElement(0,3)); + xyz.push_back(transform_matrix.getElement(1,3)); + xyz.push_back(transform_matrix.getElement(2,3)); + + return xyz; + } + + template + Vector getYprVector(Matrix transform_matrix) { + Vector result; + + Matrix rotation(0); + for(int i=0; i<3; i++) + for(int j=0; j<3; j++) + rotation.updateElement(i, j, transform_matrix.getElement(i, j)); + + T sy = sqrt(rotation.getElement(0,0) * rotation.getElement(0,0) + rotation.getElement(1,0) * rotation.getElement(1,0) ); + + bool singular = sy < 1e-6; + + T x, y, z; + if (!singular) { + x = atan2(rotation.getElement(1,0), rotation.getElement(0,0)); + y = atan2(-rotation.getElement(2,0), sy); + z = atan2(rotation.getElement(2,1), rotation.getElement(2,2)); + } + else { + x = 0; + y = atan2(-rotation.getElement(2,0), sy); + z = atan2(-rotation.getElement(1,2), rotation.getElement(1,1)); + } + + result.push_back(x); + result.push_back(y); + result.push_back(z); + + return result; + } + + Matrix GetPerspectiveMatrix( float viewWidth, float viewHeight, float nearVal, float farVal, bool actionType = false); + Matrix GetOrthographicMatrix( float viewWidth, float viewHeight, float nearVal, float farVal, bool actionType = false); + + + template + static Matrix GetObliqueMatrix( T width, T height,T nearVal,T farVal, bool actionType = false) { + int sign =1; + if (actionType) + sign=-1; + Matrix result(0); + result.updateElement(0,0,sign * nearVal/width); + result.updateElement(1,1, sign * nearVal/height); + 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; + } + class Shader { public: @@ -105,11 +807,26 @@ namespace hex::gl { void bind() const; void unbind() const; + void setUniform(std::string_view name, const int &value); void setUniform(std::string_view name, const float &value); - void setUniform(std::string_view name, const Vector &value); + + template + void setUniform(std::string_view name, const Vector &value) { + if (N == 2) + glUniform2f(getUniformLocation(name), value[0], value[1]); + else if (N == 3) + glUniform3f(getUniformLocation(name), value[0], value[1], value[2]); + else if (N == 4) + glUniform4f(getUniformLocation(name), value[0], value[1], value[2],value[3]); + } + + template + void setUniform(std::string_view name, Matrix &value){ + glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, value.data()); + } private: - void compile(GLuint shader, std::string_view source); + void compile(GLuint shader, std::string_view source) const; GLint getUniformLocation(std::string_view name); private: @@ -126,7 +843,7 @@ namespace hex::gl { class Buffer { public: Buffer() = default; - Buffer(BufferType type, std::span data); + Buffer(BufferType type, std::span data); ~Buffer(); Buffer(const Buffer&) = delete; Buffer(Buffer&& other) noexcept; @@ -137,9 +854,9 @@ namespace hex::gl { void bind() const; void unbind() const; - void draw() const; - + void draw(unsigned primitive) const; size_t getSize() const; + void update(std::span data); private: GLuint m_buffer = 0; size_t m_size = 0; @@ -148,6 +865,8 @@ namespace hex::gl { extern template class Buffer; extern template class Buffer; + extern template class Buffer; + extern template class Buffer; class VertexArray { public: @@ -160,10 +879,10 @@ namespace hex::gl { VertexArray& operator=(VertexArray&& other) noexcept; template - void addBuffer(u32 index, const Buffer &buffer) const { + void addBuffer(u32 index, const Buffer &buffer, u32 size = 3) const { glEnableVertexAttribArray(index); buffer.bind(); - glVertexAttribPointer(index, 3, impl::getType(), GL_FALSE, 3 * sizeof(T), nullptr); + glVertexAttribPointer(index, size, gl::impl::getType(), GL_FALSE, size * sizeof(T), nullptr); buffer.unbind(); } @@ -190,8 +909,8 @@ namespace hex::gl { GLuint getTexture() const; u32 getWidth() const; u32 getHeight() const; - GLuint release(); + private: GLuint m_texture; u32 m_width, m_height; @@ -199,7 +918,7 @@ namespace hex::gl { class FrameBuffer { public: - FrameBuffer(); + FrameBuffer(u32 width, u32 height); ~FrameBuffer(); FrameBuffer(const FrameBuffer&) = delete; FrameBuffer(FrameBuffer&& other) noexcept; @@ -216,4 +935,169 @@ namespace hex::gl { GLuint m_frameBuffer, m_renderBuffer; }; + class AxesVectors { + public: + AxesVectors(); + + const std::vector& getVertices() const { + return m_vertices; + } + + const std::vector& getColors() const { + return m_colors; + } + + const std::vector& getIndices() const { + return m_indices; + } + + private: + std::vector m_vertices; + std::vector m_colors; + std::vector m_indices; + + }; + + class AxesBuffers { + public: + AxesBuffers(const VertexArray& axesVertexArray, const AxesVectors &axesVectors); + + const gl::Buffer& getVertices() const { + return m_vertices; + } + + const gl::Buffer& getColors() const { + return m_colors; + } + + const gl::Buffer& getIndices() const { + return m_indices; + } + + private: + gl::Buffer m_vertices; + gl::Buffer m_colors; + gl::Buffer m_indices; + }; + + class GridVectors { + public: + GridVectors(int sliceCount); + + u32 getSlices() const { + return m_slices; + } + + const std::vector& getVertices() const { + return m_vertices; + } + + const std::vector& getColors() const { + return m_colors; + } + + const std::vector& getIndices() const { + return m_indices; + } + + private: + u32 m_slices; + std::vector m_vertices; + std::vector m_colors; + std::vector m_indices; + }; + + class GridBuffers { + public: + GridBuffers(const VertexArray &gridVertexArray, const GridVectors &gridVectors); + + const gl::Buffer& getVertices() const { + return m_vertices; + } + + const gl::Buffer& getColors() const { + return m_colors; + } + + const gl::Buffer& getIndices() const { + return m_indices; + } + + private: + gl::Buffer m_vertices; + gl::Buffer m_colors; + gl::Buffer m_indices; + }; + + class LightSourceVectors { + public: + LightSourceVectors(int res); + + void moveTo(const Vector &position); + + const std::vector& getVertices() const { + return m_vertices; + } + + const std::vector& getNormals() const { + return m_normals; + } + + const std::vector& getColors() const { + return m_colors; + } + + const std::vector& getIndices() const { + return m_indices; + } + + void setColor(float r, float g, float b) { + for (u32 i = 4; i < m_colors.size(); i += 4) { + m_colors[i - 4] = r; + m_colors[i - 3] = g; + m_colors[i - 2] = b; + m_colors[i - 1] = 1.0F; + } + } + + private: + int m_resolution; + float m_radius; + + std::vector m_vertices; + std::vector m_normals; + std::vector m_colors; + std::vector m_indices; + }; + + class LightSourceBuffers { + public: + LightSourceBuffers(const VertexArray &sourceVertexArray, const LightSourceVectors &sourceVectors); + + void moveVertices(const VertexArray &sourceVertexArray, const LightSourceVectors& sourceVectors); + void updateColors(const VertexArray& sourceVertexArray, const LightSourceVectors& sourceVectors); + + const gl::Buffer& getVertices() const { + return m_vertices; + } + + const gl::Buffer& getNormals() const { + return m_normals; + } + + const gl::Buffer& getColors() const { + return m_colors; + } + + const gl::Buffer& getIndices() const { + return m_indices; + } + + private: + gl::Buffer m_vertices; + gl::Buffer m_normals; + gl::Buffer m_colors; + gl::Buffer m_indices; + }; + } \ No newline at end of file diff --git a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h index d0d80916a..e78a0fb13 100644 --- a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h +++ b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h @@ -10,6 +10,7 @@ #include #include +#include #include @@ -73,6 +74,7 @@ namespace ImGuiExt { Texture(const ImU8 *buffer, int size, int width = 0, int height = 0); Texture(std::span bytes, int width = 0, int height = 0); explicit Texture(const char *path); + explicit Texture(const std::fs::path &path); Texture(unsigned int texture, int width, int height); Texture(const Texture&) = delete; Texture(Texture&& other) noexcept; @@ -86,10 +88,14 @@ namespace ImGuiExt { return this->m_textureId != nullptr; } - [[nodiscard]] constexpr operator ImTextureID() const noexcept { + [[nodiscard]] operator ImTextureID() const noexcept { return this->m_textureId; } + [[nodiscard]] operator intptr_t() const noexcept { + return reinterpret_cast(this->m_textureId); + } + [[nodiscard]] auto getSize() const noexcept { return ImVec2(this->m_width, this->m_height); } @@ -122,7 +128,7 @@ namespace ImGuiExt { void Header(const char *label, bool firstEntry = false); 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 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 DimmedButtonToggle(const char *icon, bool *v, ImVec2 size); bool DimmedIconToggle(const char *icon, bool *v); + bool DimmedIconToggle(const char *iconOn, const char *iconOff, bool *v); void TextOverlay(const char *text, ImVec2 pos); @@ -277,6 +284,11 @@ namespace ImGuiExt { if (ImGui::Button(textRight, ImVec2(width / 3, 0))) 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 &validExtensions); + template constexpr ImGuiDataType getImGuiDataType() { if constexpr (std::same_as) return ImGuiDataType_U8; diff --git a/lib/libimhex/source/helpers/opengl.cpp b/lib/libimhex/source/helpers/opengl.cpp index 5e5cab2d3..c64c6685b 100644 --- a/lib/libimhex/source/helpers/opengl.cpp +++ b/lib/libimhex/source/helpers/opengl.cpp @@ -6,9 +6,60 @@ #include +#include namespace hex::gl { + Matrix 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 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 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 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) { auto vertexShader = glCreateShader(GL_VERTEX_SHADER); this->compile(vertexShader, vertexSource); @@ -57,13 +108,14 @@ namespace hex::gl { 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) { glUniform1f(getUniformLocation(name), value); } - void Shader::setUniform(std::string_view name, const Vector &value) { - glUniform3f(getUniformLocation(name), value[0], value[1], value[2]); - } GLint Shader::getUniformLocation(std::string_view name) { auto uniform = this->m_uniforms.find(name.data()); @@ -81,7 +133,7 @@ namespace hex::gl { 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(); glShaderSource(shader, 1, &sourcePtr, nullptr); @@ -98,7 +150,7 @@ namespace hex::gl { template - Buffer::Buffer(BufferType type, std::span data) : m_size(data.size()), m_type(GLuint(type)) { + Buffer::Buffer(BufferType type, std::span data) : m_size(data.size()), m_type(GLuint(type)) { glGenBuffers(1, &this->m_buffer); glBindBuffer(this->m_type, this->m_buffer); glBufferData(this->m_type, data.size_bytes(), data.data(), GL_STATIC_DRAW); @@ -143,20 +195,28 @@ namespace hex::gl { } template - void Buffer::draw() const { + void Buffer::draw(unsigned primitive) const { switch (this->m_type) { case GL_ARRAY_BUFFER: - glDrawArrays(GL_TRIANGLES, 0, this->m_size); + glDrawArrays(primitive, 0, this->m_size); break; case GL_ELEMENT_ARRAY_BUFFER: - glDrawElements(GL_TRIANGLES, this->m_size, impl::getType(), nullptr); - break; + glDrawElements(primitive, this->m_size, impl::getType(), nullptr); + break; } } + template + void Buffer::update(std::span 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; template class Buffer; - + template class Buffer; + template class Buffer; VertexArray::VertexArray() { glGenVertexArrays(1, &this->m_array); @@ -244,14 +304,13 @@ namespace hex::gl { return copy; } - - FrameBuffer::FrameBuffer() { + FrameBuffer::FrameBuffer(u32 width, u32 height) { glGenFramebuffers(1, &this->m_frameBuffer); glBindFramebuffer(GL_FRAMEBUFFER, this->m_frameBuffer); glGenRenderbuffers(1, &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); glBindRenderbuffer(GL_RENDERBUFFER, 0); @@ -295,4 +354,335 @@ namespace hex::gl { glBindFramebuffer(GL_FRAMEBUFFER, 0); } -} \ No newline at end of file + 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(gl::BufferType::Vertex, axesVectors.getVertices()); + m_colors = gl::Buffer(gl::BufferType::Vertex, axesVectors.getColors()); + m_indices = gl::Buffer(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(gl::BufferType::Vertex, gridVectors.getVertices()); + m_indices = gl::Buffer(gl::BufferType::Index, gridVectors.getIndices()); + m_colors = gl::Buffer(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 * 2.0F; + constexpr auto HalfPi = std::numbers::pi_v / 2.0F; + const auto dv = TwoPi / m_resolution; + const auto du = std::numbers::pi_v / (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 &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(gl::BufferType::Vertex, sourceVectors.getVertices()); + m_indices = gl::Buffer(gl::BufferType::Index, sourceVectors.getIndices()); + m_normals = gl::Buffer(gl::BufferType::Vertex, sourceVectors.getNormals()); + m_colors = gl::Buffer(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(); + } + +} + + + diff --git a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp index 80c6a182c..a3475f5fd 100644 --- a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp +++ b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp @@ -56,6 +56,8 @@ namespace ImGuiExt { Texture::Texture(std::span bytes, int width, int height) : Texture(reinterpret_cast(bytes.data()), bytes.size(), width, height) { } + Texture::Texture(const std::fs::path &path) : Texture(reinterpret_cast(path.u8string().c_str())) { } + Texture::Texture(const char *path) { unsigned char *imageData = stbi_load(path, &this->m_width, &this->m_height, nullptr, 4); if (imageData == nullptr) @@ -382,7 +384,7 @@ namespace ImGuiExt { Separator(); } - bool InfoTooltip(const char *text) { + bool InfoTooltip(const char *text, bool isSeparator) { static double lastMoveTime; static ImGuiID lastHoveredID; @@ -393,7 +395,10 @@ namespace ImGuiExt { if (IsItemHovered() && (currTime - lastMoveTime) >= 0.5 && hoveredID == lastHoveredID) { if (!std::string_view(text).empty()) { BeginTooltip(); - TextUnformatted(text); + if (isSeparator) + SeparatorText(text); + else + TextUnformatted(text); EndTooltip(); } @@ -893,6 +898,26 @@ namespace ImGuiExt { 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) { const auto textSize = CalcTextSize(text); const auto textPos = pos - textSize / 2; @@ -937,6 +962,47 @@ namespace ImGuiExt { 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 &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 { diff --git a/lib/third_party/imgui/fonts/include/fonts/blendericons_font.h b/lib/third_party/imgui/fonts/include/fonts/blendericons_font.h new file mode 100644 index 000000000..13f8b0353 --- /dev/null +++ b/lib/third_party/imgui/fonts/include/fonts/blendericons_font.h @@ -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 diff --git a/main/gui/source/init/tasks.cpp b/main/gui/source/init/tasks.cpp index 64e20291e..14cf23393 100644 --- a/main/gui/source/init/tasks.cpp +++ b/main/gui/source/init/tasks.cpp @@ -1,4 +1,5 @@ #include "init/tasks.hpp" +#include "misc/freetype/imgui_freetype.h" #include diff --git a/plugins/builtin/CMakeLists.txt b/plugins/builtin/CMakeLists.txt index 2a728b1f1..b9a7aced9 100644 --- a/plugins/builtin/CMakeLists.txt +++ b/plugins/builtin/CMakeLists.txt @@ -80,6 +80,17 @@ add_imhex_plugin( source/content/tools/tcp_client_server.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_pattern_editor.cpp source/content/views/view_pattern_data.cpp diff --git a/plugins/builtin/include/content/pl_visualizers/visualizer_helpers.hpp b/plugins/builtin/include/content/pl_visualizers/visualizer_helpers.hpp new file mode 100644 index 000000000..f574c246f --- /dev/null +++ b/plugins/builtin/include/content/pl_visualizers/visualizer_helpers.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace hex::plugin::builtin { + + template + std::vector patternToArray(pl::ptrn::Pattern *pattern){ + const auto bytes = pattern->getBytes(); + + std::vector 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; + } + +} \ No newline at end of file diff --git a/plugins/builtin/romfs/fonts/blendericons.ttf b/plugins/builtin/romfs/fonts/blendericons.ttf new file mode 100644 index 000000000..45e095c53 Binary files /dev/null and b/plugins/builtin/romfs/fonts/blendericons.ttf differ diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index fbd3a6914..e9ac90cd8 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -455,8 +455,14 @@ "hex.builtin.pattern_drawer.var_name": "Name", "hex.builtin.pattern_drawer.visualizer.unknown": "Unknown visualizer", "hex.builtin.pattern_drawer.visualizer.invalid_parameter_count": "Invalid parameter count", - "hex.builtin.pl_visualizer.3d.rotation": "Rotation", - "hex.builtin.pl_visualizer.3d.scale": "Scale", + "hex.builtin.pl_visualizer.3d.light_position": "Light Position", + "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.longitude": "Longitude", "hex.builtin.pl_visualizer.coordinates.query": "Find address", diff --git a/plugins/builtin/romfs/shaders/default/fragment.glsl b/plugins/builtin/romfs/shaders/default/fragment.glsl index adeea15e3..02efa4045 100644 --- a/plugins/builtin/romfs/shaders/default/fragment.glsl +++ b/plugins/builtin/romfs/shaders/default/fragment.glsl @@ -1,12 +1,40 @@ #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() { - vec3 norm = normalize(normal); - vec3 lightDir = normalize(vec3(0, 0, -1)); - float diff = max(dot(norm, lightDir), 0.0); - vec3 diffuse = diff * vec3(1.0, 1.0, 1.0); + vec3 ambientLightColor = vec3(1.0, 1.0, 1.0); + + // Ambient lighting + 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; -} \ No newline at end of file diff --git a/plugins/builtin/romfs/shaders/default/lightFragment.glsl b/plugins/builtin/romfs/shaders/default/lightFragment.glsl new file mode 100644 index 000000000..6134a0b00 --- /dev/null +++ b/plugins/builtin/romfs/shaders/default/lightFragment.glsl @@ -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); +} diff --git a/plugins/builtin/romfs/shaders/default/lightVertex.glsl b/plugins/builtin/romfs/shaders/default/lightVertex.glsl new file mode 100644 index 000000000..b0e60e824 --- /dev/null +++ b/plugins/builtin/romfs/shaders/default/lightVertex.glsl @@ -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; +} diff --git a/plugins/builtin/romfs/shaders/default/lineFragment.glsl b/plugins/builtin/romfs/shaders/default/lineFragment.glsl new file mode 100644 index 000000000..cfaeca3b6 --- /dev/null +++ b/plugins/builtin/romfs/shaders/default/lineFragment.glsl @@ -0,0 +1,8 @@ +#version 330 core + +in vec4 fragColor; +out vec4 outColor; + +void main() { + outColor = fragColor; +} diff --git a/plugins/builtin/romfs/shaders/default/lineVertex.glsl b/plugins/builtin/romfs/shaders/default/lineVertex.glsl new file mode 100644 index 000000000..c960d9a2a --- /dev/null +++ b/plugins/builtin/romfs/shaders/default/lineVertex.glsl @@ -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; +} diff --git a/plugins/builtin/romfs/shaders/default/vertex.glsl b/plugins/builtin/romfs/shaders/default/vertex.glsl index 78b39ed5c..c0e4ebb3a 100644 --- a/plugins/builtin/romfs/shaders/default/vertex.glsl +++ b/plugins/builtin/romfs/shaders/default/vertex.glsl @@ -1,36 +1,39 @@ #version 330 core 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 float scale; -uniform vec3 rotation; -uniform vec3 translation; +uniform mat4 modelScale; -out vec3 normal; +uniform mat4 modelMatrix; +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; -mat4 rotationMatrix(vec3 axis, float angle) { - axis = normalize(axis); - float s = sin(angle); - float c = cos(angle); - float oc = 1.0 - c; +uniform vec3 lightPosition; +uniform vec4 lightBrightness; +uniform vec3 lightColor; - 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, - oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, - oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, - 0.0, 0.0, 0.0, 1.0); -} - -mat4 viewMatrix(vec3 rotation) { - mat4 rotationX = rotationMatrix(vec3(1, 0, 0), rotation.x); - mat4 rotationY = rotationMatrix(vec3(0, 1, 0), rotation.y); - mat4 rotationZ = rotationMatrix(vec3(0, 0, 1), rotation.z); - - return rotationX * rotationY * rotationZ; -} +out VertexData { + vec3 normal; + vec4 fragColor; + vec2 texCoord; + vec3 lightPosition; + vec3 fragPosition; + vec4 lightBrightness; + vec3 lightColor; +} vertexData; void main() { - mat4 view = viewMatrix(rotation); - normal = (vec4(in_Normal, 1.0) * view).xyz * -1; - gl_Position = vec4((in_Position + translation) * -scale, 1.0) * view; + gl_Position = projectionMatrix * viewMatrix * modelScale * vec4(in_Position, 1.0); + + 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)); } \ No newline at end of file diff --git a/plugins/builtin/source/content/fonts.cpp b/plugins/builtin/source/content/fonts.cpp index 79a68bd28..238eea819 100644 --- a/plugins/builtin/source/content/fonts.cpp +++ b/plugins/builtin/source/content/fonts.cpp @@ -7,6 +7,7 @@ #include #include +#include #include @@ -21,6 +22,7 @@ namespace hex::plugin::builtin { * 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(),{ { ICON_MIN_BI, ICON_MAX_BI } }, { 0, -3_scaled }); ImHexApi::Fonts::loadFont("Font Awesome 5", romfs::get("fonts/fontawesome.otf").span(), { { glyph(ICON_FA_BACKSPACE), glyph(ICON_FA_INFINITY), glyph(ICON_FA_TACHOMETER_ALT), glyph(ICON_FA_MICROCHIP), glyph(ICON_FA_CODE_BRANCH) } diff --git a/plugins/builtin/source/content/init_tasks.cpp b/plugins/builtin/source/content/init_tasks.cpp index 6dd00a286..c826af68d 100644 --- a/plugins/builtin/source/content/init_tasks.cpp +++ b/plugins/builtin/source/content/init_tasks.cpp @@ -209,12 +209,14 @@ namespace hex::plugin::builtin { if (fontFile.empty()) fonts->Clear(); - if (ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_bold", false)) - cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bold; - if (ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_italic", false)) - cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Oblique; - if (!ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_antialias", false)) - cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting; + if (ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font_enable", false).get()) { + if (ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_bold", false)) + cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bold; + if (ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_italic", false)) + cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Oblique; + 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) { ImFontConfig defaultConfig = cfg; @@ -266,7 +268,7 @@ namespace hex::plugin::builtin { ranges.push_back(fontRange); - cfg.FontBuilderFlags = startFlags | font.flags; + cfg.FontBuilderFlags = font.flags; float descent = [&] { ImFontAtlas atlas; @@ -297,6 +299,7 @@ namespace hex::plugin::builtin { 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); } + cfg.FontBuilderFlags = startFlags; // Create bold and italic font cfg.MergeMode = false; diff --git a/plugins/builtin/source/content/pl_visualizers.cpp b/plugins/builtin/source/content/pl_visualizers.cpp index a06f3eeb1..cbac31a53 100644 --- a/plugins/builtin/source/content/pl_visualizers.cpp +++ b/plugins/builtin/source/content/pl_visualizers.cpp @@ -1,717 +1,21 @@ #include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include - -#include -#include #include -#include - -#include - -#include -#include - -#include -#include - -#include namespace hex::plugin::builtin { - namespace { - - template - std::vector patternToArray(pl::ptrn::Pattern *pattern){ - const auto bytes = pattern->getBytes(); - - std::vector 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; - } - - } - - namespace { - - void drawLinePlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span arguments) { - static std::vector 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(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 arguments) { - static std::vector 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(xPattern.get()), ImPlot::GetPlotSize().x * 4); - yValues = sampleData(patternToArray(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 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 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 arguments) { - struct Disassembly { - u64 address; - std::vector bytes; - std::string instruction; - }; - - static std::vector 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(architecture), static_cast(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 arguments) { - auto verticesPattern = arguments[0].toPattern(); - auto indicesPattern = arguments[1].toPattern(); - - static ImGuiExt::Texture texture; - - static gl::Vector translation; - static gl::Vector rotation = { { 1.0F, -1.0F, 0.0F } }; - static float scaling = 0.1F; - - static std::vector vertices, normals; - static std::vector indices; - - static gl::Shader shader; - static gl::VertexArray vertexArray; - static gl::Buffer vertexBuffer, normalBuffer; - static gl::Buffer 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(verticesPattern.get()); - indices = patternToArray(indicesPattern.get()); - - normals.clear(); - normals.resize(vertices.size()); - - for (u32 i = 0; i < normals.size(); i += 9) { - - auto v1 = gl::Vector({ vertices[i] , vertices[i + 1], vertices[i + 2] }); - auto v2 = gl::Vector({ vertices[i + 3], vertices[i + 4], vertices[i + 5] }); - auto v3 = gl::Vector({ 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(gl::BufferType::Vertex, vertices); - normalBuffer = gl::Buffer(gl::BufferType::Vertex, normals); - indexBuffer = gl::Buffer(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({ 0.0F, 0.0F, 0.0F }); - rotation = gl::Vector({ 0.0F, 0.0F, 0.0F }); - scaling = 0.1F; - } - - ImGui::EndTable(); - } - - - } - - void drawSoundVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span arguments) { - auto wavePattern = arguments[0].toPattern(); - auto channels = arguments[1].toUnsigned(); - auto sampleRate = arguments[2].toUnsigned(); - - static std::vector 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(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::min(), std::numeric_limits::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 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 arguments) { - static ui::HexEditor editor; - static std::unique_ptr dataProvider; - - if (shouldReset) { - auto pattern = arguments[0].toPattern(); - std::vector data; - - dataProvider = std::make_unique(); - 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 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(), - jsonAddr["county"].get(), - jsonAddr["state"].get(), - jsonAddr["country"].get()); - } else if (jsonAddr.contains("city")) { - address = hex::format("{}, {} {}, {} {}", - jsonAddr["road"].get(), - jsonAddr["quarter"].get(), - jsonAddr["city"].get(), - jsonAddr["state"].get(), - jsonAddr["country"].get()); - } - } 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 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 drawLinePlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); + void drawScatterPlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); + void drawImageVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); + void drawBitmapVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); + void drawDisassemblyVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); + void draw3DVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); + void drawSoundVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); + void drawChunkBasedEntropyVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); + void drawHexVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); + void drawCoordinateVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); + void drawTimestampVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span arguments); void registerPatternLanguageVisualizers() { using ParamCount = pl::api::FunctionParameterCount; @@ -721,7 +25,7 @@ namespace hex::plugin::builtin { ContentRegistry::PatternLanguage::addVisualizer("image", drawImageVisualizer, ParamCount::exactly(1)); ContentRegistry::PatternLanguage::addVisualizer("bitmap", drawBitmapVisualizer, ParamCount::exactly(3)); 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("chunk_entropy", drawChunkBasedEntropyVisualizer, ParamCount::exactly(2)); ContentRegistry::PatternLanguage::addVisualizer("hex_viewer", drawHexVisualizer, ParamCount::exactly(1)); diff --git a/plugins/builtin/source/content/pl_visualizers/3d_model.cpp b/plugins/builtin/source/content/pl_visualizers/3d_model.cpp new file mode 100644 index 000000000..e1aa31ea9 --- /dev/null +++ b/plugins/builtin/source/content/pl_visualizers/3d_model.cpp @@ -0,0 +1,908 @@ +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +namespace hex::plugin::builtin { + + namespace { + + enum class IndexType { + U8, + U16, + U32, + Invalid, + }; + + struct Vectors { + std::vector vertices; + std::vector normals; + std::vector colors; + std::vector uv1; + std::vector indices8; + std::vector indices16; + std::vector indices32; + }; + + struct LineVectors { + std::vector vertices; + std::vector colors; + std::vector indices8; + std::vector indices16; + std::vector indices32; + }; + + struct Buffers { + gl::Buffer vertices; + gl::Buffer normals; + gl::Buffer colors; + gl::Buffer uv1; + gl::Buffer indices8; + gl::Buffer indices16; + gl::Buffer indices32; + }; + + struct LineBuffers { + gl::Buffer vertices; + gl::Buffer colors; + gl::Buffer indices8; + gl::Buffer indices16; + gl::Buffer 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 s_translation = { { 0.0F, 0.0F, -3.0F } }; + gl::Vector s_rotation = { { 0.0F, 0.0F, 0.0F } }; + gl::Vector s_lightPosition = { { -0.7F, 0.0F, 0.0F } }; + gl::Vector s_lightBrightness = { { 0.5F, 0.5F, 0.5F, 32.0F } }; + gl::Vector s_lightColor = { { 1.0F, 1.0F, 1.0f } }; + gl::Matrix s_rotate = gl::Matrix::identity(); + + ImGuiExt::Texture s_texture; + std::fs::path s_texturePath; + + template + void indicesForLines(std::vector &vertexIndices) { + std::vector 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 &vertices) { + gl::Vector minWorld(std::numeric_limits::infinity()), maxWorld(-std::numeric_limits::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 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 &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 &vertices, std::vector &normals) { + for (u32 i = 0; i < normals.size(); i += 9) { + + auto v1 = gl::Vector({vertices[i], vertices[i + 1], vertices[i + 2]}); + auto v2 = gl::Vector({vertices[i + 3], vertices[i + 4], vertices[i + 5]}); + auto v3 = gl::Vector({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({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 &vertices, std::vector &normals, const std::vector &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({vertices[3 * idx], vertices[3 * idx + 1], vertices[3 * idx + 2]}); + auto v2 = gl::Vector( + {vertices[3 * idx1], vertices[3 * idx1 + 1], vertices[3 * idx1 + 2]}); + auto v3 = gl::Vector( + {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({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 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 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 &rotation, gl::Vector &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(gl::BufferType::Vertex, vectors.vertices); + buffers.colors = gl::Buffer(gl::BufferType::Vertex, vectors.colors); + buffers.normals = gl::Buffer(gl::BufferType::Vertex, vectors.normals); + + if (indexType == IndexType::U8) + buffers.indices8 = gl::Buffer(gl::BufferType::Index, vectors.indices8); + else if (indexType == IndexType::U16) + buffers.indices16 = gl::Buffer(gl::BufferType::Index, vectors.indices16); + else + buffers.indices32 = gl::Buffer(gl::BufferType::Index, vectors.indices32); + + if (!vectors.uv1.empty()) + buffers.uv1 = gl::Buffer(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(gl::BufferType::Vertex, lineVectors.vertices); + lineBuffers.colors = gl::Buffer(gl::BufferType::Vertex, lineVectors.colors); + + if (indexType == IndexType::U8) + lineBuffers.indices8 = gl::Buffer(gl::BufferType::Index, lineVectors.indices8); + else if (indexType == IndexType::U16) + lineBuffers.indices16 = gl::Buffer(gl::BufferType::Index, lineVectors.indices16); + else + lineBuffers.indices32 = gl::Buffer(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 &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 axes = gl::Matrix::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 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 verticesPattern = arguments[0].toPattern(); + std::shared_ptr indicesPattern = arguments[1].toPattern(); + std::shared_ptr normalsPattern = nullptr; + std::shared_ptr colorsPattern = nullptr; + std::shared_ptr 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 mvp(0); + + processInputEvents(s_rotation, s_translation, s_scaling, s_nearLimit, s_farLimit); + + if (s_shouldReset) { + s_shouldReset = false; + + auto *iterable = dynamic_cast(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(verticesPattern.get()); + if (s_indexType == IndexType::U16) + vectors.indices16 = patternToArray(indicesPattern.get()); + else if (s_indexType == IndexType::U32) + vectors.indices32 = patternToArray(indicesPattern.get()); + else if (s_indexType == IndexType::U8) + vectors.indices8 = patternToArray(indicesPattern.get()); + + if (colorsPattern != nullptr) + vectors.colors = patternToArray(colorsPattern.get()); + if (normalsPattern != nullptr) + vectors.normals = patternToArray(normalsPattern.get()); + if (uvPattern1 != nullptr) + vectors.uv1 = patternToArray(uvPattern1.get()); + + loadVectors(vectors, s_indexType); + + bindBuffers(buffers, vertexArray, vectors, s_indexType); + } else { + LineVectors lineVectors; + + lineVectors.vertices = patternToArray(verticesPattern.get()); + if (s_indexType == IndexType::U16) + lineVectors.indices16 = patternToArray(indicesPattern.get()); + + else if (s_indexType == IndexType::U32) + lineVectors.indices32 = patternToArray(indicesPattern.get()); + + else if (s_indexType == IndexType::U8) + lineVectors.indices8 = patternToArray(indicesPattern.get()); + + if (colorsPattern != nullptr) + lineVectors.colors = patternToArray(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 model(0); + gl::Matrix scaledModel(0); + gl::Matrix view(0); + gl::Matrix 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(s_rotation, true, gl::RotationSequence::ZYX); + + gl::Matrix scale = gl::Matrix::identity(); + gl::Matrix scaleForVertices = gl::Matrix::identity(); + gl::Matrix translate = gl::Matrix::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); + } + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/pl_visualizers/chunk_entropy.cpp b/plugins/builtin/source/content/pl_visualizers/chunk_entropy.cpp new file mode 100644 index 000000000..e3bf3e1bf --- /dev/null +++ b/plugins/builtin/source/content/pl_visualizers/chunk_entropy.cpp @@ -0,0 +1,28 @@ +#include + +#include + +#include +#include +#include + +#include + +namespace hex::plugin::builtin { + + void drawChunkBasedEntropyVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span 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); + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/pl_visualizers/coordinates.cpp b/plugins/builtin/source/content/pl_visualizers/coordinates.cpp new file mode 100644 index 000000000..3d8e9ca03 --- /dev/null +++ b/plugins/builtin/source/content/pl_visualizers/coordinates.cpp @@ -0,0 +1,116 @@ +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace hex::plugin::builtin { + + void drawCoordinateVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span 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(), + jsonAddr["county"].get(), + jsonAddr["state"].get(), + jsonAddr["country"].get()); + } else if (jsonAddr.contains("city")) { + address = hex::format("{}, {} {}, {} {}", + jsonAddr["road"].get(), + jsonAddr["quarter"].get(), + jsonAddr["city"].get(), + jsonAddr["state"].get(), + jsonAddr["country"].get()); + } + } 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); + } + } + +} diff --git a/plugins/builtin/source/content/pl_visualizers/disassembler.cpp b/plugins/builtin/source/content/pl_visualizers/disassembler.cpp new file mode 100644 index 000000000..3e977de3d --- /dev/null +++ b/plugins/builtin/source/content/pl_visualizers/disassembler.cpp @@ -0,0 +1,70 @@ +#include + +#include + +#include + +#include + +#include +#include + +namespace hex::plugin::builtin { + + void drawDisassemblyVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span arguments) { + struct Disassembly { + u64 address; + std::vector bytes; + std::string instruction; + }; + + static std::vector 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(architecture), static_cast(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(); + } + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/pl_visualizers/hex_viewer.cpp b/plugins/builtin/source/content/pl_visualizers/hex_viewer.cpp new file mode 100644 index 000000000..9214f8ce1 --- /dev/null +++ b/plugins/builtin/source/content/pl_visualizers/hex_viewer.cpp @@ -0,0 +1,43 @@ +#include + +#include + +#include + +#include +#include +#include + +namespace hex::plugin::builtin { + + void drawHexVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span arguments) { + static ui::HexEditor editor; + static std::unique_ptr dataProvider; + + if (shouldReset) { + auto pattern = arguments[0].toPattern(); + std::vector data; + + dataProvider = std::make_unique(); + 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(); + } + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/pl_visualizers/image.cpp b/plugins/builtin/source/content/pl_visualizers/image.cpp new file mode 100644 index 000000000..8396af7fc --- /dev/null +++ b/plugins/builtin/source/content/pl_visualizers/image.cpp @@ -0,0 +1,61 @@ +#include + +#include + +#include +#include + +#include + +namespace hex::plugin::builtin { + + void drawImageVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span 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 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); + } + } + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/pl_visualizers/line_plot.cpp b/plugins/builtin/source/content/pl_visualizers/line_plot.cpp new file mode 100644 index 000000000..eee3dcea6 --- /dev/null +++ b/plugins/builtin/source/content/pl_visualizers/line_plot.cpp @@ -0,0 +1,30 @@ +#include + +#include + +#include +#include + +#include + +namespace hex::plugin::builtin { + + void drawLinePlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span arguments) { + static std::vector 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(dataPattern.get()), ImPlot::GetPlotSize().x * 4); + } + + ImPlot::PlotLine("##line", values.data(), values.size()); + + ImPlot::EndPlot(); + } + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/pl_visualizers/scatter_plot.cpp b/plugins/builtin/source/content/pl_visualizers/scatter_plot.cpp new file mode 100644 index 000000000..3721977b2 --- /dev/null +++ b/plugins/builtin/source/content/pl_visualizers/scatter_plot.cpp @@ -0,0 +1,33 @@ +#include + +#include + +#include +#include + +#include + +namespace hex::plugin::builtin { + + void drawScatterPlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span arguments) { + static std::vector 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(xPattern.get()), ImPlot::GetPlotSize().x * 4); + yValues = sampleData(patternToArray(yPattern.get()), ImPlot::GetPlotSize().x * 4); + } + + ImPlot::PlotScatter("##scatter", xValues.data(), yValues.data(), xValues.size()); + + ImPlot::EndPlot(); + } + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/pl_visualizers/sound.cpp b/plugins/builtin/source/content/pl_visualizers/sound.cpp new file mode 100644 index 000000000..7a333fae1 --- /dev/null +++ b/plugins/builtin/source/content/pl_visualizers/sound.cpp @@ -0,0 +1,124 @@ +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace hex::plugin::builtin { + + void drawSoundVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool shouldReset, std::span arguments) { + auto wavePattern = arguments[0].toPattern(); + auto channels = arguments[1].toUnsigned(); + auto sampleRate = arguments[2].toUnsigned(); + + static std::vector 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(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::min(), std::numeric_limits::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); + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/pl_visualizers/timestamp.cpp b/plugins/builtin/source/content/pl_visualizers/timestamp.cpp new file mode 100644 index 000000000..ff5fa0639 --- /dev/null +++ b/plugins/builtin/source/content/pl_visualizers/timestamp.cpp @@ -0,0 +1,107 @@ +#include + +#include + +#include + +#include +#include + +#include + +#include +#include + +namespace hex::plugin::builtin { + + void drawTimestampVisualizer(pl::ptrn::Pattern &, pl::ptrn::IIterable &, bool, std::span 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(); + } + } + +} \ No newline at end of file