diff --git a/3rdparty/dear-imgui/widgets/gizmo.h b/3rdparty/dear-imgui/widgets/gizmo.h index b929150b7..718d1b456 100644 --- a/3rdparty/dear-imgui/widgets/gizmo.h +++ b/3rdparty/dear-imgui/widgets/gizmo.h @@ -1,5 +1,5 @@ // https://github.com/CedricGuillemet/ImGuizmo -// v 1.04 WIP +// v 1.61 WIP // // The MIT License(MIT) // @@ -105,18 +105,21 @@ void EditTransform(const Camera& camera, matrix_t& matrix) namespace ImGuizmo { + // call inside your own window and before Manipulate() in order to draw gizmo to that window. + IMGUI_API void SetDrawlist(); + // call BeginFrame right after ImGui_XXXX_NewFrame(); - void BeginFrame(); + IMGUI_API void BeginFrame(); // return true if mouse cursor is over any gizmo control (axis, plan or screen component) - bool IsOver(); + IMGUI_API bool IsOver(); // return true if mouse IsOver or if the gizmo is in moving state - bool IsUsing(); + IMGUI_API bool IsUsing(); // enable/disable the gizmo. Stay in the state until next call to Enable. // gizmo is rendered with gray half transparent color when disabled - void Enable(bool enable); + IMGUI_API void Enable(bool enable); // helper functions for manualy editing translation/rotation/scale with an input float // translation, rotation and scale float points to 3 floats each @@ -130,13 +133,16 @@ namespace ImGuizmo // ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16); // // These functions have some numerical stability issues for now. Use with caution. - void DecomposeMatrixToComponents(const float *matrix, float *translation, float *rotation, float *scale); - void RecomposeMatrixFromComponents(const float *translation, const float *rotation, const float *scale, float *matrix); + IMGUI_API void DecomposeMatrixToComponents(const float *matrix, float *translation, float *rotation, float *scale); + IMGUI_API void RecomposeMatrixFromComponents(const float *translation, const float *rotation, const float *scale, float *matrix); - void SetRect(float x, float y, float width, float height); + IMGUI_API void SetRect(float x, float y, float width, float height); + // default is false + IMGUI_API void SetOrthographic(bool isOrthographic); // Render a cube with face color corresponding to face normal. Usefull for debug/tests - void DrawCube(const float *view, const float *projection, float *matrix); + IMGUI_API void DrawCube(const float *view, const float *projection, const float *matrix); + IMGUI_API void DrawGrid(const float *view, const float *projection, const float *matrix, const float gridSize); // call it when you want a gizmo // Needs view and projection matrices. @@ -147,6 +153,7 @@ namespace ImGuizmo TRANSLATE, ROTATE, SCALE, + BOUNDS, }; enum MODE @@ -155,5 +162,5 @@ namespace ImGuizmo WORLD }; - void Manipulate(const float *view, const float *projection, OPERATION operation, MODE mode, float *matrix, float *deltaMatrix = 0, float *snap = 0, float *localBounds = NULL, float *boundsSnap = NULL); + IMGUI_API void Manipulate(const float *view, const float *projection, OPERATION operation, MODE mode, float *matrix, float *deltaMatrix = 0, float *snap = 0, float *localBounds = NULL, float *boundsSnap = NULL); }; diff --git a/3rdparty/dear-imgui/widgets/gizmo.inl b/3rdparty/dear-imgui/widgets/gizmo.inl index 749a0d20d..363bfada1 100644 --- a/3rdparty/dear-imgui/widgets/gizmo.inl +++ b/3rdparty/dear-imgui/widgets/gizmo.inl @@ -1,1820 +1,1973 @@ -// The MIT License(MIT) -// -// Copyright(c) 2016 Cedric Guillemet -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#define IMGUI_DEFINE_MATH_OPERATORS - -// includes patches for multiview from -// https://github.com/CedricGuillemet/ImGuizmo/issues/15 - -namespace ImGuizmo -{ - static const float ZPI = 3.14159265358979323846f; - static const float RAD2DEG = (180.f / ZPI); - static const float DEG2RAD = (ZPI / 180.f); - - const float screenRotateSize = 0.06f; - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // utility and math - - void FPU_MatrixF_x_MatrixF(const float *a, const float *b, float *r) - { - r[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12]; - r[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13]; - r[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14]; - r[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15]; - - r[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12]; - r[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13]; - r[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14]; - r[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15]; - - r[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12]; - r[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13]; - r[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14]; - r[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15]; - - r[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12]; - r[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13]; - r[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14]; - r[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15]; - } - - //template T LERP(T x, T y, float z) { return (x + (y - x)*z); } - template T Clamp(T x, T y, T z) { return ((xz) ? z : x)); } - template T max(T x, T y) { return (x > y) ? x : y; } - template T min(T x, T y) { return (x < y) ? x : y; } - template bool IsWithin(T x, T y, T z) { return (x>=y) && (x<=z); } - - struct matrix_t; - struct vec_t - { - public: - float x, y, z, w; - - void Lerp(const vec_t& v, float t) - { - x += (v.x - x) * t; - y += (v.y - y) * t; - z += (v.z - z) * t; - w += (v.w - w) * t; - } - - void Set(float v) { x = y = z = w = v; } - void Set(float _x, float _y, float _z = 0.f, float _w = 0.f) { x = _x; y = _y; z = _z; w = _w; } - - vec_t& operator -= (const vec_t& v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; } - vec_t& operator += (const vec_t& v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; } - vec_t& operator *= (const vec_t& v) { x *= v.x; y *= v.y; z *= v.z; w *= v.w; return *this; } - vec_t& operator *= (float v) { x *= v; y *= v; z *= v; w *= v; return *this; } - - vec_t operator * (float f) const; - vec_t operator - () const; - vec_t operator - (const vec_t& v) const; - vec_t operator + (const vec_t& v) const; - vec_t operator * (const vec_t& v) const; - - const vec_t& operator + () const { return (*this); } - float Length() const { return sqrtf(x*x + y*y + z*z); }; - float LengthSq() const { return (x*x + y*y + z*z); }; - vec_t Normalize() { (*this) *= (1.f / Length()); return (*this); } - vec_t Normalize(const vec_t& v) { this->Set(v.x, v.y, v.z, v.w); this->Normalize(); return (*this); } - vec_t Abs() const; - void Cross(const vec_t& v) - { - vec_t res; - res.x = y * v.z - z * v.y; - res.y = z * v.x - x * v.z; - res.z = x * v.y - y * v.x; - - x = res.x; - y = res.y; - z = res.z; - w = 0.f; - } - void Cross(const vec_t& v1, const vec_t& v2) - { - x = v1.y * v2.z - v1.z * v2.y; - y = v1.z * v2.x - v1.x * v2.z; - z = v1.x * v2.y - v1.y * v2.x; - w = 0.f; - } - float Dot(const vec_t &v) const - { - return (x * v.x) + (y * v.y) + (z * v.z) + (w * v.w); - } - float Dot3(const vec_t &v) const - { - return (x * v.x) + (y * v.y) + (z * v.z); - } - - void Transform(const matrix_t& matrix); - void Transform(const vec_t & s, const matrix_t& matrix); - - void TransformVector(const matrix_t& matrix); - void TransformPoint(const matrix_t& matrix); - void TransformVector(const vec_t& v, const matrix_t& matrix) { (*this) = v; this->TransformVector(matrix); } - void TransformPoint(const vec_t& v, const matrix_t& matrix) { (*this) = v; this->TransformPoint(matrix); } - - float& operator [] (size_t index) { return ((float*)&x)[index]; } - const float& operator [] (size_t index) const { return ((float*)&x)[index]; } - }; - - vec_t makeVect(float _x, float _y, float _z = 0.f, float _w = 0.f) { vec_t res; res.x = _x; res.y = _y; res.z = _z; res.w = _w; return res; } - vec_t vec_t::operator * (float f) const { return makeVect(x * f, y * f, z * f, w *f); } - vec_t vec_t::operator - () const { return makeVect(-x, -y, -z, -w); } - vec_t vec_t::operator - (const vec_t& v) const { return makeVect(x - v.x, y - v.y, z - v.z, w - v.w); } - vec_t vec_t::operator + (const vec_t& v) const { return makeVect(x + v.x, y + v.y, z + v.z, w + v.w); } - vec_t vec_t::operator * (const vec_t& v) const { return makeVect(x * v.x, y * v.y, z * v.z, w * v.w); } - vec_t vec_t::Abs() const { return makeVect(fabsf(x), fabsf(y), fabsf(z)); } - - vec_t Normalized(const vec_t& v) { vec_t res; res = v; res.Normalize(); return res; } - vec_t Cross(const vec_t& v1, const vec_t& v2) - { - vec_t res; - res.x = v1.y * v2.z - v1.z * v2.y; - res.y = v1.z * v2.x - v1.x * v2.z; - res.z = v1.x * v2.y - v1.y * v2.x; - res.w = 0.f; - return res; - } - - float Dot(const vec_t &v1, const vec_t &v2) - { - return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z); - } - - vec_t BuildPlan(const vec_t & p_point1, const vec_t & p_normal) - { - vec_t normal, res; - normal.Normalize(p_normal); - res.w = normal.Dot(p_point1); - res.x = normal.x; - res.y = normal.y; - res.z = normal.z; - return res; - } - - struct matrix_t - { - public: - - union - { - float m[4][4]; - float m16[16]; - struct - { - vec_t right, up, dir, position; - } v; - vec_t component[4]; - }; - - matrix_t(const matrix_t& other) { memcpy(&m16[0], &other.m16[0], sizeof(float) * 16); } - matrix_t() {} - - operator float * () { return m16; } - operator const float* () const { return m16; } - void Translation(float _x, float _y, float _z) { this->Translation(makeVect(_x, _y, _z)); } - - void Translation(const vec_t& vt) - { - v.right.Set(1.f, 0.f, 0.f, 0.f); - v.up.Set(0.f, 1.f, 0.f, 0.f); - v.dir.Set(0.f, 0.f, 1.f, 0.f); - v.position.Set(vt.x, vt.y, vt.z, 1.f); - } - - void Scale(float _x, float _y, float _z) - { - v.right.Set(_x, 0.f, 0.f, 0.f); - v.up.Set(0.f, _y, 0.f, 0.f); - v.dir.Set(0.f, 0.f, _z, 0.f); - v.position.Set(0.f, 0.f, 0.f, 1.f); - } - void Scale(const vec_t& s) { Scale(s.x, s.y, s.z); } - - matrix_t& operator *= (const matrix_t& mat) - { - matrix_t tmpMat; - tmpMat = *this; - tmpMat.Multiply(mat); - *this = tmpMat; - return *this; - } - matrix_t operator * (const matrix_t& mat) const - { - matrix_t matT; - matT.Multiply(*this, mat); - return matT; - } - - void Multiply(const matrix_t &matrix) - { - matrix_t tmp; - tmp = *this; - - FPU_MatrixF_x_MatrixF((float*)&tmp, (float*)&matrix, (float*)this); - } - - void Multiply(const matrix_t &m1, const matrix_t &m2) - { - FPU_MatrixF_x_MatrixF((float*)&m1, (float*)&m2, (float*)this); - } - - float GetDeterminant() const - { - return m[0][0] * m[1][1] * m[2][2] + m[0][1] * m[1][2] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - - m[0][2] * m[1][1] * m[2][0] - m[0][1] * m[1][0] * m[2][2] - m[0][0] * m[1][2] * m[2][1]; - } - - float Inverse(const matrix_t &srcMatrix, bool affine = false); - void SetToIdentity() - { - v.right.Set(1.f, 0.f, 0.f, 0.f); - v.up.Set(0.f, 1.f, 0.f, 0.f); - v.dir.Set(0.f, 0.f, 1.f, 0.f); - v.position.Set(0.f, 0.f, 0.f, 1.f); - } - void Transpose() - { - matrix_t tmpm; - for (int l = 0; l < 4; l++) - { - for (int c = 0; c < 4; c++) - { - tmpm.m[l][c] = m[c][l]; - } - } - (*this) = tmpm; - } - - void RotationAxis(const vec_t & axis, float angle); - - void OrthoNormalize() - { - v.right.Normalize(); - v.up.Normalize(); - v.dir.Normalize(); - } - }; - - void vec_t::Transform(const matrix_t& matrix) - { - vec_t out; - - out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + w * matrix.m[3][0]; - out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + w * matrix.m[3][1]; - out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + w * matrix.m[3][2]; - out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + w * matrix.m[3][3]; - - x = out.x; - y = out.y; - z = out.z; - w = out.w; - } - - void vec_t::Transform(const vec_t & s, const matrix_t& matrix) - { - *this = s; - Transform(matrix); - } - - void vec_t::TransformPoint(const matrix_t& matrix) - { - vec_t out; - - out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + matrix.m[3][0]; - out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + matrix.m[3][1]; - out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + matrix.m[3][2]; - out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + matrix.m[3][3]; - - x = out.x; - y = out.y; - z = out.z; - w = out.w; - } - - - void vec_t::TransformVector(const matrix_t& matrix) - { - vec_t out; - - out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0]; - out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1]; - out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2]; - out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3]; - - x = out.x; - y = out.y; - z = out.z; - w = out.w; - } - - float matrix_t::Inverse(const matrix_t &srcMatrix, bool affine) - { - float det = 0; - - if (affine) - { - det = GetDeterminant(); - float s = 1 / det; - m[0][0] = (srcMatrix.m[1][1] * srcMatrix.m[2][2] - srcMatrix.m[1][2] * srcMatrix.m[2][1]) * s; - m[0][1] = (srcMatrix.m[2][1] * srcMatrix.m[0][2] - srcMatrix.m[2][2] * srcMatrix.m[0][1]) * s; - m[0][2] = (srcMatrix.m[0][1] * srcMatrix.m[1][2] - srcMatrix.m[0][2] * srcMatrix.m[1][1]) * s; - m[1][0] = (srcMatrix.m[1][2] * srcMatrix.m[2][0] - srcMatrix.m[1][0] * srcMatrix.m[2][2]) * s; - m[1][1] = (srcMatrix.m[2][2] * srcMatrix.m[0][0] - srcMatrix.m[2][0] * srcMatrix.m[0][2]) * s; - m[1][2] = (srcMatrix.m[0][2] * srcMatrix.m[1][0] - srcMatrix.m[0][0] * srcMatrix.m[1][2]) * s; - m[2][0] = (srcMatrix.m[1][0] * srcMatrix.m[2][1] - srcMatrix.m[1][1] * srcMatrix.m[2][0]) * s; - m[2][1] = (srcMatrix.m[2][0] * srcMatrix.m[0][1] - srcMatrix.m[2][1] * srcMatrix.m[0][0]) * s; - m[2][2] = (srcMatrix.m[0][0] * srcMatrix.m[1][1] - srcMatrix.m[0][1] * srcMatrix.m[1][0]) * s; - m[3][0] = -(m[0][0] * srcMatrix.m[3][0] + m[1][0] * srcMatrix.m[3][1] + m[2][0] * srcMatrix.m[3][2]); - m[3][1] = -(m[0][1] * srcMatrix.m[3][0] + m[1][1] * srcMatrix.m[3][1] + m[2][1] * srcMatrix.m[3][2]); - m[3][2] = -(m[0][2] * srcMatrix.m[3][0] + m[1][2] * srcMatrix.m[3][1] + m[2][2] * srcMatrix.m[3][2]); - } - else - { - // transpose matrix - float src[16]; - for (int i = 0; i < 4; ++i) - { - src[i] = srcMatrix.m16[i * 4]; - src[i + 4] = srcMatrix.m16[i * 4 + 1]; - src[i + 8] = srcMatrix.m16[i * 4 + 2]; - src[i + 12] = srcMatrix.m16[i * 4 + 3]; - } - - // calculate pairs for first 8 elements (cofactors) - float tmp[12]; // temp array for pairs - tmp[0] = src[10] * src[15]; - tmp[1] = src[11] * src[14]; - tmp[2] = src[9] * src[15]; - tmp[3] = src[11] * src[13]; - tmp[4] = src[9] * src[14]; - tmp[5] = src[10] * src[13]; - tmp[6] = src[8] * src[15]; - tmp[7] = src[11] * src[12]; - tmp[8] = src[8] * src[14]; - tmp[9] = src[10] * src[12]; - tmp[10] = src[8] * src[13]; - tmp[11] = src[9] * src[12]; - - // calculate first 8 elements (cofactors) - m16[0] = (tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7]) - (tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7]); - m16[1] = (tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7]) - (tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7]); - m16[2] = (tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7]) - (tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7]); - m16[3] = (tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6]) - (tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6]); - m16[4] = (tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3]) - (tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3]); - m16[5] = (tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3]) - (tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3]); - m16[6] = (tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3]) - (tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3]); - m16[7] = (tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2]) - (tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2]); - - // calculate pairs for second 8 elements (cofactors) - tmp[0] = src[2] * src[7]; - tmp[1] = src[3] * src[6]; - tmp[2] = src[1] * src[7]; - tmp[3] = src[3] * src[5]; - tmp[4] = src[1] * src[6]; - tmp[5] = src[2] * src[5]; - tmp[6] = src[0] * src[7]; - tmp[7] = src[3] * src[4]; - tmp[8] = src[0] * src[6]; - tmp[9] = src[2] * src[4]; - tmp[10] = src[0] * src[5]; - tmp[11] = src[1] * src[4]; - - // calculate second 8 elements (cofactors) - m16[8] = (tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15]) - (tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15]); - m16[9] = (tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15]) - (tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15]); - m16[10] = (tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15]) - (tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15]); - m16[11] = (tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14]) - (tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14]); - m16[12] = (tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9]) - (tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10]); - m16[13] = (tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10]) - (tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8]); - m16[14] = (tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8]) - (tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9]); - m16[15] = (tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9]) - (tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8]); - - // calculate determinant - det = src[0] * m16[0] + src[1] * m16[1] + src[2] * m16[2] + src[3] * m16[3]; - - // calculate matrix inverse - float invdet = 1 / det; - for (int j = 0; j < 16; ++j) - { - m16[j] *= invdet; - } - } - - return det; - } - - void matrix_t::RotationAxis(const vec_t & axis, float angle) - { - float length2 = axis.LengthSq(); - if (length2 < FLT_EPSILON) - { - SetToIdentity(); - return; - } - - vec_t n = axis * (1.f / sqrtf(length2)); - float s = sinf(angle); - float c = cosf(angle); - float k = 1.f - c; - - float xx = n.x * n.x * k + c; - float yy = n.y * n.y * k + c; - float zz = n.z * n.z * k + c; - float xy = n.x * n.y * k; - float yz = n.y * n.z * k; - float zx = n.z * n.x * k; - float xs = n.x * s; - float ys = n.y * s; - float zs = n.z * s; - - m[0][0] = xx; - m[0][1] = xy + zs; - m[0][2] = zx - ys; - m[0][3] = 0.f; - m[1][0] = xy - zs; - m[1][1] = yy; - m[1][2] = yz + xs; - m[1][3] = 0.f; - m[2][0] = zx + ys; - m[2][1] = yz - xs; - m[2][2] = zz; - m[2][3] = 0.f; - m[3][0] = 0.f; - m[3][1] = 0.f; - m[3][2] = 0.f; - m[3][3] = 1.f; - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - - enum MOVETYPE - { - NONE, - MOVE_X, - MOVE_Y, - MOVE_Z, - MOVE_XY, - MOVE_XZ, - MOVE_YZ, - MOVE_SCREEN, - ROTATE_X, - ROTATE_Y, - ROTATE_Z, - ROTATE_SCREEN, - SCALE_X, - SCALE_Y, - SCALE_Z, - SCALE_XYZ, - BOUNDS - }; - - struct Context - { - Context() : mbUsing(false), mbEnable(true), mbUsingBounds(false) - { - } - - ImDrawList* mDrawList; - - MODE mMode; - matrix_t mViewMat; - matrix_t mProjectionMat; - matrix_t mModel; - matrix_t mModelInverse; - matrix_t mModelSource; - matrix_t mModelSourceInverse; - matrix_t mMVP; - matrix_t mViewProjection; - - vec_t mModelScaleOrigin; - vec_t mCameraEye; - vec_t mCameraRight; - vec_t mCameraDir; - vec_t mCameraUp; - vec_t mRayOrigin; - vec_t mRayVector; - - float mRadiusSquareCenter; - ImVec2 mScreenSquareCenter; - ImVec2 mScreenSquareMin; - ImVec2 mScreenSquareMax; - - float mScreenFactor; - vec_t mRelativeOrigin; - - bool mbUsing; - bool mbEnable; - - // translation - vec_t mTranslationPlan; - vec_t mTranslationPlanOrigin; - vec_t mMatrixOrigin; - - // rotation - vec_t mRotationVectorSource; - float mRotationAngle; - float mRotationAngleOrigin; - //vec_t mWorldToLocalAxis; - - // scale - vec_t mScale; - vec_t mScaleValueOrigin; - float mSaveMousePosx; - - // save axis factor when using gizmo - bool mBelowAxisLimit[3]; - bool mBelowPlaneLimit[3]; - float mAxisFactor[3]; - - // bounds stretching - vec_t mBoundsPivot; - vec_t mBoundsAnchor; - vec_t mBoundsPlan; - vec_t mBoundsLocalPivot; - int mBoundsBestAxis; - int mBoundsAxis[2]; - bool mbUsingBounds; - matrix_t mBoundsMatrix; - - // - int mCurrentOperation; - - float mX = 0.f; - float mY = 0.f; - float mWidth = 0.f; - float mHeight = 0.f; - float mXMax = 0.f; - float mYMax = 0.f; - }; - - static Context gContext; - - static const float angleLimit = 0.96f; - static const float planeLimit = 0.2f; - - static const vec_t directionUnary[3] = { makeVect(1.f, 0.f, 0.f), makeVect(0.f, 1.f, 0.f), makeVect(0.f, 0.f, 1.f) }; - static const ImU32 directionColor[3] = { 0xFF0000AA, 0xFF00AA00, 0xFFAA0000 }; - - // Alpha: 100%: FF, 87%: DE, 70%: B3, 54%: 8A, 50%: 80, 38%: 61, 12%: 1F - static const ImU32 planeBorderColor[3] = { 0xFFAA0000, 0xFF0000AA, 0xFF00AA00 }; - static const ImU32 planeColor[3] = { 0x610000AA, 0x6100AA00, 0x61AA0000 }; - static const ImU32 selectionColor = 0x8A1080FF; - static const ImU32 inactiveColor = 0x99999999; - static const ImU32 translationLineColor = 0xAAAAAAAA; - static const char *translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f", "X : %5.3f Y : %5.3f", "Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f Z : %5.3f" }; - static const char *scaleInfoMask[] = { "X : %5.2f", "Y : %5.2f", "Z : %5.2f", "XYZ : %5.2f" }; - static const char *rotationInfoMask[] = { "X : %5.2f deg %5.2f rad", "Y : %5.2f deg %5.2f rad", "Z : %5.2f deg %5.2f rad", "Screen : %5.2f deg %5.2f rad" }; - static const int translationInfoIndex[] = { 0,0,0, 1,0,0, 2,0,0, 0,1,0, 1,2,0, 0,2,1, 0,1,2 }; - static const float quadMin = 0.5f; - static const float quadMax = 0.8f; - static const float quadUV[8] = { quadMin, quadMin, quadMin, quadMax, quadMax, quadMax, quadMax, quadMin }; - static const int halfCircleSegmentCount = 64; - static const float snapTension = 0.5f; - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - static int GetMoveType(vec_t *gizmoHitProportion); - static int GetRotateType(); - static int GetScaleType(); - - static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat) - { - vec_t trans; - trans.TransformPoint(worldPos, mat); - trans *= 0.5f / trans.w; - trans += makeVect(0.5f, 0.5f); - trans.y = 1.f - trans.y; - trans.x *= gContext.mWidth; - trans.y *= gContext.mHeight; - trans.x += gContext.mX; - trans.y += gContext.mY; - return ImVec2(trans.x, trans.y); - } - - static void ComputeCameraRay(vec_t &rayOrigin, vec_t &rayDir) - { - ImGuiIO& io = ImGui::GetIO(); - - matrix_t mViewProjInverse; - mViewProjInverse.Inverse(gContext.mViewMat * gContext.mProjectionMat); - - float mox = ((io.MousePos.x - gContext.mX) / gContext.mWidth) * 2.f - 1.f; - float moy = (1.f - ((io.MousePos.y - gContext.mY) / gContext.mHeight)) * 2.f - 1.f; - - rayOrigin.Transform(makeVect(mox, moy, 0.f, 1.f), mViewProjInverse); - rayOrigin *= 1.f / rayOrigin.w; - vec_t rayEnd; - rayEnd.Transform(makeVect(mox, moy, 1.f, 1.f), mViewProjInverse); - rayEnd *= 1.f / rayEnd.w; - rayDir = Normalized(rayEnd - rayOrigin); - } - - static float IntersectRayPlane(const vec_t & rOrigin, const vec_t& rVector, const vec_t& plan) - { - float numer = plan.Dot3(rOrigin) - plan.w; - float denom = plan.Dot3(rVector); - - if (fabsf(denom) < FLT_EPSILON) // normal is orthogonal to vector, cant intersect - return -1.0f; - - return -(numer / denom); - } - - static bool IsInContextRect( ImVec2 p ) - { - return IsWithin( p.x, gContext.mX, gContext.mXMax ) && IsWithin(p.y, gContext.mY, gContext.mYMax ); - } - - void SetRect(float x, float y, float width, float height) - { - gContext.mX = x; - gContext.mY = y; - gContext.mWidth = width; - gContext.mHeight = height; - gContext.mXMax = gContext.mX + gContext.mWidth; - gContext.mYMax = gContext.mY + gContext.mXMax; - } - - void SetDrawlist() - { - gContext.mDrawList = ImGui::GetWindowDrawList(); - } - - void BeginFrame() - { - ImGuiIO& io = ImGui::GetIO(); - - const ImU32 flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus; - ImGui::SetNextWindowSize(io.DisplaySize); - - ImGui::PushStyleColor(ImGuiCol_WindowBg, 0); - ImGui::Begin("gizmo", NULL, flags); - gContext.mDrawList = ImGui::GetWindowDrawList(); - ImGui::End(); - ImGui::PopStyleColor(); - } - - bool IsUsing() - { - return gContext.mbUsing||gContext.mbUsingBounds; - } - - bool IsOver() - { - return (GetMoveType(NULL) != NONE) || GetRotateType() != NONE || GetScaleType() != NONE || IsUsing(); - } - - void Enable(bool enable) - { - gContext.mbEnable = enable; - if (!enable) - { - gContext.mbUsing = false; - gContext.mbUsingBounds = false; - } - } - - static float GetUniform(const vec_t& position, const matrix_t& mat) - { - vec_t trf = makeVect(position.x, position.y, position.z, 1.f); - trf.Transform(mat); - return trf.w; - } - - static void ComputeContext(const float *view, const float *projection, float *matrix, MODE mode) - { - gContext.mMode = mode; - gContext.mViewMat = *(matrix_t*)view; - gContext.mProjectionMat = *(matrix_t*)projection; - - if (mode == LOCAL) - { - gContext.mModel = *(matrix_t*)matrix; - gContext.mModel.OrthoNormalize(); - } - else - { - gContext.mModel.Translation(((matrix_t*)matrix)->v.position); - } - gContext.mModelSource = *(matrix_t*)matrix; - gContext.mModelScaleOrigin.Set(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length()); - - gContext.mModelInverse.Inverse(gContext.mModel); - gContext.mModelSourceInverse.Inverse(gContext.mModelSource); - gContext.mViewProjection = gContext.mViewMat * gContext.mProjectionMat; - gContext.mMVP = gContext.mModel * gContext.mViewProjection; - - matrix_t viewInverse; - viewInverse.Inverse(gContext.mViewMat); - gContext.mCameraDir = viewInverse.v.dir; - gContext.mCameraEye = viewInverse.v.position; - gContext.mCameraRight = viewInverse.v.right; - gContext.mCameraUp = viewInverse.v.up; - gContext.mScreenFactor = 0.1f * GetUniform(gContext.mModel.v.position, gContext.mViewProjection); - - ImVec2 centerSSpace = worldToPos(makeVect(0.f, 0.f), gContext.mMVP); - gContext.mScreenSquareCenter = centerSSpace; - gContext.mScreenSquareMin = ImVec2(centerSSpace.x - 10.f, centerSSpace.y - 10.f); - gContext.mScreenSquareMax = ImVec2(centerSSpace.x + 10.f, centerSSpace.y + 10.f); - - ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector); - } - - static void ComputeColors(ImU32 *colors, int type, OPERATION operation) - { - if (gContext.mbEnable) - { - switch (operation) - { - case TRANSLATE: - colors[0] = (type == MOVE_SCREEN) ? selectionColor : 0xFFFFFFFF; - for (int i = 0; i < 3; i++) - { - int colorPlaneIndex = (i + 2) % 3; - colors[i + 1] = (type == (int)(MOVE_X + i)) ? selectionColor : directionColor[i]; - colors[i + 4] = (type == (int)(MOVE_XY + i)) ? selectionColor : planeColor[colorPlaneIndex]; - colors[i + 4] = (type == MOVE_SCREEN) ? selectionColor : colors[i + 4]; - } - break; - case ROTATE: - colors[0] = (type == ROTATE_SCREEN) ? selectionColor : 0xFFFFFFFF; - for (int i = 0; i < 3; i++) - colors[i + 1] = (type == (int)(ROTATE_X + i)) ? selectionColor : directionColor[i]; - break; - case SCALE: - colors[0] = (type == SCALE_XYZ) ? selectionColor : 0xFFFFFFFF; - for (int i = 0; i < 3; i++) - colors[i + 1] = (type == (int)(SCALE_X + i)) ? selectionColor : directionColor[i]; - break; - } - } - else - { - for (int i = 0; i < 7; i++) - colors[i] = inactiveColor; - } - } - - static void ComputeTripodAxisAndVisibility(int axisIndex, vec_t& dirPlaneX, vec_t& dirPlaneY, bool& belowAxisLimit, bool& belowPlaneLimit) - { - const int planNormal = (axisIndex + 2) % 3; - dirPlaneX = directionUnary[axisIndex]; - dirPlaneY = directionUnary[(axisIndex + 1) % 3]; - - if (gContext.mbUsing) - { - // when using, use stored factors so the gizmo doesn't flip when we translate - belowAxisLimit = gContext.mBelowAxisLimit[axisIndex]; - belowPlaneLimit = gContext.mBelowPlaneLimit[axisIndex]; - - dirPlaneX *= gContext.mAxisFactor[axisIndex]; - dirPlaneY *= gContext.mAxisFactor[(axisIndex + 1) % 3]; - } - else - { - vec_t dirPlaneNormalWorld; - dirPlaneNormalWorld.TransformVector(directionUnary[planNormal], gContext.mModel); - dirPlaneNormalWorld.Normalize(); - - vec_t dirPlaneXWorld(dirPlaneX); - dirPlaneXWorld.TransformVector(gContext.mModel); - dirPlaneXWorld.Normalize(); - - vec_t dirPlaneYWorld(dirPlaneY); - dirPlaneYWorld.TransformVector(gContext.mModel); - dirPlaneYWorld.Normalize(); - - vec_t cameraEyeToGizmo = Normalized(gContext.mModel.v.position - gContext.mCameraEye); - float dotCameraDirX = cameraEyeToGizmo.Dot3(dirPlaneXWorld); - float dotCameraDirY = cameraEyeToGizmo.Dot3(dirPlaneYWorld); - - // compute factor values - float mulAxisX = (dotCameraDirX > 0.f) ? -1.f : 1.f; - float mulAxisY = (dotCameraDirY > 0.f) ? -1.f : 1.f; - dirPlaneX *= mulAxisX; - dirPlaneY *= mulAxisY; - - belowAxisLimit = fabsf(dotCameraDirX) < angleLimit; - belowPlaneLimit = (fabsf(cameraEyeToGizmo.Dot3(dirPlaneNormalWorld)) > planeLimit); - - // and store values - gContext.mAxisFactor[axisIndex] = mulAxisX; - gContext.mAxisFactor[(axisIndex+1)%3] = mulAxisY; - gContext.mBelowAxisLimit[axisIndex] = belowAxisLimit; - gContext.mBelowPlaneLimit[axisIndex] = belowPlaneLimit; - } - } - - static void ComputeSnap(float*value, float snap) - { - if (snap <= FLT_EPSILON) - return; - float modulo = fmodf(*value, snap); - float moduloRatio = fabsf(modulo) / snap; - if (moduloRatio < snapTension) - *value -= modulo; - else if (moduloRatio >(1.f - snapTension)) - *value = *value - modulo + snap * ((*value<0.f) ? -1.f : 1.f); - } - static void ComputeSnap(vec_t& value, float *snap) - { - for (int i = 0; i < 3; i++) - { - ComputeSnap(&value[i], snap[i]); - } - } - - static float ComputeAngleOnPlan() - { - const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); - vec_t localPos = Normalized(gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position); - - vec_t perpendicularVector; - perpendicularVector.Cross(gContext.mRotationVectorSource, gContext.mTranslationPlan); - perpendicularVector.Normalize(); - float acosAngle = Clamp(Dot(localPos, gContext.mRotationVectorSource), -0.9999f, 0.9999f); - float angle = acosf(acosAngle); - angle *= (Dot(localPos, perpendicularVector) < 0.f) ? 1.f : -1.f; - return angle; - } - - static void DrawRotationGizmo(int type) - { - ImDrawList* drawList = gContext.mDrawList; - - // colors - ImU32 colors[7]; - ComputeColors(colors, type, ROTATE); - - vec_t cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye); - cameraToModelNormalized.TransformVector(gContext.mModelInverse); - - gContext.mRadiusSquareCenter = screenRotateSize * gContext.mHeight; - for (int axis = 0; axis < 3; axis++) - { - ImVec2 circlePos[halfCircleSegmentCount]; - - float angleStart = atan2f(cameraToModelNormalized[(4-axis)%3], cameraToModelNormalized[(3 - axis) % 3]) + ZPI * 0.5f; - - for (unsigned int i = 0; i < halfCircleSegmentCount; i++) - { - float ng = angleStart + ZPI * ((float)i / (float)halfCircleSegmentCount); - vec_t axisPos = makeVect(cosf(ng), sinf(ng), 0.f); - vec_t pos = makeVect(axisPos[axis], axisPos[(axis+1)%3], axisPos[(axis+2)%3]) * gContext.mScreenFactor; - circlePos[i] = worldToPos(pos, gContext.mMVP); - } - - float radiusAxis = sqrtf( (ImLengthSqr(worldToPos(gContext.mModel.v.position, gContext.mViewProjection) - circlePos[0]) )); - if(radiusAxis > gContext.mRadiusSquareCenter) - gContext.mRadiusSquareCenter = radiusAxis; - - drawList->AddPolyline(circlePos, halfCircleSegmentCount, colors[3 - axis], false, 2); - } - drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), gContext.mRadiusSquareCenter, colors[0], 64, 3.f); - - if (gContext.mbUsing) - { - ImVec2 circlePos[halfCircleSegmentCount +1]; - - circlePos[0] = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); - for (unsigned int i = 1; i < halfCircleSegmentCount; i++) - { - float ng = gContext.mRotationAngle * ((float)(i-1) / (float)(halfCircleSegmentCount -1)); - matrix_t rotateVectorMatrix; - rotateVectorMatrix.RotationAxis(gContext.mTranslationPlan, ng); - vec_t pos; - pos.TransformPoint(gContext.mRotationVectorSource, rotateVectorMatrix); - pos *= gContext.mScreenFactor; - circlePos[i] = worldToPos(pos + gContext.mModel.v.position, gContext.mViewProjection); - } - drawList->AddConvexPolyFilled(circlePos, halfCircleSegmentCount, 0x801080FF); - drawList->AddPolyline(circlePos, halfCircleSegmentCount, 0xFF1080FF, true, 2); - - ImVec2 destinationPosOnScreen = circlePos[1]; - char tmps[512]; - ImFormatString(tmps, sizeof(tmps), rotationInfoMask[type - ROTATE_X], (gContext.mRotationAngle/ZPI)*180.f, gContext.mRotationAngle); - drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps); - drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps); - } - } - - static void DrawHatchedAxis(const vec_t& axis) - { - for (int j = 1; j < 10; j++) - { - ImVec2 baseSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2) * gContext.mScreenFactor, gContext.mMVP); - ImVec2 worldDirSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2 + 1) * gContext.mScreenFactor, gContext.mMVP); - gContext.mDrawList->AddLine(baseSSpace2, worldDirSSpace2, 0x80000000, 6.f); - } - } - - static void DrawScaleGizmo(int type) - { - ImDrawList* drawList = gContext.mDrawList; - - // colors - ImU32 colors[7]; - ComputeColors(colors, type, SCALE); - - // draw - vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f }; - - if (gContext.mbUsing) - scaleDisplay = gContext.mScale; - - for (unsigned int i = 0; i < 3; i++) - { - vec_t dirPlaneX, dirPlaneY; - bool belowAxisLimit, belowPlaneLimit; - ComputeTripodAxisAndVisibility(i, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); - - // draw axis - if (belowAxisLimit) - { - ImVec2 baseSSpace = worldToPos(dirPlaneX * 0.1f * gContext.mScreenFactor, gContext.mMVP); - ImVec2 worldDirSSpaceNoScale = worldToPos(dirPlaneX * gContext.mScreenFactor, gContext.mMVP); - ImVec2 worldDirSSpace = worldToPos((dirPlaneX * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVP); - - if (gContext.mbUsing) - { - drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, 0xFF404040, 3.f); - drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, 0xFF404040); - } - - drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f); - drawList->AddCircleFilled(worldDirSSpace, 6.f, colors[i + 1]); - - if (gContext.mAxisFactor[i] < 0.f) - DrawHatchedAxis(dirPlaneX * scaleDisplay[i]); - } - } - - // draw screen cirle - drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32); - - if (gContext.mbUsing) - { - //ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); - ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); - /*vec_t dif(destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y); - dif.Normalize(); - dif *= 5.f; - drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor); - drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor); - drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f); - */ - char tmps[512]; - //vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin; - int componentInfoIndex = (type - SCALE_X) * 3; - ImFormatString(tmps, sizeof(tmps), scaleInfoMask[type - SCALE_X], scaleDisplay[translationInfoIndex[componentInfoIndex]]); - drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps); - drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps); - } - } - - - static void DrawTranslationGizmo(int type) - { - ImDrawList* drawList = gContext.mDrawList; - if (!drawList) - return; - - // colors - ImU32 colors[7]; - ComputeColors(colors, type, TRANSLATE); - - const ImVec2 origin = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); - - // draw - bool belowAxisLimit = false; - bool belowPlaneLimit = false; - for (unsigned int i = 0; i < 3; ++i) - { - vec_t dirPlaneX, dirPlaneY; - ComputeTripodAxisAndVisibility(i, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); - - // draw axis - if (belowAxisLimit) - { - ImVec2 baseSSpace = worldToPos(dirPlaneX * 0.1f * gContext.mScreenFactor, gContext.mMVP); - ImVec2 worldDirSSpace = worldToPos(dirPlaneX * gContext.mScreenFactor, gContext.mMVP); - - drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f); - - // Arrow head begin - ImVec2 dir(origin - worldDirSSpace); - - float d = sqrtf(ImLengthSqr(dir)); - dir /= d; // Normalize - dir *= 6.0f; - - ImVec2 ortogonalDir(dir.y, -dir.x); // Perpendicular vector - ImVec2 a(worldDirSSpace + dir); - drawList->AddTriangleFilled(worldDirSSpace - dir, a + ortogonalDir, a - ortogonalDir, colors[i + 1]); - // Arrow head end - - if (gContext.mAxisFactor[i] < 0.f) - DrawHatchedAxis(dirPlaneX); - } - - // draw plane - if (belowPlaneLimit) - { - ImVec2 screenQuadPts[4]; - for (int j = 0; j < 4; ++j) - { - vec_t cornerWorldPos = (dirPlaneX * quadUV[j * 2] + dirPlaneY * quadUV[j * 2 + 1]) * gContext.mScreenFactor; - screenQuadPts[j] = worldToPos(cornerWorldPos, gContext.mMVP); - } - drawList->AddPolyline(screenQuadPts, 4, planeBorderColor[i], true, 1.0f); - drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4]); - } - } - - drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32); - - if (gContext.mbUsing) - { - ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); - ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); - vec_t dif = { destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y, 0.f, 0.f }; - dif.Normalize(); - dif *= 5.f; - drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor); - drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor); - drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f); - - char tmps[512]; - vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin; - int componentInfoIndex = (type - MOVE_X) * 3; - ImFormatString(tmps, sizeof(tmps), translationInfoMask[type - MOVE_X], deltaInfo[translationInfoIndex[componentInfoIndex]], deltaInfo[translationInfoIndex[componentInfoIndex + 1]], deltaInfo[translationInfoIndex[componentInfoIndex + 2]]); - drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps); - drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps); - } - } - - static bool CanActivate() - { - if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && !ImGui::IsAnyItemActive()) - return true; - return false; - } - - static void HandleAndDrawLocalBounds(float *bounds, matrix_t *matrix, float *snapValues) - { - ImGuiIO& io = ImGui::GetIO(); - ImDrawList* drawList = gContext.mDrawList; - - // compute best projection axis - vec_t axesWorldDirections[3]; - vec_t bestAxisWorldDirection = { 0.0f, 0.0f, 0.0f, 0.0f }; - int axes[3]; - unsigned int numAxes = 1; - axes[0] = gContext.mBoundsBestAxis; - int bestAxis = axes[0]; - if (!gContext.mbUsingBounds) - { - numAxes = 0; - float bestDot = 0.f; - for (unsigned int i = 0; i < 3; i++) - { - vec_t dirPlaneNormalWorld; - dirPlaneNormalWorld.TransformVector(directionUnary[i], gContext.mModelSource); - dirPlaneNormalWorld.Normalize(); - - float dt = fabsf( Dot(Normalized(gContext.mCameraEye - gContext.mModelSource.v.position), dirPlaneNormalWorld) ); - if ( dt >= bestDot ) - { - bestDot = dt; - bestAxis = i; - bestAxisWorldDirection = dirPlaneNormalWorld; - } - - if( dt >= 0.1f ) - { - axes[numAxes] = i; - axesWorldDirections[numAxes] = dirPlaneNormalWorld; - ++numAxes; - } - } - } - - if( numAxes == 0 ) - { - axes[0] = bestAxis; - axesWorldDirections[0] = bestAxisWorldDirection; - numAxes = 1; - } - else if( bestAxis != axes[0] ) - { - unsigned int bestIndex = 0; - for (unsigned int i = 0; i < numAxes; i++) - { - if( axes[i] == bestAxis ) - { - bestIndex = i; - break; - } - } - int tempAxis = axes[0]; - axes[0] = axes[bestIndex]; - axes[bestIndex] = tempAxis; - vec_t tempDirection = axesWorldDirections[0]; - axesWorldDirections[0] = axesWorldDirections[bestIndex]; - axesWorldDirections[bestIndex] = tempDirection; - } - - for (unsigned int axisIndex = 0; axisIndex < numAxes; ++axisIndex) - { - bestAxis = axes[axisIndex]; - bestAxisWorldDirection = axesWorldDirections[axisIndex]; - - // corners - vec_t aabb[4]; - - int secondAxis = (bestAxis + 1) % 3; - int thirdAxis = (bestAxis + 2) % 3; - - for (int i = 0; i < 4; i++) - { - aabb[i][3] = aabb[i][bestAxis] = 0.f; - aabb[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)]; - aabb[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))]; - } - - // draw bounds - unsigned int anchorAlpha = gContext.mbEnable ? 0xFF000000 : 0x80000000; - - matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection; - for (int i = 0; i < 4;i++) - { - ImVec2 worldBound1 = worldToPos(aabb[i], boundsMVP); - ImVec2 worldBound2 = worldToPos(aabb[(i+1)%4], boundsMVP); - if( !IsInContextRect( worldBound1 ) || !IsInContextRect( worldBound2 ) ) - { - continue; - } - float boundDistance = sqrtf(ImLengthSqr(worldBound1 - worldBound2)); - int stepCount = (int)(boundDistance / 10.f); - stepCount = min( stepCount, 1000 ); - float stepLength = 1.f / (float)stepCount; - for (int j = 0; j < stepCount; j++) - { - float t1 = (float)j * stepLength; - float t2 = (float)j * stepLength + stepLength * 0.5f; - ImVec2 worldBoundSS1 = ImLerp(worldBound1, worldBound2, ImVec2(t1, t1)); - ImVec2 worldBoundSS2 = ImLerp(worldBound1, worldBound2, ImVec2(t2, t2)); - drawList->AddLine(worldBoundSS1, worldBoundSS2, 0xAAAAAA + anchorAlpha, 3.f); - } - vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4] ) * 0.5f; - ImVec2 midBound = worldToPos(midPoint, boundsMVP); - static const float AnchorBigRadius = 8.f; - static const float AnchorSmallRadius = 6.f; - bool overBigAnchor = ImLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius); - bool overSmallAnchor = ImLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius); - - - unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (0xAAAAAA + anchorAlpha); - unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (0xAAAAAA + anchorAlpha); - - drawList->AddCircleFilled(worldBound1, AnchorBigRadius, bigAnchorColor); - drawList->AddCircleFilled(midBound, AnchorSmallRadius, smallAnchorColor); - int oppositeIndex = (i + 2) % 4; - // big anchor on corners - if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && CanActivate()) - { - gContext.mBoundsPivot.TransformPoint(aabb[(i + 2) % 4], gContext.mModelSource); - gContext.mBoundsAnchor.TransformPoint(aabb[i], gContext.mModelSource); - gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection); - gContext.mBoundsBestAxis = bestAxis; - gContext.mBoundsAxis[0] = secondAxis; - gContext.mBoundsAxis[1] = thirdAxis; - - gContext.mBoundsLocalPivot.Set(0.f); - gContext.mBoundsLocalPivot[secondAxis] = aabb[oppositeIndex][secondAxis]; - gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis]; - - gContext.mbUsingBounds = true; - gContext.mBoundsMatrix = gContext.mModelSource; - } - // small anchor on middle of segment - if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && CanActivate()) - { - vec_t midPointOpposite = (aabb[(i + 2) % 4] + aabb[(i + 3) % 4]) * 0.5f; - gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource); - gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource); - gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection); - gContext.mBoundsBestAxis = bestAxis; - int indices[] = { secondAxis , thirdAxis }; - gContext.mBoundsAxis[0] = indices[i%2]; - gContext.mBoundsAxis[1] = -1; - - gContext.mBoundsLocalPivot.Set(0.f); - gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f); - - gContext.mbUsingBounds = true; - gContext.mBoundsMatrix = gContext.mModelSource; - } - } - - if (gContext.mbUsingBounds) - { - matrix_t scale; - scale.SetToIdentity(); - - // compute projected mouse position on plan - const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mBoundsPlan); - vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; - - // compute a reference and delta vectors base on mouse move - vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs(); - vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs(); - - // for 1 or 2 axes, compute a ratio that's used for scale and snap it based on resulting length - for (int i = 0; i < 2; i++) - { - int axisIndex1 = gContext.mBoundsAxis[i]; - if (axisIndex1 == -1) - continue; - - float ratioAxis = 1.f; - vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex1].Abs(); - - float dtAxis = axisDir.Dot(referenceVector); - float boundSize = bounds[axisIndex1 + 3] - bounds[axisIndex1]; - if (dtAxis > FLT_EPSILON) - ratioAxis = axisDir.Dot(deltaVector) / dtAxis; - - if (snapValues) - { - float length = boundSize * ratioAxis; - ComputeSnap(&length, snapValues[axisIndex1]); - if (boundSize > FLT_EPSILON) - ratioAxis = length / boundSize; - } - scale.component[axisIndex1] *= ratioAxis; - } - - // transform matrix - matrix_t preScale, postScale; - preScale.Translation(-gContext.mBoundsLocalPivot); - postScale.Translation(gContext.mBoundsLocalPivot); - matrix_t res = preScale * scale * postScale * gContext.mBoundsMatrix; - *matrix = res; - - // info text - char tmps[512]; - ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); - ImFormatString(tmps, sizeof(tmps), "X: %.2f Y: %.2f Z:%.2f" - , (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length() * scale.component[0].Length() - , (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length() * scale.component[1].Length() - , (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length() * scale.component[2].Length() - ); - drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps); - drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps); - } - - if (!io.MouseDown[0]) - gContext.mbUsingBounds = false; - - if( gContext.mbUsingBounds ) - break; - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - - static int GetScaleType() - { - ImGuiIO& io = ImGui::GetIO(); - int type = NONE; - - // screen - if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x && - io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y) - type = SCALE_XYZ; - - const vec_t direction[3] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir }; - // compute - for (unsigned int i = 0; i < 3 && type == NONE; i++) - { - vec_t dirPlaneX, dirPlaneY; - bool belowAxisLimit, belowPlaneLimit; - ComputeTripodAxisAndVisibility(i, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); - dirPlaneX.TransformVector(gContext.mModel); - dirPlaneY.TransformVector(gContext.mModel); - - const int planNormal = (i + 2) % 3; - - const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, direction[planNormal])); - vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len; - - const float dx = dirPlaneX.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor)); - const float dy = dirPlaneY.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor)); - if (belowAxisLimit && dy > -0.1f && dy < 0.1f && dx > 0.1f && dx < 1.f) - type = SCALE_X + i; - } - return type; - } - - static int GetRotateType() - { - ImGuiIO& io = ImGui::GetIO(); - int type = NONE; - - vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f }; - float dist = deltaScreen.Length(); - if (dist >= (gContext.mRadiusSquareCenter - 1.0f) && dist < (gContext.mRadiusSquareCenter + 1.0f)) - type = ROTATE_SCREEN; - - const vec_t planNormals[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir}; - - for (unsigned int i = 0; i < 3 && type == NONE; i++) - { - // pickup plan - vec_t pickupPlan = BuildPlan(gContext.mModel.v.position, planNormals[i]); - - const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, pickupPlan); - vec_t localPos = gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position; - - if (Dot(Normalized(localPos), gContext.mRayVector) > FLT_EPSILON) - continue; - - float distance = localPos.Length() / gContext.mScreenFactor; - if (distance > 0.9f && distance < 1.1f) - type = ROTATE_X + i; - } - - return type; - } - - static int GetMoveType(vec_t *gizmoHitProportion) - { - ImGuiIO& io = ImGui::GetIO(); - int type = NONE; - - // screen - if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x && - io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y) - type = MOVE_SCREEN; - - const vec_t direction[3] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir }; - - // compute - for (unsigned int i = 0; i < 3 && type == NONE; i++) - { - vec_t dirPlaneX, dirPlaneY; - bool belowAxisLimit, belowPlaneLimit; - ComputeTripodAxisAndVisibility(i, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); - dirPlaneX.TransformVector(gContext.mModel); - dirPlaneY.TransformVector(gContext.mModel); - - const int planNormal = (i + 2) % 3; - - const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, direction[planNormal])); - vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len; - - const float dx = dirPlaneX.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor)); - const float dy = dirPlaneY.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor)); - if (belowAxisLimit && dy > -0.1f && dy < 0.1f && dx > 0.1f && dx < 1.f) - type = MOVE_X + i; - - if (belowPlaneLimit && dx >= quadUV[0] && dx <= quadUV[4] && dy >= quadUV[1] && dy <= quadUV[3]) - type = MOVE_XY + i; - - if (gizmoHitProportion) - *gizmoHitProportion = makeVect(dx, dy, 0.f); - } - return type; - } - - static void HandleTranslation(float *matrix, float *deltaMatrix, int& type, float *snap) - { - ImGuiIO& io = ImGui::GetIO(); - bool applyRotationLocaly = gContext.mMode == LOCAL || type == MOVE_SCREEN; - - // move - if (gContext.mbUsing) - { - ImGui::CaptureMouseFromApp(); - const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); - vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; - - // compute delta - vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor; - vec_t delta = newOrigin - gContext.mModel.v.position; - - // 1 axis constraint - if (gContext.mCurrentOperation >= MOVE_X && gContext.mCurrentOperation <= MOVE_Z) - { - int axisIndex = gContext.mCurrentOperation - MOVE_X; - const vec_t& axisValue = *(vec_t*)&gContext.mModel.m[axisIndex]; - float lengthOnAxis = Dot(axisValue, delta); - delta = axisValue * lengthOnAxis; - } - - // snap - if (snap) - { - vec_t cumulativeDelta = gContext.mModel.v.position + delta - gContext.mMatrixOrigin; - if (applyRotationLocaly) - { - matrix_t modelSourceNormalized = gContext.mModelSource; - modelSourceNormalized.OrthoNormalize(); - matrix_t modelSourceNormalizedInverse; - modelSourceNormalizedInverse.Inverse(modelSourceNormalized); - cumulativeDelta.TransformVector(modelSourceNormalizedInverse); - ComputeSnap(cumulativeDelta, snap); - cumulativeDelta.TransformVector(modelSourceNormalized); - } - else - { - ComputeSnap(cumulativeDelta, snap); - } - delta = gContext.mMatrixOrigin + cumulativeDelta - gContext.mModel.v.position; - - } - - // compute matrix & delta - matrix_t deltaMatrixTranslation; - deltaMatrixTranslation.Translation(delta); - if (deltaMatrix) - memcpy(deltaMatrix, deltaMatrixTranslation.m16, sizeof(float) * 16); - - - matrix_t res = gContext.mModelSource * deltaMatrixTranslation; - *(matrix_t*)matrix = res; - - if (!io.MouseDown[0]) - gContext.mbUsing = false; - - type = gContext.mCurrentOperation; - } - else - { - // find new possible way to move - vec_t gizmoHitProportion; - type = GetMoveType(&gizmoHitProportion); - if (CanActivate() && type != NONE) - { - ImGui::CaptureMouseFromApp(); - gContext.mbUsing = true; - gContext.mCurrentOperation = type; - const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.up, -gContext.mCameraDir }; - // pickup plan - gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MOVE_X]); - const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); - gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len; - gContext.mMatrixOrigin = gContext.mModel.v.position; - - gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor); - } - } - } - - static void HandleScale(float *matrix, float *deltaMatrix, int& type, float *snap) - { - ImGuiIO& io = ImGui::GetIO(); - - if (!gContext.mbUsing) - { - // find new possible way to scale - type = GetScaleType(); - if (CanActivate() && type != NONE) - { - ImGui::CaptureMouseFromApp(); - gContext.mbUsing = true; - gContext.mCurrentOperation = type; - const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.up, gContext.mModel.v.right, -gContext.mCameraDir }; - // pickup plan - - gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - SCALE_X]); - const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); - gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len; - gContext.mMatrixOrigin = gContext.mModel.v.position; - gContext.mScale.Set(1.f, 1.f, 1.f); - gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor); - gContext.mScaleValueOrigin = makeVect(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length()); - gContext.mSaveMousePosx = io.MousePos.x; - } - } - // scale - if (gContext.mbUsing) - { - ImGui::CaptureMouseFromApp(); - const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); - vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; - vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor; - vec_t delta = newOrigin - gContext.mModel.v.position; - - // 1 axis constraint - if (gContext.mCurrentOperation >= SCALE_X && gContext.mCurrentOperation <= SCALE_Z) - { - int axisIndex = gContext.mCurrentOperation - SCALE_X; - const vec_t& axisValue = *(vec_t*)&gContext.mModel.m[axisIndex]; - float lengthOnAxis = Dot(axisValue, delta); - delta = axisValue * lengthOnAxis; - - vec_t baseVector = gContext.mTranslationPlanOrigin - gContext.mModel.v.position; - float ratio = Dot(axisValue, baseVector + delta) / Dot(axisValue, baseVector); - - gContext.mScale[axisIndex] = max(ratio, 0.001f); - } - else - { - float scaleDelta = (io.MousePos.x - gContext.mSaveMousePosx) * 0.01f; - gContext.mScale.Set(max(1.f + scaleDelta, 0.001f)); - } - - // snap - if (snap) - { - float scaleSnap[] = { snap[0], snap[0], snap[0] }; - ComputeSnap(gContext.mScale, scaleSnap); - } - - // no 0 allowed - for (int i = 0; i < 3;i++) - gContext.mScale[i] = max(gContext.mScale[i], 0.001f); - - // compute matrix & delta - matrix_t deltaMatrixScale; - deltaMatrixScale.Scale(gContext.mScale * gContext.mScaleValueOrigin); - - matrix_t res = deltaMatrixScale * gContext.mModel; - *(matrix_t*)matrix = res; - - if (deltaMatrix) - { - deltaMatrixScale.Scale(gContext.mScale); - memcpy(deltaMatrix, deltaMatrixScale.m16, sizeof(float) * 16); - } - - if (!io.MouseDown[0]) - gContext.mbUsing = false; - - type = gContext.mCurrentOperation; - } - } - - static void HandleRotation(float *matrix, float *deltaMatrix, int& type, float *snap) - { - ImGuiIO& io = ImGui::GetIO(); - bool applyRotationLocaly = gContext.mMode == LOCAL; - - if (!gContext.mbUsing) - { - type = GetRotateType(); - - if (type == ROTATE_SCREEN) - { - applyRotationLocaly = true; - } - - if (CanActivate() && type != NONE) - { - ImGui::CaptureMouseFromApp(); - gContext.mbUsing = true; - gContext.mCurrentOperation = type; - const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir }; - // pickup plan - if (applyRotationLocaly) - { - gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, rotatePlanNormal[type - ROTATE_X]); - } - else - { - gContext.mTranslationPlan = BuildPlan(gContext.mModelSource.v.position, directionUnary[type - ROTATE_X]); - } - - const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); - vec_t localPos = gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position; - gContext.mRotationVectorSource = Normalized(localPos); - gContext.mRotationAngleOrigin = ComputeAngleOnPlan(); - } - } - - // rotation - if (gContext.mbUsing) - { - ImGui::CaptureMouseFromApp(); - gContext.mRotationAngle = ComputeAngleOnPlan(); - if (snap) - { - float snapInRadian = snap[0] * DEG2RAD; - ComputeSnap(&gContext.mRotationAngle, snapInRadian); - } - vec_t rotationAxisLocalSpace; - - rotationAxisLocalSpace.TransformVector(makeVect(gContext.mTranslationPlan.x, gContext.mTranslationPlan.y, gContext.mTranslationPlan.z, 0.f), gContext.mModelInverse); - rotationAxisLocalSpace.Normalize(); - - matrix_t deltaRotation; - deltaRotation.RotationAxis(rotationAxisLocalSpace, gContext.mRotationAngle - gContext.mRotationAngleOrigin); - gContext.mRotationAngleOrigin = gContext.mRotationAngle; - - matrix_t scaleOrigin; - scaleOrigin.Scale(gContext.mModelScaleOrigin); - - if (applyRotationLocaly) - { - *(matrix_t*)matrix = scaleOrigin * deltaRotation * gContext.mModel; - } - else - { - matrix_t res = gContext.mModelSource; - res.v.position.Set(0.f); - - *(matrix_t*)matrix = res * deltaRotation; - ((matrix_t*)matrix)->v.position = gContext.mModelSource.v.position; - } - - if (deltaMatrix) - { - *(matrix_t*)deltaMatrix = gContext.mModelInverse * deltaRotation * gContext.mModel; - } - - if (!io.MouseDown[0]) - gContext.mbUsing = false; - - type = gContext.mCurrentOperation; - } - } - - void DecomposeMatrixToComponents(const float *matrix, float *translation, float *rotation, float *scale) - { - matrix_t mat = *(matrix_t*)matrix; - - scale[0] = mat.v.right.Length(); - scale[1] = mat.v.up.Length(); - scale[2] = mat.v.dir.Length(); - - mat.OrthoNormalize(); - - rotation[0] = RAD2DEG * atan2f(mat.m[1][2], mat.m[2][2]); - rotation[1] = RAD2DEG * atan2f(-mat.m[0][2], sqrtf(mat.m[1][2] * mat.m[1][2] + mat.m[2][2]* mat.m[2][2])); - rotation[2] = RAD2DEG * atan2f(mat.m[0][1], mat.m[0][0]); - - translation[0] = mat.v.position.x; - translation[1] = mat.v.position.y; - translation[2] = mat.v.position.z; - } - - void RecomposeMatrixFromComponents(const float *translation, const float *rotation, const float *scale, float *matrix) - { - matrix_t& mat = *(matrix_t*)matrix; - - matrix_t rot[3]; - for (int i = 0; i < 3;i++) - rot[i].RotationAxis(directionUnary[i], rotation[i] * DEG2RAD); - - mat = rot[0] * rot[1] * rot[2]; - - float validScale[3]; - for (int i = 0; i < 3; i++) - { - if (fabsf(scale[i]) < FLT_EPSILON) - validScale[i] = 0.001f; - else - validScale[i] = scale[i]; - } - mat.v.right *= validScale[0]; - mat.v.up *= validScale[1]; - mat.v.dir *= validScale[2]; - mat.v.position.Set(translation[0], translation[1], translation[2], 1.f); - } - - void Manipulate(const float *view, const float *projection, OPERATION operation, MODE mode, float *matrix, float *deltaMatrix, float *snap, float *localBounds, float *boundsSnap) - { - ComputeContext(view, projection, matrix, mode); - - // set delta to identity - if (deltaMatrix) - ((matrix_t*)deltaMatrix)->SetToIdentity(); - - // behind camera - vec_t camSpacePosition; - camSpacePosition.TransformPoint(makeVect(0.f, 0.f, 0.f), gContext.mMVP); - if (camSpacePosition.z < 0.001f) - return; - - // -- - int type = NONE; - if (gContext.mbEnable) - { - if (!gContext.mbUsingBounds) - { - switch (operation) - { - case ROTATE: - HandleRotation(matrix, deltaMatrix, type, snap); - break; - case TRANSLATE: - HandleTranslation(matrix, deltaMatrix, type, snap); - break; - case SCALE: - HandleScale(matrix, deltaMatrix, type, snap); - break; - } - } - } - - if (localBounds && !gContext.mbUsing) - HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap); - - if (!gContext.mbUsingBounds) - { - switch (operation) - { - case ROTATE: - DrawRotationGizmo(type); - break; - case TRANSLATE: - DrawTranslationGizmo(type); - break; - case SCALE: - DrawScaleGizmo(type); - break; - } - } - } - - void DrawCube(const float *view, const float *projection, float *matrix) - { - matrix_t viewInverse; - viewInverse.Inverse(*(matrix_t*)view); - const matrix_t& model = *(matrix_t*)matrix; - matrix_t res = *(matrix_t*)matrix * *(matrix_t*)view * *(matrix_t*)projection; - - for (int iFace = 0; iFace < 6; iFace++) - { - const int normalIndex = (iFace % 3); - const int perpXIndex = (normalIndex + 1) % 3; - const int perpYIndex = (normalIndex + 2) % 3; - const float invert = (iFace > 2) ? -1.f : 1.f; - - const vec_t faceCoords[4] = { directionUnary[normalIndex] + directionUnary[perpXIndex] + directionUnary[perpYIndex], - directionUnary[normalIndex] + directionUnary[perpXIndex] - directionUnary[perpYIndex], - directionUnary[normalIndex] - directionUnary[perpXIndex] - directionUnary[perpYIndex], - directionUnary[normalIndex] - directionUnary[perpXIndex] + directionUnary[perpYIndex], - }; - - // clipping - bool skipFace = false; - for (unsigned int iCoord = 0; iCoord < 4; iCoord++) - { - vec_t camSpacePosition; - camSpacePosition.TransformPoint(faceCoords[iCoord] * 0.5f * invert, gContext.mMVP); - if (camSpacePosition.z < 0.001f) - { - skipFace = true; - break; - } - } - if (skipFace) - continue; - - // 3D->2D - ImVec2 faceCoordsScreen[4]; - for (unsigned int iCoord = 0; iCoord < 4; iCoord++) - faceCoordsScreen[iCoord] = worldToPos(faceCoords[iCoord] * 0.5f * invert, res); - - // back face culling - vec_t cullPos, cullNormal; - cullPos.TransformPoint(faceCoords[0] * 0.5f * invert, model); - cullNormal.TransformVector(directionUnary[normalIndex] * invert, model); - float dt = Dot(Normalized(cullPos - viewInverse.v.position), Normalized(cullNormal)); - if (dt>0.f) - continue; - - // draw face with lighter color - gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, directionColor[normalIndex] | 0x808080); - } - } -}; - +// The MIT License(MIT) +// +// Copyright(c) 2016 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#define IMGUI_DEFINE_MATH_OPERATORS + +// includes patches for multiview from +// https://github.com/CedricGuillemet/ImGuizmo/issues/15 + +namespace ImGuizmo +{ + static const float ZPI = 3.14159265358979323846f; + static const float RAD2DEG = (180.f / ZPI); + static const float DEG2RAD = (ZPI / 180.f); + static const float gGizmoSizeClipSpace = 0.1f; + const float screenRotateSize = 0.06f; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // utility and math + + void FPU_MatrixF_x_MatrixF(const float *a, const float *b, float *r) + { + r[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12]; + r[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13]; + r[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14]; + r[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15]; + + r[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12]; + r[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13]; + r[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14]; + r[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15]; + + r[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12]; + r[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13]; + r[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14]; + r[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15]; + + r[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12]; + r[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13]; + r[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14]; + r[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15]; + } + + //template T LERP(T x, T y, float z) { return (x + (y - x)*z); } + template T Clamp(T x, T y, T z) { return ((xz) ? z : x)); } + template T max(T x, T y) { return (x > y) ? x : y; } + template T min(T x, T y) { return (x < y) ? x : y; } + template bool IsWithin(T x, T y, T z) { return (x>=y) && (x<=z); } + + struct matrix_t; + struct vec_t + { + public: + float x, y, z, w; + + void Lerp(const vec_t& v, float t) + { + x += (v.x - x) * t; + y += (v.y - y) * t; + z += (v.z - z) * t; + w += (v.w - w) * t; + } + + void Set(float v) { x = y = z = w = v; } + void Set(float _x, float _y, float _z = 0.f, float _w = 0.f) { x = _x; y = _y; z = _z; w = _w; } + + vec_t& operator -= (const vec_t& v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; } + vec_t& operator += (const vec_t& v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; } + vec_t& operator *= (const vec_t& v) { x *= v.x; y *= v.y; z *= v.z; w *= v.w; return *this; } + vec_t& operator *= (float v) { x *= v; y *= v; z *= v; w *= v; return *this; } + + vec_t operator * (float f) const; + vec_t operator - () const; + vec_t operator - (const vec_t& v) const; + vec_t operator + (const vec_t& v) const; + vec_t operator * (const vec_t& v) const; + + const vec_t& operator + () const { return (*this); } + float Length() const { return sqrtf(x*x + y*y + z*z); }; + float LengthSq() const { return (x*x + y*y + z*z); }; + vec_t Normalize() { (*this) *= (1.f / Length()); return (*this); } + vec_t Normalize(const vec_t& v) { this->Set(v.x, v.y, v.z, v.w); this->Normalize(); return (*this); } + vec_t Abs() const; + void Cross(const vec_t& v) + { + vec_t res; + res.x = y * v.z - z * v.y; + res.y = z * v.x - x * v.z; + res.z = x * v.y - y * v.x; + + x = res.x; + y = res.y; + z = res.z; + w = 0.f; + } + void Cross(const vec_t& v1, const vec_t& v2) + { + x = v1.y * v2.z - v1.z * v2.y; + y = v1.z * v2.x - v1.x * v2.z; + z = v1.x * v2.y - v1.y * v2.x; + w = 0.f; + } + float Dot(const vec_t &v) const + { + return (x * v.x) + (y * v.y) + (z * v.z) + (w * v.w); + } + float Dot3(const vec_t &v) const + { + return (x * v.x) + (y * v.y) + (z * v.z); + } + + void Transform(const matrix_t& matrix); + void Transform(const vec_t & s, const matrix_t& matrix); + + void TransformVector(const matrix_t& matrix); + void TransformPoint(const matrix_t& matrix); + void TransformVector(const vec_t& v, const matrix_t& matrix) { (*this) = v; this->TransformVector(matrix); } + void TransformPoint(const vec_t& v, const matrix_t& matrix) { (*this) = v; this->TransformPoint(matrix); } + + float& operator [] (size_t index) { return ((float*)&x)[index]; } + const float& operator [] (size_t index) const { return ((float*)&x)[index]; } + }; + + vec_t makeVect(float _x, float _y, float _z = 0.f, float _w = 0.f) { vec_t res; res.x = _x; res.y = _y; res.z = _z; res.w = _w; return res; } + vec_t makeVect(ImVec2 v) { vec_t res; res.x = v.x; res.y = v.y; res.z = 0.f; res.w = 0.f; return res; } + vec_t vec_t::operator * (float f) const { return makeVect(x * f, y * f, z * f, w *f); } + vec_t vec_t::operator - () const { return makeVect(-x, -y, -z, -w); } + vec_t vec_t::operator - (const vec_t& v) const { return makeVect(x - v.x, y - v.y, z - v.z, w - v.w); } + vec_t vec_t::operator + (const vec_t& v) const { return makeVect(x + v.x, y + v.y, z + v.z, w + v.w); } + vec_t vec_t::operator * (const vec_t& v) const { return makeVect(x * v.x, y * v.y, z * v.z, w * v.w); } + vec_t vec_t::Abs() const { return makeVect(fabsf(x), fabsf(y), fabsf(z)); } + + vec_t Normalized(const vec_t& v) { vec_t res; res = v; res.Normalize(); return res; } + vec_t Cross(const vec_t& v1, const vec_t& v2) + { + vec_t res; + res.x = v1.y * v2.z - v1.z * v2.y; + res.y = v1.z * v2.x - v1.x * v2.z; + res.z = v1.x * v2.y - v1.y * v2.x; + res.w = 0.f; + return res; + } + + float Dot(const vec_t &v1, const vec_t &v2) + { + return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z); + } + + vec_t BuildPlan(const vec_t & p_point1, const vec_t & p_normal) + { + vec_t normal, res; + normal.Normalize(p_normal); + res.w = normal.Dot(p_point1); + res.x = normal.x; + res.y = normal.y; + res.z = normal.z; + return res; + } + + struct matrix_t + { + public: + + union + { + float m[4][4]; + float m16[16]; + struct + { + vec_t right, up, dir, position; + } v; + vec_t component[4]; + }; + + matrix_t(const matrix_t& other) { memcpy(&m16[0], &other.m16[0], sizeof(float) * 16); } + matrix_t() {} + + operator float * () { return m16; } + operator const float* () const { return m16; } + void Translation(float _x, float _y, float _z) { this->Translation(makeVect(_x, _y, _z)); } + + void Translation(const vec_t& vt) + { + v.right.Set(1.f, 0.f, 0.f, 0.f); + v.up.Set(0.f, 1.f, 0.f, 0.f); + v.dir.Set(0.f, 0.f, 1.f, 0.f); + v.position.Set(vt.x, vt.y, vt.z, 1.f); + } + + void Scale(float _x, float _y, float _z) + { + v.right.Set(_x, 0.f, 0.f, 0.f); + v.up.Set(0.f, _y, 0.f, 0.f); + v.dir.Set(0.f, 0.f, _z, 0.f); + v.position.Set(0.f, 0.f, 0.f, 1.f); + } + void Scale(const vec_t& s) { Scale(s.x, s.y, s.z); } + + matrix_t& operator *= (const matrix_t& mat) + { + matrix_t tmpMat; + tmpMat = *this; + tmpMat.Multiply(mat); + *this = tmpMat; + return *this; + } + matrix_t operator * (const matrix_t& mat) const + { + matrix_t matT; + matT.Multiply(*this, mat); + return matT; + } + + void Multiply(const matrix_t &matrix) + { + matrix_t tmp; + tmp = *this; + + FPU_MatrixF_x_MatrixF((float*)&tmp, (float*)&matrix, (float*)this); + } + + void Multiply(const matrix_t &m1, const matrix_t &m2) + { + FPU_MatrixF_x_MatrixF((float*)&m1, (float*)&m2, (float*)this); + } + + float GetDeterminant() const + { + return m[0][0] * m[1][1] * m[2][2] + m[0][1] * m[1][2] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - + m[0][2] * m[1][1] * m[2][0] - m[0][1] * m[1][0] * m[2][2] - m[0][0] * m[1][2] * m[2][1]; + } + + float Inverse(const matrix_t &srcMatrix, bool affine = false); + void SetToIdentity() + { + v.right.Set(1.f, 0.f, 0.f, 0.f); + v.up.Set(0.f, 1.f, 0.f, 0.f); + v.dir.Set(0.f, 0.f, 1.f, 0.f); + v.position.Set(0.f, 0.f, 0.f, 1.f); + } + void Transpose() + { + matrix_t tmpm; + for (int l = 0; l < 4; l++) + { + for (int c = 0; c < 4; c++) + { + tmpm.m[l][c] = m[c][l]; + } + } + (*this) = tmpm; + } + + void RotationAxis(const vec_t & axis, float angle); + + void OrthoNormalize() + { + v.right.Normalize(); + v.up.Normalize(); + v.dir.Normalize(); + } + }; + + void vec_t::Transform(const matrix_t& matrix) + { + vec_t out; + + out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + w * matrix.m[3][0]; + out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + w * matrix.m[3][1]; + out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + w * matrix.m[3][2]; + out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + w * matrix.m[3][3]; + + x = out.x; + y = out.y; + z = out.z; + w = out.w; + } + + void vec_t::Transform(const vec_t & s, const matrix_t& matrix) + { + *this = s; + Transform(matrix); + } + + void vec_t::TransformPoint(const matrix_t& matrix) + { + vec_t out; + + out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + matrix.m[3][0]; + out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + matrix.m[3][1]; + out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + matrix.m[3][2]; + out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + matrix.m[3][3]; + + x = out.x; + y = out.y; + z = out.z; + w = out.w; + } + + + void vec_t::TransformVector(const matrix_t& matrix) + { + vec_t out; + + out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0]; + out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1]; + out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2]; + out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3]; + + x = out.x; + y = out.y; + z = out.z; + w = out.w; + } + + float matrix_t::Inverse(const matrix_t &srcMatrix, bool affine) + { + float det = 0; + + if (affine) + { + det = GetDeterminant(); + float s = 1 / det; + m[0][0] = (srcMatrix.m[1][1] * srcMatrix.m[2][2] - srcMatrix.m[1][2] * srcMatrix.m[2][1]) * s; + m[0][1] = (srcMatrix.m[2][1] * srcMatrix.m[0][2] - srcMatrix.m[2][2] * srcMatrix.m[0][1]) * s; + m[0][2] = (srcMatrix.m[0][1] * srcMatrix.m[1][2] - srcMatrix.m[0][2] * srcMatrix.m[1][1]) * s; + m[1][0] = (srcMatrix.m[1][2] * srcMatrix.m[2][0] - srcMatrix.m[1][0] * srcMatrix.m[2][2]) * s; + m[1][1] = (srcMatrix.m[2][2] * srcMatrix.m[0][0] - srcMatrix.m[2][0] * srcMatrix.m[0][2]) * s; + m[1][2] = (srcMatrix.m[0][2] * srcMatrix.m[1][0] - srcMatrix.m[0][0] * srcMatrix.m[1][2]) * s; + m[2][0] = (srcMatrix.m[1][0] * srcMatrix.m[2][1] - srcMatrix.m[1][1] * srcMatrix.m[2][0]) * s; + m[2][1] = (srcMatrix.m[2][0] * srcMatrix.m[0][1] - srcMatrix.m[2][1] * srcMatrix.m[0][0]) * s; + m[2][2] = (srcMatrix.m[0][0] * srcMatrix.m[1][1] - srcMatrix.m[0][1] * srcMatrix.m[1][0]) * s; + m[3][0] = -(m[0][0] * srcMatrix.m[3][0] + m[1][0] * srcMatrix.m[3][1] + m[2][0] * srcMatrix.m[3][2]); + m[3][1] = -(m[0][1] * srcMatrix.m[3][0] + m[1][1] * srcMatrix.m[3][1] + m[2][1] * srcMatrix.m[3][2]); + m[3][2] = -(m[0][2] * srcMatrix.m[3][0] + m[1][2] * srcMatrix.m[3][1] + m[2][2] * srcMatrix.m[3][2]); + } + else + { + // transpose matrix + float src[16]; + for (int i = 0; i < 4; ++i) + { + src[i] = srcMatrix.m16[i * 4]; + src[i + 4] = srcMatrix.m16[i * 4 + 1]; + src[i + 8] = srcMatrix.m16[i * 4 + 2]; + src[i + 12] = srcMatrix.m16[i * 4 + 3]; + } + + // calculate pairs for first 8 elements (cofactors) + float tmp[12]; // temp array for pairs + tmp[0] = src[10] * src[15]; + tmp[1] = src[11] * src[14]; + tmp[2] = src[9] * src[15]; + tmp[3] = src[11] * src[13]; + tmp[4] = src[9] * src[14]; + tmp[5] = src[10] * src[13]; + tmp[6] = src[8] * src[15]; + tmp[7] = src[11] * src[12]; + tmp[8] = src[8] * src[14]; + tmp[9] = src[10] * src[12]; + tmp[10] = src[8] * src[13]; + tmp[11] = src[9] * src[12]; + + // calculate first 8 elements (cofactors) + m16[0] = (tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7]) - (tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7]); + m16[1] = (tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7]) - (tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7]); + m16[2] = (tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7]) - (tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7]); + m16[3] = (tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6]) - (tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6]); + m16[4] = (tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3]) - (tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3]); + m16[5] = (tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3]) - (tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3]); + m16[6] = (tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3]) - (tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3]); + m16[7] = (tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2]) - (tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2]); + + // calculate pairs for second 8 elements (cofactors) + tmp[0] = src[2] * src[7]; + tmp[1] = src[3] * src[6]; + tmp[2] = src[1] * src[7]; + tmp[3] = src[3] * src[5]; + tmp[4] = src[1] * src[6]; + tmp[5] = src[2] * src[5]; + tmp[6] = src[0] * src[7]; + tmp[7] = src[3] * src[4]; + tmp[8] = src[0] * src[6]; + tmp[9] = src[2] * src[4]; + tmp[10] = src[0] * src[5]; + tmp[11] = src[1] * src[4]; + + // calculate second 8 elements (cofactors) + m16[8] = (tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15]) - (tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15]); + m16[9] = (tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15]) - (tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15]); + m16[10] = (tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15]) - (tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15]); + m16[11] = (tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14]) - (tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14]); + m16[12] = (tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9]) - (tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10]); + m16[13] = (tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10]) - (tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8]); + m16[14] = (tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8]) - (tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9]); + m16[15] = (tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9]) - (tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8]); + + // calculate determinant + det = src[0] * m16[0] + src[1] * m16[1] + src[2] * m16[2] + src[3] * m16[3]; + + // calculate matrix inverse + float invdet = 1 / det; + for (int j = 0; j < 16; ++j) + { + m16[j] *= invdet; + } + } + + return det; + } + + void matrix_t::RotationAxis(const vec_t & axis, float angle) + { + float length2 = axis.LengthSq(); + if (length2 < FLT_EPSILON) + { + SetToIdentity(); + return; + } + + vec_t n = axis * (1.f / sqrtf(length2)); + float s = sinf(angle); + float c = cosf(angle); + float k = 1.f - c; + + float xx = n.x * n.x * k + c; + float yy = n.y * n.y * k + c; + float zz = n.z * n.z * k + c; + float xy = n.x * n.y * k; + float yz = n.y * n.z * k; + float zx = n.z * n.x * k; + float xs = n.x * s; + float ys = n.y * s; + float zs = n.z * s; + + m[0][0] = xx; + m[0][1] = xy + zs; + m[0][2] = zx - ys; + m[0][3] = 0.f; + m[1][0] = xy - zs; + m[1][1] = yy; + m[1][2] = yz + xs; + m[1][3] = 0.f; + m[2][0] = zx + ys; + m[2][1] = yz - xs; + m[2][2] = zz; + m[2][3] = 0.f; + m[3][0] = 0.f; + m[3][1] = 0.f; + m[3][2] = 0.f; + m[3][3] = 1.f; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + + enum MOVETYPE + { + NONE, + MOVE_X, + MOVE_Y, + MOVE_Z, + MOVE_YZ, + MOVE_ZX, + MOVE_XY, + MOVE_SCREEN, + ROTATE_X, + ROTATE_Y, + ROTATE_Z, + ROTATE_SCREEN, + SCALE_X, + SCALE_Y, + SCALE_Z, + SCALE_XYZ + }; + + struct Context + { + Context() : mbUsing(false), mbEnable(true), mbUsingBounds(false) + { + } + + ImDrawList* mDrawList; + + MODE mMode; + matrix_t mViewMat; + matrix_t mProjectionMat; + matrix_t mModel; + matrix_t mModelInverse; + matrix_t mModelSource; + matrix_t mModelSourceInverse; + matrix_t mMVP; + matrix_t mViewProjection; + + vec_t mModelScaleOrigin; + vec_t mCameraEye; + vec_t mCameraRight; + vec_t mCameraDir; + vec_t mCameraUp; + vec_t mRayOrigin; + vec_t mRayVector; + + float mRadiusSquareCenter; + ImVec2 mScreenSquareCenter; + ImVec2 mScreenSquareMin; + ImVec2 mScreenSquareMax; + + float mScreenFactor; + vec_t mRelativeOrigin; + + bool mbUsing; + bool mbEnable; + + // translation + vec_t mTranslationPlan; + vec_t mTranslationPlanOrigin; + vec_t mMatrixOrigin; + + // rotation + vec_t mRotationVectorSource; + float mRotationAngle; + float mRotationAngleOrigin; + //vec_t mWorldToLocalAxis; + + // scale + vec_t mScale; + vec_t mScaleValueOrigin; + float mSaveMousePosx; + + // save axis factor when using gizmo + bool mBelowAxisLimit[3]; + bool mBelowPlaneLimit[3]; + float mAxisFactor[3]; + + // bounds stretching + vec_t mBoundsPivot; + vec_t mBoundsAnchor; + vec_t mBoundsPlan; + vec_t mBoundsLocalPivot; + int mBoundsBestAxis; + int mBoundsAxis[2]; + bool mbUsingBounds; + matrix_t mBoundsMatrix; + + // + int mCurrentOperation; + + float mX = 0.f; + float mY = 0.f; + float mWidth = 0.f; + float mHeight = 0.f; + float mXMax = 0.f; + float mYMax = 0.f; + float mDisplayRatio = 1.f; + + bool mIsOrthographic = false; + }; + + static Context gContext; + + static const float angleLimit = 0.96f; + static const float planeLimit = 0.2f; + + static const vec_t directionUnary[3] = { makeVect(1.f, 0.f, 0.f), makeVect(0.f, 1.f, 0.f), makeVect(0.f, 0.f, 1.f) }; + static const ImU32 directionColor[3] = { 0xFF0000AA, 0xFF00AA00, 0xFFAA0000 }; + + // Alpha: 100%: FF, 87%: DE, 70%: B3, 54%: 8A, 50%: 80, 38%: 61, 12%: 1F + static const ImU32 planeColor[3] = { 0x610000AA, 0x6100AA00, 0x61AA0000 }; + static const ImU32 selectionColor = 0x8A1080FF; + static const ImU32 inactiveColor = 0x99999999; + static const ImU32 translationLineColor = 0xAAAAAAAA; + static const char *translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f", + "Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f", + "X : %5.3f Y : %5.3f Z : %5.3f" }; + static const char *scaleInfoMask[] = { "X : %5.2f", "Y : %5.2f", "Z : %5.2f", "XYZ : %5.2f" }; + static const char *rotationInfoMask[] = { "X : %5.2f deg %5.2f rad", "Y : %5.2f deg %5.2f rad", "Z : %5.2f deg %5.2f rad", "Screen : %5.2f deg %5.2f rad" }; + static const int translationInfoIndex[] = { 0,0,0, 1,0,0, 2,0,0, 1,2,0, 0,2,0, 0,1,0, 0,1,2 }; + static const float quadMin = 0.5f; + static const float quadMax = 0.8f; + static const float quadUV[8] = { quadMin, quadMin, quadMin, quadMax, quadMax, quadMax, quadMax, quadMin }; + static const int halfCircleSegmentCount = 64; + static const float snapTension = 0.5f; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + static int GetMoveType(vec_t *gizmoHitProportion); + static int GetRotateType(); + static int GetScaleType(); + + static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat) + { + vec_t trans; + trans.TransformPoint(worldPos, mat); + trans *= 0.5f / trans.w; + trans += makeVect(0.5f, 0.5f); + trans.y = 1.f - trans.y; + trans.x *= gContext.mWidth; + trans.y *= gContext.mHeight; + trans.x += gContext.mX; + trans.y += gContext.mY; + return ImVec2(trans.x, trans.y); + } + + static void ComputeCameraRay(vec_t &rayOrigin, vec_t &rayDir) + { + ImGuiIO& io = ImGui::GetIO(); + + matrix_t mViewProjInverse; + mViewProjInverse.Inverse(gContext.mViewMat * gContext.mProjectionMat); + + float mox = ((io.MousePos.x - gContext.mX) / gContext.mWidth) * 2.f - 1.f; + float moy = (1.f - ((io.MousePos.y - gContext.mY) / gContext.mHeight)) * 2.f - 1.f; + + rayOrigin.Transform(makeVect(mox, moy, 0.f, 1.f), mViewProjInverse); + rayOrigin *= 1.f / rayOrigin.w; + vec_t rayEnd; + rayEnd.Transform(makeVect(mox, moy, 1.f, 1.f), mViewProjInverse); + rayEnd *= 1.f / rayEnd.w; + rayDir = Normalized(rayEnd - rayOrigin); + } + + static float GetSegmentLengthClipSpace(const vec_t& start, const vec_t& end) + { + vec_t startOfSegment = start; + startOfSegment.TransformPoint(gContext.mMVP); + if (fabsf(startOfSegment.w)> FLT_EPSILON) // check for axis aligned with camera direction + startOfSegment *= 1.f / startOfSegment.w; + + vec_t endOfSegment = end; + endOfSegment.TransformPoint(gContext.mMVP); + if (fabsf(endOfSegment.w)> FLT_EPSILON) // check for axis aligned with camera direction + endOfSegment *= 1.f / endOfSegment.w; + + vec_t clipSpaceAxis = endOfSegment - startOfSegment; + clipSpaceAxis.y /= gContext.mDisplayRatio; + float segmentLengthInClipSpace = sqrtf(clipSpaceAxis.x*clipSpaceAxis.x + clipSpaceAxis.y*clipSpaceAxis.y); + return segmentLengthInClipSpace; + } + + static float GetParallelogram(const vec_t& ptO, const vec_t& ptA, const vec_t& ptB) + { + vec_t pts[] = { ptO, ptA, ptB }; + for (unsigned int i = 0; i < 3; i++) + { + pts[i].TransformPoint(gContext.mMVP); + if (fabsf(pts[i].w)> FLT_EPSILON) // check for axis aligned with camera direction + pts[i] *= 1.f / pts[i].w; + } + vec_t segA = pts[1] - pts[0]; + vec_t segB = pts[2] - pts[0]; + segA.y /= gContext.mDisplayRatio; + segB.y /= gContext.mDisplayRatio; + vec_t segAOrtho = makeVect(-segA.y, segA.x); + segAOrtho.Normalize(); + float dt = segAOrtho.Dot3(segB); + float surface = sqrtf(segA.x*segA.x + segA.y*segA.y) * fabsf(dt); + return surface; + } + + inline vec_t PointOnSegment(const vec_t & point, const vec_t & vertPos1, const vec_t & vertPos2) + { + vec_t c = point - vertPos1; + vec_t V; + + V.Normalize(vertPos2 - vertPos1); + float d = (vertPos2 - vertPos1).Length(); + float t = V.Dot3(c); + + if (t < 0.f) + return vertPos1; + + if (t > d) + return vertPos2; + + return vertPos1 + V * t; + } + + static float IntersectRayPlane(const vec_t & rOrigin, const vec_t& rVector, const vec_t& plan) + { + float numer = plan.Dot3(rOrigin) - plan.w; + float denom = plan.Dot3(rVector); + + if (fabsf(denom) < FLT_EPSILON) // normal is orthogonal to vector, cant intersect + return -1.0f; + + return -(numer / denom); + } + + static bool IsInContextRect( ImVec2 p ) + { + return IsWithin( p.x, gContext.mX, gContext.mXMax ) && IsWithin(p.y, gContext.mY, gContext.mYMax ); + } + + void SetRect(float x, float y, float width, float height) + { + gContext.mX = x; + gContext.mY = y; + gContext.mWidth = width; + gContext.mHeight = height; + gContext.mXMax = gContext.mX + gContext.mWidth; + gContext.mYMax = gContext.mY + gContext.mXMax; + gContext.mDisplayRatio = width / height; + } + + IMGUI_API void SetOrthographic(bool isOrthographic) + { + gContext.mIsOrthographic = isOrthographic; + } + + void SetDrawlist() + { + gContext.mDrawList = ImGui::GetWindowDrawList(); + } + + void BeginFrame() + { + ImGuiIO& io = ImGui::GetIO(); + + const ImU32 flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus; + ImGui::SetNextWindowSize(io.DisplaySize); + + ImGui::PushStyleColor(ImGuiCol_WindowBg, 0); + ImGui::Begin("gizmo", NULL, flags); + gContext.mDrawList = ImGui::GetWindowDrawList(); + ImGui::End(); + ImGui::PopStyleColor(); + } + + bool IsUsing() + { + return gContext.mbUsing||gContext.mbUsingBounds; + } + + bool IsOver() + { + return (GetMoveType(NULL) != NONE) || GetRotateType() != NONE || GetScaleType() != NONE || IsUsing(); + } + + void Enable(bool enable) + { + gContext.mbEnable = enable; + if (!enable) + { + gContext.mbUsing = false; + gContext.mbUsingBounds = false; + } + } + + static float GetUniform(const vec_t& position, const matrix_t& mat) + { + vec_t trf = makeVect(position.x, position.y, position.z, 1.f); + trf.Transform(mat); + return trf.w; + } + + static void ComputeContext(const float *view, const float *projection, float *matrix, MODE mode) + { + gContext.mMode = mode; + gContext.mViewMat = *(matrix_t*)view; + gContext.mProjectionMat = *(matrix_t*)projection; + + if (mode == LOCAL) + { + gContext.mModel = *(matrix_t*)matrix; + gContext.mModel.OrthoNormalize(); + } + else + { + gContext.mModel.Translation(((matrix_t*)matrix)->v.position); + } + gContext.mModelSource = *(matrix_t*)matrix; + gContext.mModelScaleOrigin.Set(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length()); + + gContext.mModelInverse.Inverse(gContext.mModel); + gContext.mModelSourceInverse.Inverse(gContext.mModelSource); + gContext.mViewProjection = gContext.mViewMat * gContext.mProjectionMat; + gContext.mMVP = gContext.mModel * gContext.mViewProjection; + + matrix_t viewInverse; + viewInverse.Inverse(gContext.mViewMat); + gContext.mCameraDir = viewInverse.v.dir; + gContext.mCameraEye = viewInverse.v.position; + gContext.mCameraRight = viewInverse.v.right; + gContext.mCameraUp = viewInverse.v.up; + + // compute scale from the size of camera right vector projected on screen at the matrix position + vec_t pointRight = viewInverse.v.right; + pointRight.TransformPoint(gContext.mViewProjection); + gContext.mScreenFactor = gGizmoSizeClipSpace / (pointRight.x / pointRight.w - gContext.mMVP.v.position.x / gContext.mMVP.v.position.w); + + vec_t rightViewInverse = viewInverse.v.right; + rightViewInverse.TransformVector(gContext.mModelInverse); + float rightLength = GetSegmentLengthClipSpace(makeVect(0.f, 0.f), rightViewInverse); + gContext.mScreenFactor = gGizmoSizeClipSpace / rightLength; + + ImVec2 centerSSpace = worldToPos(makeVect(0.f, 0.f), gContext.mMVP); + gContext.mScreenSquareCenter = centerSSpace; + gContext.mScreenSquareMin = ImVec2(centerSSpace.x - 10.f, centerSSpace.y - 10.f); + gContext.mScreenSquareMax = ImVec2(centerSSpace.x + 10.f, centerSSpace.y + 10.f); + + ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector); + } + + static void ComputeColors(ImU32 *colors, int type, OPERATION operation) + { + if (gContext.mbEnable) + { + switch (operation) + { + case TRANSLATE: + colors[0] = (type == MOVE_SCREEN) ? selectionColor : 0xFFFFFFFF; + for (int i = 0; i < 3; i++) + { + colors[i + 1] = (type == (int)(MOVE_X + i)) ? selectionColor : directionColor[i]; + colors[i + 4] = (type == (int)(MOVE_YZ + i)) ? selectionColor : planeColor[i]; + colors[i + 4] = (type == MOVE_SCREEN) ? selectionColor : colors[i + 4]; + } + break; + case ROTATE: + colors[0] = (type == ROTATE_SCREEN) ? selectionColor : 0xFFFFFFFF; + for (int i = 0; i < 3; i++) + colors[i + 1] = (type == (int)(ROTATE_X + i)) ? selectionColor : directionColor[i]; + break; + case SCALE: + colors[0] = (type == SCALE_XYZ) ? selectionColor : 0xFFFFFFFF; + for (int i = 0; i < 3; i++) + colors[i + 1] = (type == (int)(SCALE_X + i)) ? selectionColor : directionColor[i]; + break; + case BOUNDS: + break; + } + } + else + { + for (int i = 0; i < 7; i++) + colors[i] = inactiveColor; + } + } + + static void ComputeTripodAxisAndVisibility(int axisIndex, vec_t& dirAxis, vec_t& dirPlaneX, vec_t& dirPlaneY, bool& belowAxisLimit, bool& belowPlaneLimit) + { + dirAxis = directionUnary[axisIndex]; + dirPlaneX = directionUnary[(axisIndex + 1) % 3]; + dirPlaneY = directionUnary[(axisIndex + 2) % 3]; + + if (gContext.mbUsing) + { + // when using, use stored factors so the gizmo doesn't flip when we translate + belowAxisLimit = gContext.mBelowAxisLimit[axisIndex]; + belowPlaneLimit = gContext.mBelowPlaneLimit[axisIndex]; + + dirPlaneX *= gContext.mAxisFactor[axisIndex]; + dirPlaneY *= gContext.mAxisFactor[(axisIndex + 1) % 3]; + } + else + { + // new method + float lenDir = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis); + float lenDirMinus = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirAxis); + + float lenDirPlaneX = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirPlaneX); + float lenDirMinusPlaneX = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirPlaneX); + + float lenDirPlaneY = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirPlaneY); + float lenDirMinusPlaneY = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirPlaneY); + + float mulAxis = (lenDir < lenDirMinus && fabsf(lenDir - lenDirMinus) > FLT_EPSILON) ? -1.f : 1.f; + float mulAxisX = (lenDirPlaneX < lenDirMinusPlaneX && fabsf(lenDirPlaneX - lenDirMinusPlaneX) > FLT_EPSILON) ? -1.f : 1.f; + float mulAxisY = (lenDirPlaneY < lenDirMinusPlaneY && fabsf(lenDirPlaneY - lenDirMinusPlaneY) > FLT_EPSILON) ? -1.f : 1.f; + dirAxis *= mulAxis; + dirPlaneX *= mulAxisX; + dirPlaneY *= mulAxisY; + + // for axis + float axisLengthInClipSpace = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis * gContext.mScreenFactor); + + float paraSurf = GetParallelogram(makeVect(0.f, 0.f, 0.f), dirPlaneX * gContext.mScreenFactor, dirPlaneY * gContext.mScreenFactor); + belowPlaneLimit = (paraSurf > 0.0025f); + belowAxisLimit = (axisLengthInClipSpace > 0.02f); + + // and store values + gContext.mAxisFactor[axisIndex] = mulAxis; + gContext.mAxisFactor[(axisIndex + 1) % 3] = mulAxisY; + gContext.mAxisFactor[(axisIndex + 2) % 3] = mulAxisY; + gContext.mBelowAxisLimit[axisIndex] = belowAxisLimit; + gContext.mBelowPlaneLimit[axisIndex] = belowPlaneLimit; + } + } + + static void ComputeSnap(float*value, float snap) + { + if (snap <= FLT_EPSILON) + return; + float modulo = fmodf(*value, snap); + float moduloRatio = fabsf(modulo) / snap; + if (moduloRatio < snapTension) + *value -= modulo; + else if (moduloRatio >(1.f - snapTension)) + *value = *value - modulo + snap * ((*value<0.f) ? -1.f : 1.f); + } + static void ComputeSnap(vec_t& value, float *snap) + { + for (int i = 0; i < 3; i++) + { + ComputeSnap(&value[i], snap[i]); + } + } + + static float ComputeAngleOnPlan() + { + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); + vec_t localPos = Normalized(gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position); + + vec_t perpendicularVector; + perpendicularVector.Cross(gContext.mRotationVectorSource, gContext.mTranslationPlan); + perpendicularVector.Normalize(); + float acosAngle = Clamp(Dot(localPos, gContext.mRotationVectorSource), -0.9999f, 0.9999f); + float angle = acosf(acosAngle); + angle *= (Dot(localPos, perpendicularVector) < 0.f) ? 1.f : -1.f; + return angle; + } + + static void DrawRotationGizmo(int type) + { + ImDrawList* drawList = gContext.mDrawList; + + // colors + ImU32 colors[7]; + ComputeColors(colors, type, ROTATE); + + vec_t cameraToModelNormalized; + if (gContext.mIsOrthographic) + { + matrix_t viewInverse; + viewInverse.Inverse(*(matrix_t*)&gContext.mViewMat); + cameraToModelNormalized = viewInverse.v.dir; + } + else + { + cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye); + } + + cameraToModelNormalized.TransformVector(gContext.mModelInverse); + + gContext.mRadiusSquareCenter = screenRotateSize * gContext.mHeight; + + for (int axis = 0; axis < 3; axis++) + { + ImVec2 circlePos[halfCircleSegmentCount]; + + float angleStart = atan2f(cameraToModelNormalized[(4-axis)%3], cameraToModelNormalized[(3 - axis) % 3]) + ZPI * 0.5f; + + for (unsigned int i = 0; i < halfCircleSegmentCount; i++) + { + float ng = angleStart + ZPI * ((float)i / (float)halfCircleSegmentCount); + vec_t axisPos = makeVect(cosf(ng), sinf(ng), 0.f); + vec_t pos = makeVect(axisPos[axis], axisPos[(axis+1)%3], axisPos[(axis+2)%3]) * gContext.mScreenFactor; + circlePos[i] = worldToPos(pos, gContext.mMVP); + } + + float radiusAxis = sqrtf( (ImLengthSqr(worldToPos(gContext.mModel.v.position, gContext.mViewProjection) - circlePos[0]) )); + if(radiusAxis > gContext.mRadiusSquareCenter) + gContext.mRadiusSquareCenter = radiusAxis; + + drawList->AddPolyline(circlePos, halfCircleSegmentCount, colors[3 - axis], false, 2); + } + drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), gContext.mRadiusSquareCenter, colors[0], 64, 3.f); + + if (gContext.mbUsing) + { + ImVec2 circlePos[halfCircleSegmentCount +1]; + + circlePos[0] = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); + for (unsigned int i = 1; i < halfCircleSegmentCount; i++) + { + float ng = gContext.mRotationAngle * ((float)(i-1) / (float)(halfCircleSegmentCount -1)); + matrix_t rotateVectorMatrix; + rotateVectorMatrix.RotationAxis(gContext.mTranslationPlan, ng); + vec_t pos; + pos.TransformPoint(gContext.mRotationVectorSource, rotateVectorMatrix); + pos *= gContext.mScreenFactor; + circlePos[i] = worldToPos(pos + gContext.mModel.v.position, gContext.mViewProjection); + } + drawList->AddConvexPolyFilled(circlePos, halfCircleSegmentCount, 0x801080FF); + drawList->AddPolyline(circlePos, halfCircleSegmentCount, 0xFF1080FF, true, 2); + + ImVec2 destinationPosOnScreen = circlePos[1]; + char tmps[512]; + ImFormatString(tmps, sizeof(tmps), rotationInfoMask[type - ROTATE_X], (gContext.mRotationAngle/ZPI)*180.f, gContext.mRotationAngle); + drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps); + drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps); + } + } + + static void DrawHatchedAxis(const vec_t& axis) + { + for (int j = 1; j < 10; j++) + { + ImVec2 baseSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2) * gContext.mScreenFactor, gContext.mMVP); + ImVec2 worldDirSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2 + 1) * gContext.mScreenFactor, gContext.mMVP); + gContext.mDrawList->AddLine(baseSSpace2, worldDirSSpace2, 0x80000000, 6.f); + } + } + + static void DrawScaleGizmo(int type) + { + ImDrawList* drawList = gContext.mDrawList; + + // colors + ImU32 colors[7]; + ComputeColors(colors, type, SCALE); + + // draw + vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f }; + + if (gContext.mbUsing) + scaleDisplay = gContext.mScale; + + for (unsigned int i = 0; i < 3; i++) + { + vec_t dirPlaneX, dirPlaneY, dirAxis; + bool belowAxisLimit, belowPlaneLimit; + ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); + + // draw axis + if (belowAxisLimit) + { + ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP); + ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * gContext.mScreenFactor, gContext.mMVP); + ImVec2 worldDirSSpace = worldToPos((dirAxis * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVP); + + if (gContext.mbUsing) + { + drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, 0xFF404040, 3.f); + drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, 0xFF404040); + } + + drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f); + drawList->AddCircleFilled(worldDirSSpace, 6.f, colors[i + 1]); + + if (gContext.mAxisFactor[i] < 0.f) + DrawHatchedAxis(dirAxis * scaleDisplay[i]); + } + } + + // draw screen cirle + drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32); + + if (gContext.mbUsing) + { + //ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); + ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); + /*vec_t dif(destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y); + dif.Normalize(); + dif *= 5.f; + drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor); + drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor); + drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f); + */ + char tmps[512]; + //vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin; + int componentInfoIndex = (type - SCALE_X) * 3; + ImFormatString(tmps, sizeof(tmps), scaleInfoMask[type - SCALE_X], scaleDisplay[translationInfoIndex[componentInfoIndex]]); + drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps); + drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps); + } + } + + + static void DrawTranslationGizmo(int type) + { + ImDrawList* drawList = gContext.mDrawList; + if (!drawList) + return; + + // colors + ImU32 colors[7]; + ComputeColors(colors, type, TRANSLATE); + + const ImVec2 origin = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); + + // draw + bool belowAxisLimit = false; + bool belowPlaneLimit = false; + for (unsigned int i = 0; i < 3; ++i) + { + vec_t dirPlaneX, dirPlaneY, dirAxis; + ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); + + // draw axis + if (belowAxisLimit) + { + ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP); + ImVec2 worldDirSSpace = worldToPos(dirAxis * gContext.mScreenFactor, gContext.mMVP); + + drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f); + + // Arrow head begin + ImVec2 dir(origin - worldDirSSpace); + + float d = sqrtf(ImLengthSqr(dir)); + dir /= d; // Normalize + dir *= 6.0f; + + ImVec2 ortogonalDir(dir.y, -dir.x); // Perpendicular vector + ImVec2 a(worldDirSSpace + dir); + drawList->AddTriangleFilled(worldDirSSpace - dir, a + ortogonalDir, a - ortogonalDir, colors[i + 1]); + // Arrow head end + + if (gContext.mAxisFactor[i] < 0.f) + DrawHatchedAxis(dirAxis); + } + + // draw plane + if (belowPlaneLimit) + { + ImVec2 screenQuadPts[4]; + for (int j = 0; j < 4; ++j) + { + vec_t cornerWorldPos = (dirPlaneX * quadUV[j * 2] + dirPlaneY * quadUV[j * 2 + 1]) * gContext.mScreenFactor; + screenQuadPts[j] = worldToPos(cornerWorldPos, gContext.mMVP); + } + drawList->AddPolyline(screenQuadPts, 4, directionColor[i], true, 1.0f); + drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4]); + } + } + + drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32); + + if (gContext.mbUsing) + { + ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); + ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); + vec_t dif = { destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y, 0.f, 0.f }; + dif.Normalize(); + dif *= 5.f; + drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor); + drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor); + drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f); + + char tmps[512]; + vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin; + int componentInfoIndex = (type - MOVE_X) * 3; + ImFormatString(tmps, sizeof(tmps), translationInfoMask[type - MOVE_X], deltaInfo[translationInfoIndex[componentInfoIndex]], deltaInfo[translationInfoIndex[componentInfoIndex + 1]], deltaInfo[translationInfoIndex[componentInfoIndex + 2]]); + drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps); + drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps); + } + } + + static bool CanActivate() + { + if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && !ImGui::IsAnyItemActive()) + return true; + return false; + } + + static void HandleAndDrawLocalBounds(float *bounds, matrix_t *matrix, float *snapValues, OPERATION operation) + { + ImGuiIO& io = ImGui::GetIO(); + ImDrawList* drawList = gContext.mDrawList; + + // compute best projection axis + vec_t axesWorldDirections[3]; + vec_t bestAxisWorldDirection = { 0.0f, 0.0f, 0.0f, 0.0f }; + int axes[3]; + unsigned int numAxes = 1; + axes[0] = gContext.mBoundsBestAxis; + int bestAxis = axes[0]; + if (!gContext.mbUsingBounds) + { + numAxes = 0; + float bestDot = 0.f; + for (unsigned int i = 0; i < 3; i++) + { + vec_t dirPlaneNormalWorld; + dirPlaneNormalWorld.TransformVector(directionUnary[i], gContext.mModelSource); + dirPlaneNormalWorld.Normalize(); + + float dt = fabsf( Dot(Normalized(gContext.mCameraEye - gContext.mModelSource.v.position), dirPlaneNormalWorld) ); + if ( dt >= bestDot ) + { + bestDot = dt; + bestAxis = i; + bestAxisWorldDirection = dirPlaneNormalWorld; + } + + if( dt >= 0.1f ) + { + axes[numAxes] = i; + axesWorldDirections[numAxes] = dirPlaneNormalWorld; + ++numAxes; + } + } + } + + if( numAxes == 0 ) + { + axes[0] = bestAxis; + axesWorldDirections[0] = bestAxisWorldDirection; + numAxes = 1; + } + else if( bestAxis != axes[0] ) + { + unsigned int bestIndex = 0; + for (unsigned int i = 0; i < numAxes; i++) + { + if( axes[i] == bestAxis ) + { + bestIndex = i; + break; + } + } + int tempAxis = axes[0]; + axes[0] = axes[bestIndex]; + axes[bestIndex] = tempAxis; + vec_t tempDirection = axesWorldDirections[0]; + axesWorldDirections[0] = axesWorldDirections[bestIndex]; + axesWorldDirections[bestIndex] = tempDirection; + } + + for (unsigned int axisIndex = 0; axisIndex < numAxes; ++axisIndex) + { + bestAxis = axes[axisIndex]; + bestAxisWorldDirection = axesWorldDirections[axisIndex]; + + // corners + vec_t aabb[4]; + + int secondAxis = (bestAxis + 1) % 3; + int thirdAxis = (bestAxis + 2) % 3; + + for (int i = 0; i < 4; i++) + { + aabb[i][3] = aabb[i][bestAxis] = 0.f; + aabb[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)]; + aabb[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))]; + } + + // draw bounds + unsigned int anchorAlpha = gContext.mbEnable ? 0xFF000000 : 0x80000000; + + matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection; + for (int i = 0; i < 4;i++) + { + ImVec2 worldBound1 = worldToPos(aabb[i], boundsMVP); + ImVec2 worldBound2 = worldToPos(aabb[(i+1)%4], boundsMVP); + if( !IsInContextRect( worldBound1 ) || !IsInContextRect( worldBound2 ) ) + { + continue; + } + float boundDistance = sqrtf(ImLengthSqr(worldBound1 - worldBound2)); + int stepCount = (int)(boundDistance / 10.f); + stepCount = min( stepCount, 1000 ); + float stepLength = 1.f / (float)stepCount; + for (int j = 0; j < stepCount; j++) + { + float t1 = (float)j * stepLength; + float t2 = (float)j * stepLength + stepLength * 0.5f; + ImVec2 worldBoundSS1 = ImLerp(worldBound1, worldBound2, ImVec2(t1, t1)); + ImVec2 worldBoundSS2 = ImLerp(worldBound1, worldBound2, ImVec2(t2, t2)); + //drawList->AddLine(worldBoundSS1, worldBoundSS2, 0x000000 + anchorAlpha, 3.f); + drawList->AddLine(worldBoundSS1, worldBoundSS2, 0xAAAAAA + anchorAlpha, 2.f); + } + vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4] ) * 0.5f; + ImVec2 midBound = worldToPos(midPoint, boundsMVP); + static const float AnchorBigRadius = 8.f; + static const float AnchorSmallRadius = 6.f; + bool overBigAnchor = ImLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius); + bool overSmallAnchor = ImLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius); + + int type = NONE; + vec_t gizmoHitProportion; + + switch (operation) + { + case TRANSLATE: type = GetMoveType(&gizmoHitProportion); break; + case ROTATE: type = GetRotateType(); break; + case SCALE: type = GetScaleType(); break; + case BOUNDS: break; + } + if (type != NONE) + { + overBigAnchor = false; + overSmallAnchor = false; + } + + + unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (0xAAAAAA + anchorAlpha); + unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (0xAAAAAA + anchorAlpha); + + drawList->AddCircleFilled(worldBound1, AnchorBigRadius, 0xFF000000); + drawList->AddCircleFilled(worldBound1, AnchorBigRadius-1.2f, bigAnchorColor); + + drawList->AddCircleFilled(midBound, AnchorSmallRadius, 0xFF000000); + drawList->AddCircleFilled(midBound, AnchorSmallRadius-1.2f, smallAnchorColor); + int oppositeIndex = (i + 2) % 4; + // big anchor on corners + if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && CanActivate()) + { + gContext.mBoundsPivot.TransformPoint(aabb[(i + 2) % 4], gContext.mModelSource); + gContext.mBoundsAnchor.TransformPoint(aabb[i], gContext.mModelSource); + gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection); + gContext.mBoundsBestAxis = bestAxis; + gContext.mBoundsAxis[0] = secondAxis; + gContext.mBoundsAxis[1] = thirdAxis; + + gContext.mBoundsLocalPivot.Set(0.f); + gContext.mBoundsLocalPivot[secondAxis] = aabb[oppositeIndex][secondAxis]; + gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis]; + + gContext.mbUsingBounds = true; + gContext.mBoundsMatrix = gContext.mModelSource; + } + // small anchor on middle of segment + if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && CanActivate()) + { + vec_t midPointOpposite = (aabb[(i + 2) % 4] + aabb[(i + 3) % 4]) * 0.5f; + gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource); + gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource); + gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection); + gContext.mBoundsBestAxis = bestAxis; + int indices[] = { secondAxis , thirdAxis }; + gContext.mBoundsAxis[0] = indices[i%2]; + gContext.mBoundsAxis[1] = -1; + + gContext.mBoundsLocalPivot.Set(0.f); + gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f); + + gContext.mbUsingBounds = true; + gContext.mBoundsMatrix = gContext.mModelSource; + } + } + + if (gContext.mbUsingBounds) + { + matrix_t scale; + scale.SetToIdentity(); + + // compute projected mouse position on plan + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mBoundsPlan); + vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; + + // compute a reference and delta vectors base on mouse move + vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs(); + vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs(); + + // for 1 or 2 axes, compute a ratio that's used for scale and snap it based on resulting length + for (int i = 0; i < 2; i++) + { + int axisIndex1 = gContext.mBoundsAxis[i]; + if (axisIndex1 == -1) + continue; + + float ratioAxis = 1.f; + vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex1].Abs(); + + float dtAxis = axisDir.Dot(referenceVector); + float boundSize = bounds[axisIndex1 + 3] - bounds[axisIndex1]; + if (dtAxis > FLT_EPSILON) + ratioAxis = axisDir.Dot(deltaVector) / dtAxis; + + if (snapValues) + { + float length = boundSize * ratioAxis; + ComputeSnap(&length, snapValues[axisIndex1]); + if (boundSize > FLT_EPSILON) + ratioAxis = length / boundSize; + } + scale.component[axisIndex1] *= ratioAxis; + } + + // transform matrix + matrix_t preScale, postScale; + preScale.Translation(-gContext.mBoundsLocalPivot); + postScale.Translation(gContext.mBoundsLocalPivot); + matrix_t res = preScale * scale * postScale * gContext.mBoundsMatrix; + *matrix = res; + + // info text + char tmps[512]; + ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); + ImFormatString(tmps, sizeof(tmps), "X: %.2f Y: %.2f Z:%.2f" + , (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length() * scale.component[0].Length() + , (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length() * scale.component[1].Length() + , (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length() * scale.component[2].Length() + ); + drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps); + drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps); + } + + if (!io.MouseDown[0]) + gContext.mbUsingBounds = false; + + if( gContext.mbUsingBounds ) + break; + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + + static int GetScaleType() + { + ImGuiIO& io = ImGui::GetIO(); + int type = NONE; + + // screen + if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x && + io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y) + type = SCALE_XYZ; + + // compute + for (unsigned int i = 0; i < 3 && type == NONE; i++) + { + vec_t dirPlaneX, dirPlaneY, dirAxis; + bool belowAxisLimit, belowPlaneLimit; + ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); + + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, dirAxis)); + vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len; + + const ImVec2 posOnPlanScreen = worldToPos(posOnPlan, gContext.mViewProjection); + const ImVec2 axisStartOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor * 0.1f, gContext.mViewProjection); + const ImVec2 axisEndOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor, gContext.mViewProjection); + + vec_t closestPointOnAxis = PointOnSegment(makeVect(posOnPlanScreen), makeVect(axisStartOnScreen), makeVect(axisEndOnScreen)); + + if ((closestPointOnAxis - makeVect(posOnPlanScreen)).Length() < 12.f) // pixel size + type = SCALE_X + i; + } + return type; + } + + static int GetRotateType() + { + ImGuiIO& io = ImGui::GetIO(); + int type = NONE; + + vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f }; + float dist = deltaScreen.Length(); + if (dist >= (gContext.mRadiusSquareCenter - 1.0f) && dist < (gContext.mRadiusSquareCenter + 1.0f)) + type = ROTATE_SCREEN; + + const vec_t planNormals[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir}; + + for (unsigned int i = 0; i < 3 && type == NONE; i++) + { + // pickup plan + vec_t pickupPlan = BuildPlan(gContext.mModel.v.position, planNormals[i]); + + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, pickupPlan); + vec_t localPos = gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position; + + if (Dot(Normalized(localPos), gContext.mRayVector) > FLT_EPSILON) + continue; + vec_t idealPosOnCircle = Normalized(localPos); + idealPosOnCircle.TransformVector(gContext.mModelInverse); + ImVec2 idealPosOnCircleScreen = worldToPos(idealPosOnCircle * gContext.mScreenFactor, gContext.mMVP); + + //gContext.mDrawList->AddCircle(idealPosOnCircleScreen, 5.f, 0xFFFFFFFF); + ImVec2 distanceOnScreen = idealPosOnCircleScreen - io.MousePos; + + float distance = makeVect(distanceOnScreen).Length(); + if (distance < 8.f) // pixel size + type = ROTATE_X + i; + } + + return type; + } + + static int GetMoveType(vec_t *gizmoHitProportion) + { + ImGuiIO& io = ImGui::GetIO(); + int type = NONE; + + // screen + if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x && + io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y) + type = MOVE_SCREEN; + + // compute + for (unsigned int i = 0; i < 3 && type == NONE; i++) + { + vec_t dirPlaneX, dirPlaneY, dirAxis; + bool belowAxisLimit, belowPlaneLimit; + ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); + dirAxis.TransformVector(gContext.mModel); + dirPlaneX.TransformVector(gContext.mModel); + dirPlaneY.TransformVector(gContext.mModel); + + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, dirAxis)); + vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len; + + const ImVec2 posOnPlanScreen = worldToPos(posOnPlan, gContext.mViewProjection); + const ImVec2 axisStartOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor * 0.1f, gContext.mViewProjection); + const ImVec2 axisEndOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor, gContext.mViewProjection); + + vec_t closestPointOnAxis = PointOnSegment(makeVect(posOnPlanScreen), makeVect(axisStartOnScreen), makeVect(axisEndOnScreen)); + + if ((closestPointOnAxis - makeVect(posOnPlanScreen)).Length() < 12.f) // pixel size + type = MOVE_X + i; + + const float dx = dirPlaneX.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor)); + const float dy = dirPlaneY.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor)); + if (belowPlaneLimit && dx >= quadUV[0] && dx <= quadUV[4] && dy >= quadUV[1] && dy <= quadUV[3]) + type = MOVE_YZ + i; + + if (gizmoHitProportion) + *gizmoHitProportion = makeVect(dx, dy, 0.f); + } + return type; + } + + static void HandleTranslation(float *matrix, float *deltaMatrix, int& type, float *snap) + { + ImGuiIO& io = ImGui::GetIO(); + bool applyRotationLocaly = gContext.mMode == LOCAL || type == MOVE_SCREEN; + + // move + if (gContext.mbUsing) + { + ImGui::CaptureMouseFromApp(); + const float len = fabsf(IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan)); // near plan + vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; + + + + // compute delta + vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor; + vec_t delta = newOrigin - gContext.mModel.v.position; + + // 1 axis constraint + if (gContext.mCurrentOperation >= MOVE_X && gContext.mCurrentOperation <= MOVE_Z) + { + int axisIndex = gContext.mCurrentOperation - MOVE_X; + const vec_t& axisValue = *(vec_t*)&gContext.mModel.m[axisIndex]; + float lengthOnAxis = Dot(axisValue, delta); + delta = axisValue * lengthOnAxis; + } + + // snap + if (snap) + { + vec_t cumulativeDelta = gContext.mModel.v.position + delta - gContext.mMatrixOrigin; + if (applyRotationLocaly) + { + matrix_t modelSourceNormalized = gContext.mModelSource; + modelSourceNormalized.OrthoNormalize(); + matrix_t modelSourceNormalizedInverse; + modelSourceNormalizedInverse.Inverse(modelSourceNormalized); + cumulativeDelta.TransformVector(modelSourceNormalizedInverse); + ComputeSnap(cumulativeDelta, snap); + cumulativeDelta.TransformVector(modelSourceNormalized); + } + else + { + ComputeSnap(cumulativeDelta, snap); + } + delta = gContext.mMatrixOrigin + cumulativeDelta - gContext.mModel.v.position; + + } + + // compute matrix & delta + matrix_t deltaMatrixTranslation; + deltaMatrixTranslation.Translation(delta); + if (deltaMatrix) + memcpy(deltaMatrix, deltaMatrixTranslation.m16, sizeof(float) * 16); + + + matrix_t res = gContext.mModelSource * deltaMatrixTranslation; + *(matrix_t*)matrix = res; + + if (!io.MouseDown[0]) + gContext.mbUsing = false; + + type = gContext.mCurrentOperation; + } + else + { + // find new possible way to move + vec_t gizmoHitProportion; + type = GetMoveType(&gizmoHitProportion); + if(type != NONE) + { + ImGui::CaptureMouseFromApp(); + } + if (CanActivate() && type != NONE) + { + gContext.mbUsing = true; + gContext.mCurrentOperation = type; + vec_t movePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, + gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, + -gContext.mCameraDir }; + + vec_t cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye); + for (unsigned int i = 0; i < 3; i++) + { + vec_t orthoVector = Cross(movePlanNormal[i], cameraToModelNormalized); + movePlanNormal[i].Cross(orthoVector); + movePlanNormal[i].Normalize(); + } + // pickup plan + gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MOVE_X]); + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); + gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len; + gContext.mMatrixOrigin = gContext.mModel.v.position; + + gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor); + } + } + } + + static void HandleScale(float *matrix, float *deltaMatrix, int& type, float *snap) + { + ImGuiIO& io = ImGui::GetIO(); + + if (!gContext.mbUsing) + { + // find new possible way to scale + type = GetScaleType(); + if(type != NONE) + { + ImGui::CaptureMouseFromApp(); + } + if (CanActivate() && type != NONE) + { + gContext.mbUsing = true; + gContext.mCurrentOperation = type; + const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.up, gContext.mModel.v.right, -gContext.mCameraDir }; + // pickup plan + + gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - SCALE_X]); + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); + gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len; + gContext.mMatrixOrigin = gContext.mModel.v.position; + gContext.mScale.Set(1.f, 1.f, 1.f); + gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor); + gContext.mScaleValueOrigin = makeVect(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length()); + gContext.mSaveMousePosx = io.MousePos.x; + } + } + // scale + if (gContext.mbUsing) + { + ImGui::CaptureMouseFromApp(); + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); + vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; + vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor; + vec_t delta = newOrigin - gContext.mModel.v.position; + + // 1 axis constraint + if (gContext.mCurrentOperation >= SCALE_X && gContext.mCurrentOperation <= SCALE_Z) + { + int axisIndex = gContext.mCurrentOperation - SCALE_X; + const vec_t& axisValue = *(vec_t*)&gContext.mModel.m[axisIndex]; + float lengthOnAxis = Dot(axisValue, delta); + delta = axisValue * lengthOnAxis; + + vec_t baseVector = gContext.mTranslationPlanOrigin - gContext.mModel.v.position; + float ratio = Dot(axisValue, baseVector + delta) / Dot(axisValue, baseVector); + + gContext.mScale[axisIndex] = max(ratio, 0.001f); + } + else + { + float scaleDelta = (io.MousePos.x - gContext.mSaveMousePosx) * 0.01f; + gContext.mScale.Set(max(1.f + scaleDelta, 0.001f)); + } + + // snap + if (snap) + { + float scaleSnap[] = { snap[0], snap[0], snap[0] }; + ComputeSnap(gContext.mScale, scaleSnap); + } + + // no 0 allowed + for (int i = 0; i < 3;i++) + gContext.mScale[i] = max(gContext.mScale[i], 0.001f); + + // compute matrix & delta + matrix_t deltaMatrixScale; + deltaMatrixScale.Scale(gContext.mScale * gContext.mScaleValueOrigin); + + matrix_t res = deltaMatrixScale * gContext.mModel; + *(matrix_t*)matrix = res; + + if (deltaMatrix) + { + deltaMatrixScale.Scale(gContext.mScale); + memcpy(deltaMatrix, deltaMatrixScale.m16, sizeof(float) * 16); + } + + if (!io.MouseDown[0]) + gContext.mbUsing = false; + + type = gContext.mCurrentOperation; + } + } + + static void HandleRotation(float *matrix, float *deltaMatrix, int& type, float *snap) + { + ImGuiIO& io = ImGui::GetIO(); + bool applyRotationLocaly = gContext.mMode == LOCAL; + + if (!gContext.mbUsing) + { + type = GetRotateType(); + + if(type != NONE) + { + ImGui::CaptureMouseFromApp(); + } + + if (type == ROTATE_SCREEN) + { + applyRotationLocaly = true; + } + + if (CanActivate() && type != NONE) + { + gContext.mbUsing = true; + gContext.mCurrentOperation = type; + const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir }; + // pickup plan + if (applyRotationLocaly) + { + gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, rotatePlanNormal[type - ROTATE_X]); + } + else + { + gContext.mTranslationPlan = BuildPlan(gContext.mModelSource.v.position, directionUnary[type - ROTATE_X]); + } + + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); + vec_t localPos = gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position; + gContext.mRotationVectorSource = Normalized(localPos); + gContext.mRotationAngleOrigin = ComputeAngleOnPlan(); + } + } + + // rotation + if (gContext.mbUsing) + { + ImGui::CaptureMouseFromApp(); + gContext.mRotationAngle = ComputeAngleOnPlan(); + if (snap) + { + float snapInRadian = snap[0] * DEG2RAD; + ComputeSnap(&gContext.mRotationAngle, snapInRadian); + } + vec_t rotationAxisLocalSpace; + + rotationAxisLocalSpace.TransformVector(makeVect(gContext.mTranslationPlan.x, gContext.mTranslationPlan.y, gContext.mTranslationPlan.z, 0.f), gContext.mModelInverse); + rotationAxisLocalSpace.Normalize(); + + matrix_t deltaRotation; + deltaRotation.RotationAxis(rotationAxisLocalSpace, gContext.mRotationAngle - gContext.mRotationAngleOrigin); + gContext.mRotationAngleOrigin = gContext.mRotationAngle; + + matrix_t scaleOrigin; + scaleOrigin.Scale(gContext.mModelScaleOrigin); + + if (applyRotationLocaly) + { + *(matrix_t*)matrix = scaleOrigin * deltaRotation * gContext.mModel; + } + else + { + matrix_t res = gContext.mModelSource; + res.v.position.Set(0.f); + + *(matrix_t*)matrix = res * deltaRotation; + ((matrix_t*)matrix)->v.position = gContext.mModelSource.v.position; + } + + if (deltaMatrix) + { + *(matrix_t*)deltaMatrix = gContext.mModelInverse * deltaRotation * gContext.mModel; + } + + if (!io.MouseDown[0]) + gContext.mbUsing = false; + + type = gContext.mCurrentOperation; + } + } + + void DecomposeMatrixToComponents(const float *matrix, float *translation, float *rotation, float *scale) + { + matrix_t mat = *(matrix_t*)matrix; + + scale[0] = mat.v.right.Length(); + scale[1] = mat.v.up.Length(); + scale[2] = mat.v.dir.Length(); + + mat.OrthoNormalize(); + + rotation[0] = RAD2DEG * atan2f(mat.m[1][2], mat.m[2][2]); + rotation[1] = RAD2DEG * atan2f(-mat.m[0][2], sqrtf(mat.m[1][2] * mat.m[1][2] + mat.m[2][2]* mat.m[2][2])); + rotation[2] = RAD2DEG * atan2f(mat.m[0][1], mat.m[0][0]); + + translation[0] = mat.v.position.x; + translation[1] = mat.v.position.y; + translation[2] = mat.v.position.z; + } + + void RecomposeMatrixFromComponents(const float *translation, const float *rotation, const float *scale, float *matrix) + { + matrix_t& mat = *(matrix_t*)matrix; + + matrix_t rot[3]; + for (int i = 0; i < 3;i++) + rot[i].RotationAxis(directionUnary[i], rotation[i] * DEG2RAD); + + mat = rot[0] * rot[1] * rot[2]; + + float validScale[3]; + for (int i = 0; i < 3; i++) + { + if (fabsf(scale[i]) < FLT_EPSILON) + validScale[i] = 0.001f; + else + validScale[i] = scale[i]; + } + mat.v.right *= validScale[0]; + mat.v.up *= validScale[1]; + mat.v.dir *= validScale[2]; + mat.v.position.Set(translation[0], translation[1], translation[2], 1.f); + } + + void Manipulate(const float *view, const float *projection, OPERATION operation, MODE mode, float *matrix, float *deltaMatrix, float *snap, float *localBounds, float *boundsSnap) + { + ComputeContext(view, projection, matrix, mode); + + // set delta to identity + if (deltaMatrix) + ((matrix_t*)deltaMatrix)->SetToIdentity(); + + // behind camera + vec_t camSpacePosition; + camSpacePosition.TransformPoint(makeVect(0.f, 0.f, 0.f), gContext.mMVP); + if (camSpacePosition.z < 0.001f) + return; + + // -- + int type = NONE; + if (gContext.mbEnable) + { + if (!gContext.mbUsingBounds) + { + switch (operation) + { + case ROTATE: + HandleRotation(matrix, deltaMatrix, type, snap); + break; + case TRANSLATE: + HandleTranslation(matrix, deltaMatrix, type, snap); + break; + case SCALE: + HandleScale(matrix, deltaMatrix, type, snap); + break; + case BOUNDS: + break; + } + } + } + + if (localBounds && !gContext.mbUsing) + HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap, operation); + + if (!gContext.mbUsingBounds) + { + switch (operation) + { + case ROTATE: + DrawRotationGizmo(type); + break; + case TRANSLATE: + DrawTranslationGizmo(type); + break; + case SCALE: + DrawScaleGizmo(type); + break; + case BOUNDS: + break; + } + } + } + + void DrawCube(const float *view, const float *projection, const float *matrix) + { + matrix_t viewInverse; + viewInverse.Inverse(*(matrix_t*)view); + const matrix_t& model = *(matrix_t*)matrix; + matrix_t res = *(matrix_t*)matrix * *(matrix_t*)view * *(matrix_t*)projection; + + for (int iFace = 0; iFace < 6; iFace++) + { + const int normalIndex = (iFace % 3); + const int perpXIndex = (normalIndex + 1) % 3; + const int perpYIndex = (normalIndex + 2) % 3; + const float invert = (iFace > 2) ? -1.f : 1.f; + + const vec_t faceCoords[4] = { directionUnary[normalIndex] + directionUnary[perpXIndex] + directionUnary[perpYIndex], + directionUnary[normalIndex] + directionUnary[perpXIndex] - directionUnary[perpYIndex], + directionUnary[normalIndex] - directionUnary[perpXIndex] - directionUnary[perpYIndex], + directionUnary[normalIndex] - directionUnary[perpXIndex] + directionUnary[perpYIndex], + }; + + // clipping + bool skipFace = false; + for (unsigned int iCoord = 0; iCoord < 4; iCoord++) + { + vec_t camSpacePosition; + camSpacePosition.TransformPoint(faceCoords[iCoord] * 0.5f * invert, gContext.mMVP); + if (camSpacePosition.z < 0.001f) + { + skipFace = true; + break; + } + } + if (skipFace) + continue; + + // 3D->2D + ImVec2 faceCoordsScreen[4]; + for (unsigned int iCoord = 0; iCoord < 4; iCoord++) + faceCoordsScreen[iCoord] = worldToPos(faceCoords[iCoord] * 0.5f * invert, res); + + // back face culling + vec_t cullPos, cullNormal; + cullPos.TransformPoint(faceCoords[0] * 0.5f * invert, model); + cullNormal.TransformVector(directionUnary[normalIndex] * invert, model); + float dt = Dot(Normalized(cullPos - viewInverse.v.position), Normalized(cullNormal)); + if (dt>0.f) + continue; + + // draw face with lighter color + gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, directionColor[normalIndex] | 0x808080); + } + } + + void DrawGrid(const float *view, const float *projection, const float *matrix, const float gridSize) + { + matrix_t res = *(matrix_t*)matrix * *(matrix_t*)view * *(matrix_t*)projection; + + for (float f = -gridSize; f <= gridSize; f += 1.f) + { + gContext.mDrawList->AddLine(worldToPos(makeVect(f, 0.f, -gridSize), res), worldToPos(makeVect(f, 0.f, gridSize), res), 0xFF808080); + gContext.mDrawList->AddLine(worldToPos(makeVect(-gridSize, 0.f, f), res), worldToPos(makeVect(gridSize, 0.f, f), res), 0xFF808080); + } + } +}; +