1// The MIT License(MIT)
2//
3// Copyright(c) 2016 Cedric Guillemet
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files(the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions :
11//
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23#define IMGUI_DEFINE_MATH_OPERATORS
24
25// includes patches for multiview from
26// https://github.com/CedricGuillemet/ImGuizmo/issues/15
27
28namespace ImGuizmo
29{
30   static const float ZPI = 3.14159265358979323846f;
31   static const float RAD2DEG = (180.f / ZPI);
32   static const float DEG2RAD = (ZPI / 180.f);
33   static const float gGizmoSizeClipSpace = 0.1f;
34   const float screenRotateSize = 0.06f;
35
36   ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
37   // utility and math
38
39   void FPU_MatrixF_x_MatrixF(const float *a, const float *b, float *r)
40   {
41      r[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12];
42      r[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13];
43      r[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14];
44      r[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15];
45
46      r[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12];
47      r[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13];
48      r[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14];
49      r[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15];
50
51      r[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12];
52      r[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13];
53      r[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14];
54      r[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15];
55
56      r[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12];
57      r[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13];
58      r[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14];
59      r[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15];
60   }
61
62   //template <typename T> T LERP(T x, T y, float z) { return (x + (y - x)*z); }
63   template <typename T> T Clamp(T x, T y, T z) { return ((x<y) ? y : ((x>z) ? z : x)); }
64   template <typename T> T max(T x, T y) { return (x > y) ? x : y; }
65   template <typename T> T min(T x, T y) { return (x < y) ? x : y; }
66   template <typename T> bool IsWithin(T x, T y, T z) { return (x>=y) && (x<=z); }
67
68   struct matrix_t;
69   struct vec_t
70   {
71   public:
72      float x, y, z, w;
73
74      void Lerp(const vec_t& v, float t)
75      {
76         x += (v.x - x) * t;
77         y += (v.y - y) * t;
78         z += (v.z - z) * t;
79         w += (v.w - w) * t;
80      }
81
82      void Set(float v) { x = y = z = w = v; }
83      void Set(float _x, float _y, float _z = 0.f, float _w = 0.f) { x = _x; y = _y; z = _z; w = _w; }
84
85      vec_t& operator -= (const vec_t& v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; }
86      vec_t& operator += (const vec_t& v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; }
87      vec_t& operator *= (const vec_t& v) { x *= v.x; y *= v.y; z *= v.z; w *= v.w; return *this; }
88      vec_t& operator *= (float v) { x *= v;    y *= v;    z *= v;    w *= v;    return *this; }
89
90      vec_t operator * (float f) const;
91      vec_t operator - () const;
92      vec_t operator - (const vec_t& v) const;
93      vec_t operator + (const vec_t& v) const;
94      vec_t operator * (const vec_t& v) const;
95
96      const vec_t& operator + () const { return (*this); }
97      float Length() const { return sqrtf(x*x + y*y + z*z); };
98      float LengthSq() const { return (x*x + y*y + z*z); };
99      vec_t Normalize() { (*this) *= (1.f / Length()); return (*this); }
100      vec_t Normalize(const vec_t& v) { this->Set(v.x, v.y, v.z, v.w); this->Normalize(); return (*this); }
101      vec_t Abs() const;
102      void Cross(const vec_t& v)
103      {
104         vec_t res;
105         res.x = y * v.z - z * v.y;
106         res.y = z * v.x - x * v.z;
107         res.z = x * v.y - y * v.x;
108
109         x = res.x;
110         y = res.y;
111         z = res.z;
112         w = 0.f;
113      }
114      void Cross(const vec_t& v1, const vec_t& v2)
115      {
116         x = v1.y * v2.z - v1.z * v2.y;
117         y = v1.z * v2.x - v1.x * v2.z;
118         z = v1.x * v2.y - v1.y * v2.x;
119         w = 0.f;
120      }
121      float Dot(const vec_t &v) const
122      {
123         return (x * v.x) + (y * v.y) + (z * v.z) + (w * v.w);
124      }
125      float Dot3(const vec_t &v) const
126      {
127         return (x * v.x) + (y * v.y) + (z * v.z);
128      }
129
130      void Transform(const matrix_t& matrix);
131      void Transform(const vec_t & s, const matrix_t& matrix);
132
133      void TransformVector(const matrix_t& matrix);
134      void TransformPoint(const matrix_t& matrix);
135      void TransformVector(const vec_t& v, const matrix_t& matrix) { (*this) = v; this->TransformVector(matrix); }
136      void TransformPoint(const vec_t& v, const matrix_t& matrix) { (*this) = v; this->TransformPoint(matrix); }
137
138      float& operator [] (size_t index) { return ((float*)&x)[index]; }
139      const float& operator [] (size_t index) const { return ((float*)&x)[index]; }
140   };
141
142   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; }
143   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; }
144   vec_t vec_t::operator * (float f) const { return makeVect(x * f, y * f, z * f, w *f); }
145   vec_t vec_t::operator - () const { return makeVect(-x, -y, -z, -w); }
146   vec_t vec_t::operator - (const vec_t& v) const { return makeVect(x - v.x, y - v.y, z - v.z, w - v.w); }
147   vec_t vec_t::operator + (const vec_t& v) const { return makeVect(x + v.x, y + v.y, z + v.z, w + v.w); }
148   vec_t vec_t::operator * (const vec_t& v) const { return makeVect(x * v.x, y * v.y, z * v.z, w * v.w); }
149   vec_t vec_t::Abs() const { return makeVect(fabsf(x), fabsf(y), fabsf(z)); }
150
151   vec_t Normalized(const vec_t& v) { vec_t res; res = v; res.Normalize(); return res; }
152   vec_t Cross(const vec_t& v1, const vec_t& v2)
153   {
154      vec_t res;
155      res.x = v1.y * v2.z - v1.z * v2.y;
156      res.y = v1.z * v2.x - v1.x * v2.z;
157      res.z = v1.x * v2.y - v1.y * v2.x;
158      res.w = 0.f;
159      return res;
160   }
161
162   float Dot(const vec_t &v1, const vec_t &v2)
163   {
164      return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z);
165   }
166
167   vec_t BuildPlan(const vec_t & p_point1, const vec_t & p_normal)
168   {
169      vec_t normal, res;
170      normal.Normalize(p_normal);
171      res.w = normal.Dot(p_point1);
172      res.x = normal.x;
173      res.y = normal.y;
174      res.z = normal.z;
175      return res;
176   }
177
178   struct matrix_t
179   {
180   public:
181
182      union
183      {
184         float m[4][4];
185         float m16[16];
186         struct
187         {
188            vec_t right, up, dir, position;
189         } v;
190         vec_t component[4];
191      };
192
193      matrix_t(const matrix_t& other) { memcpy(&m16[0], &other.m16[0], sizeof(float) * 16); }
194      matrix_t() {}
195
196      operator float * () { return m16; }
197      operator const float* () const { return m16; }
198      void Translation(float _x, float _y, float _z) { this->Translation(makeVect(_x, _y, _z)); }
199
200      void Translation(const vec_t& vt)
201      {
202         v.right.Set(1.f, 0.f, 0.f, 0.f);
203         v.up.Set(0.f, 1.f, 0.f, 0.f);
204         v.dir.Set(0.f, 0.f, 1.f, 0.f);
205         v.position.Set(vt.x, vt.y, vt.z, 1.f);
206      }
207
208      void Scale(float _x, float _y, float _z)
209      {
210         v.right.Set(_x, 0.f, 0.f, 0.f);
211         v.up.Set(0.f, _y, 0.f, 0.f);
212         v.dir.Set(0.f, 0.f, _z, 0.f);
213         v.position.Set(0.f, 0.f, 0.f, 1.f);
214      }
215      void Scale(const vec_t& s) { Scale(s.x, s.y, s.z); }
216
217      matrix_t& operator *= (const matrix_t& mat)
218      {
219         matrix_t tmpMat;
220         tmpMat = *this;
221         tmpMat.Multiply(mat);
222         *this = tmpMat;
223         return *this;
224      }
225      matrix_t operator * (const matrix_t& mat) const
226      {
227         matrix_t matT;
228         matT.Multiply(*this, mat);
229         return matT;
230      }
231
232      void Multiply(const matrix_t &matrix)
233      {
234         matrix_t tmp;
235         tmp = *this;
236
237         FPU_MatrixF_x_MatrixF((float*)&tmp, (float*)&matrix, (float*)this);
238      }
239
240      void Multiply(const matrix_t &m1, const matrix_t &m2)
241      {
242         FPU_MatrixF_x_MatrixF((float*)&m1, (float*)&m2, (float*)this);
243      }
244
245      float GetDeterminant() const
246      {
247         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] -
248            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];
249      }
250
251      float Inverse(const matrix_t &srcMatrix, bool affine = false);
252      void SetToIdentity()
253      {
254         v.right.Set(1.f, 0.f, 0.f, 0.f);
255         v.up.Set(0.f, 1.f, 0.f, 0.f);
256         v.dir.Set(0.f, 0.f, 1.f, 0.f);
257         v.position.Set(0.f, 0.f, 0.f, 1.f);
258      }
259      void Transpose()
260      {
261         matrix_t tmpm;
262         for (int l = 0; l < 4; l++)
263         {
264            for (int c = 0; c < 4; c++)
265            {
266               tmpm.m[l][c] = m[c][l];
267            }
268         }
269         (*this) = tmpm;
270      }
271
272      void RotationAxis(const vec_t & axis, float angle);
273
274      void OrthoNormalize()
275      {
276         v.right.Normalize();
277         v.up.Normalize();
278         v.dir.Normalize();
279      }
280   };
281
282   void vec_t::Transform(const matrix_t& matrix)
283   {
284      vec_t out;
285
286      out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + w * matrix.m[3][0];
287      out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + w * matrix.m[3][1];
288      out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + w * matrix.m[3][2];
289      out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + w * matrix.m[3][3];
290
291      x = out.x;
292      y = out.y;
293      z = out.z;
294      w = out.w;
295   }
296
297   void vec_t::Transform(const vec_t & s, const matrix_t& matrix)
298   {
299      *this = s;
300      Transform(matrix);
301   }
302
303   void vec_t::TransformPoint(const matrix_t& matrix)
304   {
305      vec_t out;
306
307      out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + matrix.m[3][0];
308      out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + matrix.m[3][1];
309      out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + matrix.m[3][2];
310      out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + matrix.m[3][3];
311
312      x = out.x;
313      y = out.y;
314      z = out.z;
315      w = out.w;
316   }
317
318
319   void vec_t::TransformVector(const matrix_t& matrix)
320   {
321      vec_t out;
322
323      out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0];
324      out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1];
325      out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2];
326      out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3];
327
328      x = out.x;
329      y = out.y;
330      z = out.z;
331      w = out.w;
332   }
333
334   float matrix_t::Inverse(const matrix_t &srcMatrix, bool affine)
335   {
336      float det = 0;
337
338      if (affine)
339      {
340         det = GetDeterminant();
341         float s = 1 / det;
342         m[0][0] = (srcMatrix.m[1][1] * srcMatrix.m[2][2] - srcMatrix.m[1][2] * srcMatrix.m[2][1]) * s;
343         m[0][1] = (srcMatrix.m[2][1] * srcMatrix.m[0][2] - srcMatrix.m[2][2] * srcMatrix.m[0][1]) * s;
344         m[0][2] = (srcMatrix.m[0][1] * srcMatrix.m[1][2] - srcMatrix.m[0][2] * srcMatrix.m[1][1]) * s;
345         m[1][0] = (srcMatrix.m[1][2] * srcMatrix.m[2][0] - srcMatrix.m[1][0] * srcMatrix.m[2][2]) * s;
346         m[1][1] = (srcMatrix.m[2][2] * srcMatrix.m[0][0] - srcMatrix.m[2][0] * srcMatrix.m[0][2]) * s;
347         m[1][2] = (srcMatrix.m[0][2] * srcMatrix.m[1][0] - srcMatrix.m[0][0] * srcMatrix.m[1][2]) * s;
348         m[2][0] = (srcMatrix.m[1][0] * srcMatrix.m[2][1] - srcMatrix.m[1][1] * srcMatrix.m[2][0]) * s;
349         m[2][1] = (srcMatrix.m[2][0] * srcMatrix.m[0][1] - srcMatrix.m[2][1] * srcMatrix.m[0][0]) * s;
350         m[2][2] = (srcMatrix.m[0][0] * srcMatrix.m[1][1] - srcMatrix.m[0][1] * srcMatrix.m[1][0]) * s;
351         m[3][0] = -(m[0][0] * srcMatrix.m[3][0] + m[1][0] * srcMatrix.m[3][1] + m[2][0] * srcMatrix.m[3][2]);
352         m[3][1] = -(m[0][1] * srcMatrix.m[3][0] + m[1][1] * srcMatrix.m[3][1] + m[2][1] * srcMatrix.m[3][2]);
353         m[3][2] = -(m[0][2] * srcMatrix.m[3][0] + m[1][2] * srcMatrix.m[3][1] + m[2][2] * srcMatrix.m[3][2]);
354      }
355      else
356      {
357         // transpose matrix
358         float src[16];
359         for (int i = 0; i < 4; ++i)
360         {
361            src[i] = srcMatrix.m16[i * 4];
362            src[i + 4] = srcMatrix.m16[i * 4 + 1];
363            src[i + 8] = srcMatrix.m16[i * 4 + 2];
364            src[i + 12] = srcMatrix.m16[i * 4 + 3];
365         }
366
367         // calculate pairs for first 8 elements (cofactors)
368         float tmp[12]; // temp array for pairs
369         tmp[0] = src[10] * src[15];
370         tmp[1] = src[11] * src[14];
371         tmp[2] = src[9] * src[15];
372         tmp[3] = src[11] * src[13];
373         tmp[4] = src[9] * src[14];
374         tmp[5] = src[10] * src[13];
375         tmp[6] = src[8] * src[15];
376         tmp[7] = src[11] * src[12];
377         tmp[8] = src[8] * src[14];
378         tmp[9] = src[10] * src[12];
379         tmp[10] = src[8] * src[13];
380         tmp[11] = src[9] * src[12];
381
382         // calculate first 8 elements (cofactors)
383         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]);
384         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]);
385         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]);
386         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]);
387         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]);
388         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]);
389         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]);
390         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]);
391
392         // calculate pairs for second 8 elements (cofactors)
393         tmp[0] = src[2] * src[7];
394         tmp[1] = src[3] * src[6];
395         tmp[2] = src[1] * src[7];
396         tmp[3] = src[3] * src[5];
397         tmp[4] = src[1] * src[6];
398         tmp[5] = src[2] * src[5];
399         tmp[6] = src[0] * src[7];
400         tmp[7] = src[3] * src[4];
401         tmp[8] = src[0] * src[6];
402         tmp[9] = src[2] * src[4];
403         tmp[10] = src[0] * src[5];
404         tmp[11] = src[1] * src[4];
405
406         // calculate second 8 elements (cofactors)
407         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]);
408         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]);
409         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]);
410         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]);
411         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]);
412         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]);
413         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]);
414         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]);
415
416         // calculate determinant
417         det = src[0] * m16[0] + src[1] * m16[1] + src[2] * m16[2] + src[3] * m16[3];
418
419         // calculate matrix inverse
420         float invdet = 1 / det;
421         for (int j = 0; j < 16; ++j)
422         {
423            m16[j] *= invdet;
424         }
425      }
426
427      return det;
428   }
429
430   void matrix_t::RotationAxis(const vec_t & axis, float angle)
431   {
432      float length2 = axis.LengthSq();
433      if (length2 < FLT_EPSILON)
434      {
435         SetToIdentity();
436         return;
437      }
438
439      vec_t n = axis * (1.f / sqrtf(length2));
440      float s = sinf(angle);
441      float c = cosf(angle);
442      float k = 1.f - c;
443
444      float xx = n.x * n.x * k + c;
445      float yy = n.y * n.y * k + c;
446      float zz = n.z * n.z * k + c;
447      float xy = n.x * n.y * k;
448      float yz = n.y * n.z * k;
449      float zx = n.z * n.x * k;
450      float xs = n.x * s;
451      float ys = n.y * s;
452      float zs = n.z * s;
453
454      m[0][0] = xx;
455      m[0][1] = xy + zs;
456      m[0][2] = zx - ys;
457      m[0][3] = 0.f;
458      m[1][0] = xy - zs;
459      m[1][1] = yy;
460      m[1][2] = yz + xs;
461      m[1][3] = 0.f;
462      m[2][0] = zx + ys;
463      m[2][1] = yz - xs;
464      m[2][2] = zz;
465      m[2][3] = 0.f;
466      m[3][0] = 0.f;
467      m[3][1] = 0.f;
468      m[3][2] = 0.f;
469      m[3][3] = 1.f;
470   }
471
472   ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
473   //
474
475   enum MOVETYPE
476   {
477      NONE,
478      MOVE_X,
479      MOVE_Y,
480      MOVE_Z,
481      MOVE_YZ,
482      MOVE_ZX,
483      MOVE_XY,
484      MOVE_SCREEN,
485      ROTATE_X,
486      ROTATE_Y,
487      ROTATE_Z,
488      ROTATE_SCREEN,
489      SCALE_X,
490      SCALE_Y,
491      SCALE_Z,
492      SCALE_XYZ
493   };
494
495   struct Context
496   {
497      Context() : mbUsing(false), mbEnable(true), mbUsingBounds(false)
498      {
499      }
500
501      ImDrawList* mDrawList;
502
503      MODE mMode;
504      matrix_t mViewMat;
505      matrix_t mProjectionMat;
506      matrix_t mModel;
507      matrix_t mModelInverse;
508      matrix_t mModelSource;
509      matrix_t mModelSourceInverse;
510      matrix_t mMVP;
511      matrix_t mViewProjection;
512
513      vec_t mModelScaleOrigin;
514      vec_t mCameraEye;
515      vec_t mCameraRight;
516      vec_t mCameraDir;
517      vec_t mCameraUp;
518      vec_t mRayOrigin;
519      vec_t mRayVector;
520
521      float  mRadiusSquareCenter;
522      ImVec2 mScreenSquareCenter;
523      ImVec2 mScreenSquareMin;
524      ImVec2 mScreenSquareMax;
525
526      float mScreenFactor;
527      vec_t mRelativeOrigin;
528
529      bool mbUsing;
530      bool mbEnable;
531
532      // translation
533      vec_t mTranslationPlan;
534      vec_t mTranslationPlanOrigin;
535      vec_t mMatrixOrigin;
536
537      // rotation
538      vec_t mRotationVectorSource;
539      float mRotationAngle;
540      float mRotationAngleOrigin;
541      //vec_t mWorldToLocalAxis;
542
543      // scale
544      vec_t mScale;
545      vec_t mScaleValueOrigin;
546      float mSaveMousePosx;
547
548      // save axis factor when using gizmo
549      bool mBelowAxisLimit[3];
550      bool mBelowPlaneLimit[3];
551      float mAxisFactor[3];
552
553      // bounds stretching
554      vec_t mBoundsPivot;
555      vec_t mBoundsAnchor;
556      vec_t mBoundsPlan;
557      vec_t mBoundsLocalPivot;
558      int mBoundsBestAxis;
559      int mBoundsAxis[2];
560      bool mbUsingBounds;
561      matrix_t mBoundsMatrix;
562
563      //
564      int mCurrentOperation;
565
566      float mX = 0.f;
567      float mY = 0.f;
568      float mWidth = 0.f;
569      float mHeight = 0.f;
570      float mXMax = 0.f;
571      float mYMax = 0.f;
572     float mDisplayRatio = 1.f;
573
574     bool mIsOrthographic = false;
575   };
576
577   static Context gContext;
578
579   static const float angleLimit = 0.96f;
580   static const float planeLimit = 0.2f;
581
582   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) };
583   static const ImU32 directionColor[3] = { 0xFF0000AA, 0xFF00AA00, 0xFFAA0000 };
584
585   // Alpha: 100%: FF, 87%: DE, 70%: B3, 54%: 8A, 50%: 80, 38%: 61, 12%: 1F
586   static const ImU32 planeColor[3] = { 0x610000AA, 0x6100AA00, 0x61AA0000 };
587   static const ImU32 selectionColor = 0x8A1080FF;
588   static const ImU32 inactiveColor = 0x99999999;
589   static const ImU32 translationLineColor = 0xAAAAAAAA;
590   static const char *translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f",
591      "Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f",
592      "X : %5.3f Y : %5.3f Z : %5.3f" };
593   static const char *scaleInfoMask[] = { "X : %5.2f", "Y : %5.2f", "Z : %5.2f", "XYZ : %5.2f" };
594   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" };
595   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 };
596   static const float quadMin = 0.5f;
597   static const float quadMax = 0.8f;
598   static const float quadUV[8] = { quadMin, quadMin, quadMin, quadMax, quadMax, quadMax, quadMax, quadMin };
599   static const int halfCircleSegmentCount = 64;
600   static const float snapTension = 0.5f;
601
602   ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
603   //
604   static int GetMoveType(vec_t *gizmoHitProportion);
605   static int GetRotateType();
606   static int GetScaleType();
607
608   static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat)
609   {
610      vec_t trans;
611      trans.TransformPoint(worldPos, mat);
612      trans *= 0.5f / trans.w;
613      trans += makeVect(0.5f, 0.5f);
614      trans.y = 1.f - trans.y;
615      trans.x *= gContext.mWidth;
616      trans.y *= gContext.mHeight;
617      trans.x += gContext.mX;
618      trans.y += gContext.mY;
619      return ImVec2(trans.x, trans.y);
620   }
621
622   static void ComputeCameraRay(vec_t &rayOrigin, vec_t &rayDir)
623   {
624      ImGuiIO& io = ImGui::GetIO();
625
626      matrix_t mViewProjInverse;
627      mViewProjInverse.Inverse(gContext.mViewMat * gContext.mProjectionMat);
628
629      float mox = ((io.MousePos.x - gContext.mX) / gContext.mWidth) * 2.f - 1.f;
630      float moy = (1.f - ((io.MousePos.y - gContext.mY) / gContext.mHeight)) * 2.f - 1.f;
631
632      rayOrigin.Transform(makeVect(mox, moy, 0.f, 1.f), mViewProjInverse);
633      rayOrigin *= 1.f / rayOrigin.w;
634      vec_t rayEnd;
635      rayEnd.Transform(makeVect(mox, moy, 1.f, 1.f), mViewProjInverse);
636      rayEnd *= 1.f / rayEnd.w;
637      rayDir = Normalized(rayEnd - rayOrigin);
638   }
639
640   static float GetSegmentLengthClipSpace(const vec_t& start, const vec_t& end)
641   {
642      vec_t startOfSegment = start;
643      startOfSegment.TransformPoint(gContext.mMVP);
644      if (fabsf(startOfSegment.w)> FLT_EPSILON) // check for axis aligned with camera direction
645         startOfSegment *= 1.f / startOfSegment.w;
646
647      vec_t endOfSegment = end;
648      endOfSegment.TransformPoint(gContext.mMVP);
649      if (fabsf(endOfSegment.w)> FLT_EPSILON) // check for axis aligned with camera direction
650         endOfSegment *= 1.f / endOfSegment.w;
651
652      vec_t clipSpaceAxis = endOfSegment - startOfSegment;
653      clipSpaceAxis.y /= gContext.mDisplayRatio;
654      float segmentLengthInClipSpace = sqrtf(clipSpaceAxis.x*clipSpaceAxis.x + clipSpaceAxis.y*clipSpaceAxis.y);
655      return segmentLengthInClipSpace;
656   }
657
658   static float GetParallelogram(const vec_t& ptO, const vec_t& ptA, const vec_t& ptB)
659   {
660      vec_t pts[] = { ptO, ptA, ptB };
661      for (unsigned int i = 0; i < 3; i++)
662      {
663         pts[i].TransformPoint(gContext.mMVP);
664         if (fabsf(pts[i].w)> FLT_EPSILON) // check for axis aligned with camera direction
665            pts[i] *= 1.f / pts[i].w;
666      }
667      vec_t segA = pts[1] - pts[0];
668      vec_t segB = pts[2] - pts[0];
669      segA.y /= gContext.mDisplayRatio;
670      segB.y /= gContext.mDisplayRatio;
671      vec_t segAOrtho = makeVect(-segA.y, segA.x);
672      segAOrtho.Normalize();
673      float dt = segAOrtho.Dot3(segB);
674      float surface = sqrtf(segA.x*segA.x + segA.y*segA.y) * fabsf(dt);
675      return surface;
676   }
677
678   inline vec_t PointOnSegment(const vec_t & point, const vec_t & vertPos1, const vec_t & vertPos2)
679   {
680      vec_t c = point - vertPos1;
681      vec_t V;
682
683      V.Normalize(vertPos2 - vertPos1);
684      float d = (vertPos2 - vertPos1).Length();
685      float t = V.Dot3(c);
686
687      if (t < 0.f)
688         return vertPos1;
689
690      if (t > d)
691         return vertPos2;
692
693      return vertPos1 + V * t;
694   }
695
696   static float IntersectRayPlane(const vec_t & rOrigin, const vec_t& rVector, const vec_t& plan)
697   {
698      float numer = plan.Dot3(rOrigin) - plan.w;
699      float denom = plan.Dot3(rVector);
700
701      if (fabsf(denom) < FLT_EPSILON)  // normal is orthogonal to vector, cant intersect
702         return -1.0f;
703
704      return -(numer / denom);
705   }
706
707   static bool IsInContextRect( ImVec2 p )
708   {
709       return IsWithin( p.x, gContext.mX, gContext.mXMax ) && IsWithin(p.y, gContext.mY, gContext.mYMax );
710   }
711
712   void SetRect(float x, float y, float width, float height)
713   {
714       gContext.mX = x;
715       gContext.mY = y;
716       gContext.mWidth = width;
717       gContext.mHeight = height;
718       gContext.mXMax = gContext.mX + gContext.mWidth;
719       gContext.mYMax = gContext.mY + gContext.mXMax;
720      gContext.mDisplayRatio = width / height;
721   }
722
723   IMGUI_API void SetOrthographic(bool isOrthographic)
724   {
725      gContext.mIsOrthographic = isOrthographic;
726   }
727
728   void SetDrawlist()
729   {
730      gContext.mDrawList = ImGui::GetWindowDrawList();
731   }
732
733   void BeginFrame()
734   {
735      ImGuiIO& io = ImGui::GetIO();
736
737      const ImU32 flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus;
738      ImGui::SetNextWindowSize(io.DisplaySize);
739      ImGui::SetNextWindowPos(ImVec2(0, 0));
740
741      ImGui::PushStyleColor(ImGuiCol_WindowBg, 0);
742      ImGui::PushStyleColor(ImGuiCol_Border, 0);
743      ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
744
745      ImGui::Begin("gizmo", NULL, flags);
746      gContext.mDrawList = ImGui::GetWindowDrawList();
747      ImGui::End();
748      ImGui::PopStyleVar();
749      ImGui::PopStyleColor(2);
750   }
751
752   bool IsUsing()
753   {
754      return gContext.mbUsing||gContext.mbUsingBounds;
755   }
756
757   bool IsOver()
758   {
759      return (GetMoveType(NULL) != NONE) || GetRotateType() != NONE || GetScaleType() != NONE || IsUsing();
760   }
761
762   void Enable(bool enable)
763   {
764      gContext.mbEnable = enable;
765      if (!enable)
766      {
767          gContext.mbUsing = false;
768          gContext.mbUsingBounds = false;
769      }
770   }
771
772   static void ComputeContext(const float *view, const float *projection, float *matrix, MODE mode)
773   {
774      gContext.mMode = mode;
775      gContext.mViewMat = *(matrix_t*)view;
776      gContext.mProjectionMat = *(matrix_t*)projection;
777
778      if (mode == LOCAL)
779      {
780         gContext.mModel = *(matrix_t*)matrix;
781         gContext.mModel.OrthoNormalize();
782      }
783      else
784      {
785         gContext.mModel.Translation(((matrix_t*)matrix)->v.position);
786      }
787      gContext.mModelSource = *(matrix_t*)matrix;
788      gContext.mModelScaleOrigin.Set(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length());
789
790      gContext.mModelInverse.Inverse(gContext.mModel);
791      gContext.mModelSourceInverse.Inverse(gContext.mModelSource);
792      gContext.mViewProjection = gContext.mViewMat * gContext.mProjectionMat;
793      gContext.mMVP = gContext.mModel * gContext.mViewProjection;
794
795      matrix_t viewInverse;
796      viewInverse.Inverse(gContext.mViewMat);
797      gContext.mCameraDir = viewInverse.v.dir;
798      gContext.mCameraEye = viewInverse.v.position;
799      gContext.mCameraRight = viewInverse.v.right;
800      gContext.mCameraUp = viewInverse.v.up;
801
802     // compute scale from the size of camera right vector projected on screen at the matrix position
803     vec_t pointRight = viewInverse.v.right;
804     pointRight.TransformPoint(gContext.mViewProjection);
805     gContext.mScreenFactor = gGizmoSizeClipSpace / (pointRight.x / pointRight.w - gContext.mMVP.v.position.x / gContext.mMVP.v.position.w);
806
807     vec_t rightViewInverse = viewInverse.v.right;
808     rightViewInverse.TransformVector(gContext.mModelInverse);
809     float rightLength = GetSegmentLengthClipSpace(makeVect(0.f, 0.f), rightViewInverse);
810     gContext.mScreenFactor = gGizmoSizeClipSpace / rightLength;
811
812      ImVec2 centerSSpace = worldToPos(makeVect(0.f, 0.f), gContext.mMVP);
813      gContext.mScreenSquareCenter = centerSSpace;
814      gContext.mScreenSquareMin = ImVec2(centerSSpace.x - 10.f, centerSSpace.y - 10.f);
815      gContext.mScreenSquareMax = ImVec2(centerSSpace.x + 10.f, centerSSpace.y + 10.f);
816
817      ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector);
818   }
819
820   static void ComputeColors(ImU32 *colors, int type, OPERATION operation)
821   {
822      if (gContext.mbEnable)
823      {
824         switch (operation)
825         {
826         case TRANSLATE:
827            colors[0] = (type == MOVE_SCREEN) ? selectionColor : 0xFFFFFFFF;
828            for (int i = 0; i < 3; i++)
829            {
830               colors[i + 1] = (type == (int)(MOVE_X + i)) ? selectionColor : directionColor[i];
831               colors[i + 4] = (type == (int)(MOVE_YZ + i)) ? selectionColor : planeColor[i];
832               colors[i + 4] = (type == MOVE_SCREEN) ? selectionColor : colors[i + 4];
833            }
834            break;
835         case ROTATE:
836            colors[0] = (type == ROTATE_SCREEN) ? selectionColor : 0xFFFFFFFF;
837            for (int i = 0; i < 3; i++)
838               colors[i + 1] = (type == (int)(ROTATE_X + i)) ? selectionColor : directionColor[i];
839            break;
840         case SCALE:
841            colors[0] = (type == SCALE_XYZ) ? selectionColor : 0xFFFFFFFF;
842            for (int i = 0; i < 3; i++)
843               colors[i + 1] = (type == (int)(SCALE_X + i)) ? selectionColor : directionColor[i];
844            break;
845         case BOUNDS:
846            break;
847         }
848      }
849      else
850      {
851         for (int i = 0; i < 7; i++)
852            colors[i] = inactiveColor;
853      }
854   }
855
856   static void ComputeTripodAxisAndVisibility(int axisIndex, vec_t& dirAxis, vec_t& dirPlaneX, vec_t& dirPlaneY, bool& belowAxisLimit, bool& belowPlaneLimit)
857   {
858      dirAxis = directionUnary[axisIndex];
859      dirPlaneX = directionUnary[(axisIndex + 1) % 3];
860      dirPlaneY = directionUnary[(axisIndex + 2) % 3];
861
862      if (gContext.mbUsing)
863      {
864         // when using, use stored factors so the gizmo doesn't flip when we translate
865         belowAxisLimit = gContext.mBelowAxisLimit[axisIndex];
866         belowPlaneLimit = gContext.mBelowPlaneLimit[axisIndex];
867
868         dirAxis *= gContext.mAxisFactor[axisIndex];
869         dirPlaneX *= gContext.mAxisFactor[(axisIndex + 1) % 3];
870         dirPlaneY *= gContext.mAxisFactor[(axisIndex + 2) % 3];
871      }
872      else
873      {
874         // new method
875         float lenDir = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis);
876         float lenDirMinus = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirAxis);
877
878         float lenDirPlaneX = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirPlaneX);
879         float lenDirMinusPlaneX = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirPlaneX);
880
881         float lenDirPlaneY = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirPlaneY);
882         float lenDirMinusPlaneY = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirPlaneY);
883
884         float mulAxis = (lenDir < lenDirMinus && fabsf(lenDir - lenDirMinus) > FLT_EPSILON) ? -1.f : 1.f;
885         float mulAxisX = (lenDirPlaneX < lenDirMinusPlaneX && fabsf(lenDirPlaneX - lenDirMinusPlaneX) > FLT_EPSILON) ? -1.f : 1.f;
886         float mulAxisY = (lenDirPlaneY < lenDirMinusPlaneY && fabsf(lenDirPlaneY - lenDirMinusPlaneY) > FLT_EPSILON) ? -1.f : 1.f;
887         dirAxis *= mulAxis;
888         dirPlaneX *= mulAxisX;
889         dirPlaneY *= mulAxisY;
890
891         // for axis
892         float axisLengthInClipSpace = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis * gContext.mScreenFactor);
893
894         float paraSurf = GetParallelogram(makeVect(0.f, 0.f, 0.f), dirPlaneX * gContext.mScreenFactor, dirPlaneY * gContext.mScreenFactor);
895         belowPlaneLimit = (paraSurf > 0.0025f);
896         belowAxisLimit = (axisLengthInClipSpace > 0.02f);
897
898         // and store values
899         gContext.mAxisFactor[axisIndex] = mulAxis;
900         gContext.mAxisFactor[(axisIndex + 1) % 3] = mulAxisX;
901         gContext.mAxisFactor[(axisIndex + 2) % 3] = mulAxisY;
902         gContext.mBelowAxisLimit[axisIndex] = belowAxisLimit;
903         gContext.mBelowPlaneLimit[axisIndex] = belowPlaneLimit;
904      }
905   }
906
907   static void ComputeSnap(float*value, float snap)
908   {
909      if (snap <= FLT_EPSILON)
910         return;
911      float modulo = fmodf(*value, snap);
912      float moduloRatio = fabsf(modulo) / snap;
913      if (moduloRatio < snapTension)
914         *value -= modulo;
915      else if (moduloRatio >(1.f - snapTension))
916         *value = *value - modulo + snap * ((*value<0.f) ? -1.f : 1.f);
917   }
918   static void ComputeSnap(vec_t& value, float *snap)
919   {
920      for (int i = 0; i < 3; i++)
921      {
922         ComputeSnap(&value[i], snap[i]);
923      }
924   }
925
926   static float ComputeAngleOnPlan()
927   {
928      const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
929      vec_t localPos = Normalized(gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position);
930
931      vec_t perpendicularVector;
932      perpendicularVector.Cross(gContext.mRotationVectorSource, gContext.mTranslationPlan);
933      perpendicularVector.Normalize();
934      float acosAngle = Clamp(Dot(localPos, gContext.mRotationVectorSource), -0.9999f, 0.9999f);
935      float angle = acosf(acosAngle);
936      angle *= (Dot(localPos, perpendicularVector) < 0.f) ? 1.f : -1.f;
937      return angle;
938   }
939
940   static void DrawRotationGizmo(int type)
941   {
942      ImDrawList* drawList = gContext.mDrawList;
943
944      // colors
945      ImU32 colors[7];
946      ComputeColors(colors, type, ROTATE);
947
948     vec_t cameraToModelNormalized;
949     if (gContext.mIsOrthographic)
950     {
951        matrix_t viewInverse;
952        viewInverse.Inverse(*(matrix_t*)&gContext.mViewMat);
953        cameraToModelNormalized = viewInverse.v.dir;
954     }
955     else
956     {
957        cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye);
958     }
959
960      cameraToModelNormalized.TransformVector(gContext.mModelInverse);
961
962      gContext.mRadiusSquareCenter = screenRotateSize * gContext.mHeight;
963
964     for (int axis = 0; axis < 3; axis++)
965      {
966         ImVec2 circlePos[halfCircleSegmentCount];
967
968         float angleStart = atan2f(cameraToModelNormalized[(4-axis)%3], cameraToModelNormalized[(3 - axis) % 3]) + ZPI * 0.5f;
969
970         for (unsigned int i = 0; i < halfCircleSegmentCount; i++)
971         {
972            float ng = angleStart + ZPI * ((float)i / (float)halfCircleSegmentCount);
973            vec_t axisPos = makeVect(cosf(ng), sinf(ng), 0.f);
974            vec_t pos = makeVect(axisPos[axis], axisPos[(axis+1)%3], axisPos[(axis+2)%3]) * gContext.mScreenFactor;
975            circlePos[i] = worldToPos(pos, gContext.mMVP);
976         }
977
978         float radiusAxis = sqrtf( (ImLengthSqr(worldToPos(gContext.mModel.v.position, gContext.mViewProjection) - circlePos[0]) ));
979         if(radiusAxis > gContext.mRadiusSquareCenter)
980           gContext.mRadiusSquareCenter = radiusAxis;
981
982         drawList->AddPolyline(circlePos, halfCircleSegmentCount, colors[3 - axis], false, 2);
983      }
984      drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), gContext.mRadiusSquareCenter, colors[0], 64, 3.f);
985
986      if (gContext.mbUsing)
987      {
988         ImVec2 circlePos[halfCircleSegmentCount +1];
989
990         circlePos[0] = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
991         for (unsigned int i = 1; i < halfCircleSegmentCount; i++)
992         {
993            float ng = gContext.mRotationAngle * ((float)(i-1) / (float)(halfCircleSegmentCount -1));
994            matrix_t rotateVectorMatrix;
995            rotateVectorMatrix.RotationAxis(gContext.mTranslationPlan, ng);
996            vec_t pos;
997            pos.TransformPoint(gContext.mRotationVectorSource, rotateVectorMatrix);
998            pos *= gContext.mScreenFactor;
999            circlePos[i] = worldToPos(pos + gContext.mModel.v.position, gContext.mViewProjection);
1000         }
1001         drawList->AddConvexPolyFilled(circlePos, halfCircleSegmentCount, 0x801080FF);
1002         drawList->AddPolyline(circlePos, halfCircleSegmentCount, 0xFF1080FF, true, 2);
1003
1004         ImVec2 destinationPosOnScreen = circlePos[1];
1005         char tmps[512];
1006         ImFormatString(tmps, sizeof(tmps), rotationInfoMask[type - ROTATE_X], (gContext.mRotationAngle/ZPI)*180.f, gContext.mRotationAngle);
1007         drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps);
1008         drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps);
1009      }
1010   }
1011
1012   static void DrawHatchedAxis(const vec_t& axis)
1013   {
1014      for (int j = 1; j < 10; j++)
1015      {
1016         ImVec2 baseSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2) * gContext.mScreenFactor, gContext.mMVP);
1017         ImVec2 worldDirSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2 + 1) * gContext.mScreenFactor, gContext.mMVP);
1018         gContext.mDrawList->AddLine(baseSSpace2, worldDirSSpace2, 0x80000000, 6.f);
1019      }
1020   }
1021
1022   static void DrawScaleGizmo(int type)
1023   {
1024      ImDrawList* drawList = gContext.mDrawList;
1025
1026      // colors
1027      ImU32 colors[7];
1028      ComputeColors(colors, type, SCALE);
1029
1030      // draw
1031      vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f };
1032
1033      if (gContext.mbUsing)
1034         scaleDisplay = gContext.mScale;
1035
1036      for (unsigned int i = 0; i < 3; i++)
1037      {
1038        vec_t dirPlaneX, dirPlaneY, dirAxis;
1039         bool belowAxisLimit, belowPlaneLimit;
1040         ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
1041
1042         // draw axis
1043         if (belowAxisLimit)
1044         {
1045            ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP);
1046            ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * gContext.mScreenFactor, gContext.mMVP);
1047            ImVec2 worldDirSSpace = worldToPos((dirAxis * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVP);
1048
1049            if (gContext.mbUsing)
1050            {
1051               drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, 0xFF404040, 3.f);
1052               drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, 0xFF404040);
1053            }
1054
1055            drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f);
1056            drawList->AddCircleFilled(worldDirSSpace, 6.f, colors[i + 1]);
1057
1058            if (gContext.mAxisFactor[i] < 0.f)
1059               DrawHatchedAxis(dirAxis * scaleDisplay[i]);
1060         }
1061      }
1062
1063      // draw screen cirle
1064      drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32);
1065
1066      if (gContext.mbUsing)
1067      {
1068         //ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection);
1069         ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
1070         /*vec_t dif(destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y);
1071         dif.Normalize();
1072         dif *= 5.f;
1073         drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor);
1074         drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor);
1075         drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f);
1076         */
1077         char tmps[512];
1078         //vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin;
1079         int componentInfoIndex = (type - SCALE_X) * 3;
1080         ImFormatString(tmps, sizeof(tmps), scaleInfoMask[type - SCALE_X], scaleDisplay[translationInfoIndex[componentInfoIndex]]);
1081         drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps);
1082         drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps);
1083      }
1084   }
1085
1086
1087   static void DrawTranslationGizmo(int type)
1088   {
1089      ImDrawList* drawList = gContext.mDrawList;
1090      if (!drawList)
1091          return;
1092
1093      // colors
1094      ImU32 colors[7];
1095      ComputeColors(colors, type, TRANSLATE);
1096
1097      const ImVec2 origin = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
1098
1099      // draw
1100      bool belowAxisLimit = false;
1101      bool belowPlaneLimit = false;
1102      for (unsigned int i = 0; i < 3; ++i)
1103      {
1104         vec_t dirPlaneX, dirPlaneY, dirAxis;
1105         ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
1106
1107         // draw axis
1108         if (belowAxisLimit)
1109         {
1110            ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP);
1111            ImVec2 worldDirSSpace = worldToPos(dirAxis * gContext.mScreenFactor, gContext.mMVP);
1112
1113            drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f);
1114
1115            // Arrow head begin
1116            ImVec2 dir(origin - worldDirSSpace);
1117
1118            float d = sqrtf(ImLengthSqr(dir));
1119            dir /= d; // Normalize
1120            dir *= 6.0f;
1121
1122            ImVec2 ortogonalDir(dir.y, -dir.x); // Perpendicular vector
1123            ImVec2 a(worldDirSSpace + dir);
1124            drawList->AddTriangleFilled(worldDirSSpace - dir, a + ortogonalDir, a - ortogonalDir, colors[i + 1]);
1125            // Arrow head end
1126
1127            if (gContext.mAxisFactor[i] < 0.f)
1128               DrawHatchedAxis(dirAxis);
1129         }
1130
1131         // draw plane
1132         if (belowPlaneLimit)
1133         {
1134            ImVec2 screenQuadPts[4];
1135            for (int j = 0; j < 4; ++j)
1136            {
1137               vec_t cornerWorldPos = (dirPlaneX * quadUV[j * 2] + dirPlaneY  * quadUV[j * 2 + 1]) * gContext.mScreenFactor;
1138               screenQuadPts[j] = worldToPos(cornerWorldPos, gContext.mMVP);
1139            }
1140            drawList->AddPolyline(screenQuadPts, 4, directionColor[i], true, 1.0f);
1141            drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4]);
1142         }
1143      }
1144
1145      drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32);
1146
1147      if (gContext.mbUsing)
1148      {
1149         ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection);
1150         ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
1151         vec_t dif = { destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y, 0.f, 0.f };
1152         dif.Normalize();
1153         dif *= 5.f;
1154         drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor);
1155         drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor);
1156         drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f);
1157
1158         char tmps[512];
1159         vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin;
1160         int componentInfoIndex = (type - MOVE_X) * 3;
1161         ImFormatString(tmps, sizeof(tmps), translationInfoMask[type - MOVE_X], deltaInfo[translationInfoIndex[componentInfoIndex]], deltaInfo[translationInfoIndex[componentInfoIndex + 1]], deltaInfo[translationInfoIndex[componentInfoIndex + 2]]);
1162         drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps);
1163         drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps);
1164      }
1165   }
1166
1167   static bool CanActivate()
1168   {
1169      if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && !ImGui::IsAnyItemActive())
1170         return true;
1171      return false;
1172   }
1173
1174   static void HandleAndDrawLocalBounds(float *bounds, matrix_t *matrix, float *snapValues, OPERATION operation)
1175   {
1176       ImGuiIO& io = ImGui::GetIO();
1177       ImDrawList* drawList = gContext.mDrawList;
1178
1179       // compute best projection axis
1180       vec_t axesWorldDirections[3];
1181       vec_t bestAxisWorldDirection = { 0.0f, 0.0f, 0.0f, 0.0f };
1182       int axes[3];
1183       unsigned int numAxes = 1;
1184       axes[0] = gContext.mBoundsBestAxis;
1185       int bestAxis = axes[0];
1186       if (!gContext.mbUsingBounds)
1187       {
1188           numAxes = 0;
1189           float bestDot = 0.f;
1190           for (unsigned int i = 0; i < 3; i++)
1191           {
1192               vec_t dirPlaneNormalWorld;
1193               dirPlaneNormalWorld.TransformVector(directionUnary[i], gContext.mModelSource);
1194               dirPlaneNormalWorld.Normalize();
1195
1196               float dt = fabsf( Dot(Normalized(gContext.mCameraEye - gContext.mModelSource.v.position), dirPlaneNormalWorld) );
1197               if ( dt >= bestDot )
1198               {
1199                   bestDot = dt;
1200                   bestAxis = i;
1201                   bestAxisWorldDirection = dirPlaneNormalWorld;
1202               }
1203
1204               if( dt >= 0.1f )
1205               {
1206                   axes[numAxes] = i;
1207                   axesWorldDirections[numAxes] = dirPlaneNormalWorld;
1208                   ++numAxes;
1209               }
1210           }
1211       }
1212
1213       if( numAxes == 0 )
1214       {
1215            axes[0] = bestAxis;
1216            axesWorldDirections[0] = bestAxisWorldDirection;
1217            numAxes = 1;
1218       }
1219       else if( bestAxis != axes[0] )
1220       {
1221          unsigned int bestIndex = 0;
1222          for (unsigned int i = 0; i < numAxes; i++)
1223          {
1224              if( axes[i] == bestAxis )
1225              {
1226                  bestIndex = i;
1227                  break;
1228              }
1229          }
1230          int tempAxis = axes[0];
1231          axes[0] = axes[bestIndex];
1232          axes[bestIndex] = tempAxis;
1233          vec_t tempDirection = axesWorldDirections[0];
1234          axesWorldDirections[0] = axesWorldDirections[bestIndex];
1235          axesWorldDirections[bestIndex] = tempDirection;
1236       }
1237
1238       for (unsigned int axisIndex = 0; axisIndex < numAxes; ++axisIndex)
1239       {
1240           bestAxis = axes[axisIndex];
1241           bestAxisWorldDirection = axesWorldDirections[axisIndex];
1242
1243           // corners
1244           vec_t aabb[4];
1245
1246           int secondAxis = (bestAxis + 1) % 3;
1247           int thirdAxis = (bestAxis + 2) % 3;
1248
1249           for (int i = 0; i < 4; i++)
1250           {
1251               aabb[i][3] = aabb[i][bestAxis] = 0.f;
1252               aabb[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)];
1253               aabb[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))];
1254           }
1255
1256           // draw bounds
1257           unsigned int anchorAlpha = gContext.mbEnable ? 0xFF000000 : 0x80000000;
1258
1259           matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection;
1260           for (int i = 0; i < 4;i++)
1261           {
1262               ImVec2 worldBound1 = worldToPos(aabb[i], boundsMVP);
1263               ImVec2 worldBound2 = worldToPos(aabb[(i+1)%4], boundsMVP);
1264               if( !IsInContextRect( worldBound1 ) || !IsInContextRect( worldBound2 ) )
1265               {
1266                   continue;
1267               }
1268               float boundDistance = sqrtf(ImLengthSqr(worldBound1 - worldBound2));
1269               int stepCount = (int)(boundDistance / 10.f);
1270               stepCount = min( stepCount, 1000 );
1271               float stepLength = 1.f / (float)stepCount;
1272               for (int j = 0; j < stepCount; j++)
1273               {
1274                   float t1 = (float)j * stepLength;
1275                   float t2 = (float)j * stepLength + stepLength * 0.5f;
1276                   ImVec2 worldBoundSS1 = ImLerp(worldBound1, worldBound2, ImVec2(t1, t1));
1277                   ImVec2 worldBoundSS2 = ImLerp(worldBound1, worldBound2, ImVec2(t2, t2));
1278                   //drawList->AddLine(worldBoundSS1, worldBoundSS2, 0x000000 + anchorAlpha, 3.f);
1279               drawList->AddLine(worldBoundSS1, worldBoundSS2, 0xAAAAAA + anchorAlpha, 2.f);
1280               }
1281               vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4] ) * 0.5f;
1282               ImVec2 midBound = worldToPos(midPoint, boundsMVP);
1283               static const float AnchorBigRadius = 8.f;
1284               static const float AnchorSmallRadius = 6.f;
1285               bool overBigAnchor = ImLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius);
1286               bool overSmallAnchor = ImLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius);
1287
1288            int type = NONE;
1289            vec_t gizmoHitProportion;
1290
1291            switch (operation)
1292            {
1293            case TRANSLATE: type = GetMoveType(&gizmoHitProportion); break;
1294            case ROTATE: type = GetRotateType(); break;
1295            case SCALE: type = GetScaleType(); break;
1296            case BOUNDS: break;
1297            }
1298            if (type != NONE)
1299            {
1300               overBigAnchor = false;
1301               overSmallAnchor = false;
1302            }
1303
1304
1305               unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (0xAAAAAA + anchorAlpha);
1306               unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (0xAAAAAA + anchorAlpha);
1307
1308               drawList->AddCircleFilled(worldBound1, AnchorBigRadius, 0xFF000000);
1309            drawList->AddCircleFilled(worldBound1, AnchorBigRadius-1.2f, bigAnchorColor);
1310
1311               drawList->AddCircleFilled(midBound, AnchorSmallRadius, 0xFF000000);
1312            drawList->AddCircleFilled(midBound, AnchorSmallRadius-1.2f, smallAnchorColor);
1313               int oppositeIndex = (i + 2) % 4;
1314               // big anchor on corners
1315               if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && CanActivate())
1316               {
1317                   gContext.mBoundsPivot.TransformPoint(aabb[(i + 2) % 4], gContext.mModelSource);
1318                   gContext.mBoundsAnchor.TransformPoint(aabb[i], gContext.mModelSource);
1319                   gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
1320                   gContext.mBoundsBestAxis = bestAxis;
1321                   gContext.mBoundsAxis[0] = secondAxis;
1322                   gContext.mBoundsAxis[1] = thirdAxis;
1323
1324                   gContext.mBoundsLocalPivot.Set(0.f);
1325                   gContext.mBoundsLocalPivot[secondAxis] = aabb[oppositeIndex][secondAxis];
1326                   gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis];
1327
1328                   gContext.mbUsingBounds = true;
1329                   gContext.mBoundsMatrix = gContext.mModelSource;
1330               }
1331               // small anchor on middle of segment
1332               if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && CanActivate())
1333               {
1334                   vec_t midPointOpposite = (aabb[(i + 2) % 4] + aabb[(i + 3) % 4]) * 0.5f;
1335                   gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource);
1336                   gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource);
1337                   gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
1338                   gContext.mBoundsBestAxis = bestAxis;
1339                   int indices[] = { secondAxis , thirdAxis };
1340                   gContext.mBoundsAxis[0] = indices[i%2];
1341                   gContext.mBoundsAxis[1] = -1;
1342
1343                   gContext.mBoundsLocalPivot.Set(0.f);
1344                   gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f);
1345
1346                   gContext.mbUsingBounds = true;
1347                   gContext.mBoundsMatrix = gContext.mModelSource;
1348               }
1349           }
1350
1351           if (gContext.mbUsingBounds)
1352           {
1353               matrix_t scale;
1354               scale.SetToIdentity();
1355
1356               // compute projected mouse position on plan
1357               const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mBoundsPlan);
1358               vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
1359
1360               // compute a reference and delta vectors base on mouse move
1361               vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs();
1362               vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs();
1363
1364               // for 1 or 2 axes, compute a ratio that's used for scale and snap it based on resulting length
1365               for (int i = 0; i < 2; i++)
1366               {
1367                   int axisIndex1 = gContext.mBoundsAxis[i];
1368                   if (axisIndex1 == -1)
1369                       continue;
1370
1371                   float ratioAxis = 1.f;
1372                   vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex1].Abs();
1373
1374                   float dtAxis = axisDir.Dot(referenceVector);
1375                   float boundSize = bounds[axisIndex1 + 3] - bounds[axisIndex1];
1376                   if (dtAxis > FLT_EPSILON)
1377                       ratioAxis = axisDir.Dot(deltaVector) / dtAxis;
1378
1379                   if (snapValues)
1380                   {
1381                       float length = boundSize * ratioAxis;
1382                       ComputeSnap(&length, snapValues[axisIndex1]);
1383                       if (boundSize > FLT_EPSILON)
1384                           ratioAxis = length / boundSize;
1385                   }
1386                   scale.component[axisIndex1] *= ratioAxis;
1387               }
1388
1389               // transform matrix
1390               matrix_t preScale, postScale;
1391               preScale.Translation(-gContext.mBoundsLocalPivot);
1392               postScale.Translation(gContext.mBoundsLocalPivot);
1393               matrix_t res = preScale * scale * postScale * gContext.mBoundsMatrix;
1394               *matrix = res;
1395
1396               // info text
1397               char tmps[512];
1398               ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
1399               ImFormatString(tmps, sizeof(tmps), "X: %.2f Y: %.2f Z:%.2f"
1400                   , (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length() * scale.component[0].Length()
1401                   , (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length() * scale.component[1].Length()
1402                   , (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length() * scale.component[2].Length()
1403               );
1404               drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps);
1405               drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps);
1406            }
1407
1408           if (!io.MouseDown[0])
1409               gContext.mbUsingBounds = false;
1410
1411           if( gContext.mbUsingBounds )
1412               break;
1413       }
1414   }
1415
1416   ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1417   //
1418
1419   static int GetScaleType()
1420   {
1421      ImGuiIO& io = ImGui::GetIO();
1422      int type = NONE;
1423
1424      // screen
1425      if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x &&
1426         io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y)
1427         type = SCALE_XYZ;
1428
1429      // compute
1430      for (unsigned int i = 0; i < 3 && type == NONE; i++)
1431      {
1432         vec_t dirPlaneX, dirPlaneY, dirAxis;
1433         bool belowAxisLimit, belowPlaneLimit;
1434         ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
1435
1436         dirAxis.TransformVector(gContext.mModel);
1437       const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, dirAxis));
1438       vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len;
1439
1440       const ImVec2 posOnPlanScreen = worldToPos(posOnPlan, gContext.mViewProjection);
1441       const ImVec2 axisStartOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor * 0.1f, gContext.mViewProjection);
1442       const ImVec2 axisEndOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor, gContext.mViewProjection);
1443
1444       vec_t closestPointOnAxis = PointOnSegment(makeVect(posOnPlanScreen), makeVect(axisStartOnScreen), makeVect(axisEndOnScreen));
1445
1446       if ((closestPointOnAxis - makeVect(posOnPlanScreen)).Length() < 12.f) // pixel size
1447          type = SCALE_X + i;
1448      }
1449      return type;
1450   }
1451
1452   static int GetRotateType()
1453   {
1454      ImGuiIO& io = ImGui::GetIO();
1455      int type = NONE;
1456
1457      vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f };
1458      float dist = deltaScreen.Length();
1459      if (dist >= (gContext.mRadiusSquareCenter - 1.0f) && dist < (gContext.mRadiusSquareCenter + 1.0f))
1460         type = ROTATE_SCREEN;
1461
1462      const vec_t planNormals[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir};
1463
1464      for (unsigned int i = 0; i < 3 && type == NONE; i++)
1465      {
1466         // pickup plan
1467         vec_t pickupPlan = BuildPlan(gContext.mModel.v.position, planNormals[i]);
1468
1469         const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, pickupPlan);
1470         vec_t localPos = gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position;
1471
1472         if (Dot(Normalized(localPos), gContext.mRayVector) > FLT_EPSILON)
1473            continue;
1474       vec_t idealPosOnCircle = Normalized(localPos);
1475       idealPosOnCircle.TransformVector(gContext.mModelInverse);
1476       ImVec2 idealPosOnCircleScreen = worldToPos(idealPosOnCircle * gContext.mScreenFactor, gContext.mMVP);
1477
1478       //gContext.mDrawList->AddCircle(idealPosOnCircleScreen, 5.f, 0xFFFFFFFF);
1479       ImVec2 distanceOnScreen = idealPosOnCircleScreen - io.MousePos;
1480
1481         float distance = makeVect(distanceOnScreen).Length();
1482         if (distance < 8.f) // pixel size
1483            type = ROTATE_X + i;
1484      }
1485
1486      return type;
1487   }
1488
1489   static int GetMoveType(vec_t *gizmoHitProportion)
1490   {
1491      ImGuiIO& io = ImGui::GetIO();
1492      int type = NONE;
1493
1494      // screen
1495      if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x &&
1496         io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y)
1497         type = MOVE_SCREEN;
1498
1499      // compute
1500      for (unsigned int i = 0; i < 3 && type == NONE; i++)
1501      {
1502         vec_t dirPlaneX, dirPlaneY, dirAxis;
1503         bool belowAxisLimit, belowPlaneLimit;
1504         ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
1505       dirAxis.TransformVector(gContext.mModel);
1506         dirPlaneX.TransformVector(gContext.mModel);
1507         dirPlaneY.TransformVector(gContext.mModel);
1508
1509         const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, dirAxis));
1510         vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len;
1511
1512       const ImVec2 posOnPlanScreen = worldToPos(posOnPlan, gContext.mViewProjection);
1513       const ImVec2 axisStartOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor * 0.1f, gContext.mViewProjection);
1514       const ImVec2 axisEndOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor, gContext.mViewProjection);
1515
1516       vec_t closestPointOnAxis = PointOnSegment(makeVect(posOnPlanScreen), makeVect(axisStartOnScreen), makeVect(axisEndOnScreen));
1517
1518       if ((closestPointOnAxis - makeVect(posOnPlanScreen)).Length() < 12.f) // pixel size
1519            type = MOVE_X + i;
1520
1521       const float dx = dirPlaneX.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor));
1522       const float dy = dirPlaneY.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor));
1523         if (belowPlaneLimit && dx >= quadUV[0] && dx <= quadUV[4] && dy >= quadUV[1] && dy <= quadUV[3])
1524            type = MOVE_YZ + i;
1525
1526         if (gizmoHitProportion)
1527            *gizmoHitProportion = makeVect(dx, dy, 0.f);
1528      }
1529      return type;
1530   }
1531
1532   static void HandleTranslation(float *matrix, float *deltaMatrix, int& type, float *snap)
1533   {
1534      ImGuiIO& io = ImGui::GetIO();
1535      bool applyRotationLocaly = gContext.mMode == LOCAL || type == MOVE_SCREEN;
1536
1537      // move
1538      if (gContext.mbUsing)
1539      {
1540         ImGui::CaptureMouseFromApp();
1541         const float len = fabsf(IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan)); // near plan
1542         vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
1543
1544
1545
1546         // compute delta
1547         vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor;
1548         vec_t delta = newOrigin - gContext.mModel.v.position;
1549
1550         // 1 axis constraint
1551         if (gContext.mCurrentOperation >= MOVE_X && gContext.mCurrentOperation <= MOVE_Z)
1552         {
1553            int axisIndex = gContext.mCurrentOperation - MOVE_X;
1554            const vec_t& axisValue = *(vec_t*)&gContext.mModel.m[axisIndex];
1555            float lengthOnAxis = Dot(axisValue, delta);
1556            delta = axisValue * lengthOnAxis;
1557         }
1558
1559         // snap
1560         if (snap)
1561         {
1562            vec_t cumulativeDelta = gContext.mModel.v.position + delta - gContext.mMatrixOrigin;
1563            if (applyRotationLocaly)
1564            {
1565               matrix_t modelSourceNormalized = gContext.mModelSource;
1566               modelSourceNormalized.OrthoNormalize();
1567               matrix_t modelSourceNormalizedInverse;
1568               modelSourceNormalizedInverse.Inverse(modelSourceNormalized);
1569               cumulativeDelta.TransformVector(modelSourceNormalizedInverse);
1570               ComputeSnap(cumulativeDelta, snap);
1571               cumulativeDelta.TransformVector(modelSourceNormalized);
1572            }
1573            else
1574            {
1575               ComputeSnap(cumulativeDelta, snap);
1576            }
1577            delta = gContext.mMatrixOrigin + cumulativeDelta - gContext.mModel.v.position;
1578
1579         }
1580
1581         // compute matrix & delta
1582         matrix_t deltaMatrixTranslation;
1583         deltaMatrixTranslation.Translation(delta);
1584         if (deltaMatrix)
1585            memcpy(deltaMatrix, deltaMatrixTranslation.m16, sizeof(float) * 16);
1586
1587
1588         matrix_t res = gContext.mModelSource * deltaMatrixTranslation;
1589         *(matrix_t*)matrix = res;
1590
1591         if (!io.MouseDown[0])
1592            gContext.mbUsing = false;
1593
1594         type = gContext.mCurrentOperation;
1595      }
1596      else
1597      {
1598         // find new possible way to move
1599         vec_t gizmoHitProportion;
1600         type = GetMoveType(&gizmoHitProportion);
1601         if(type != NONE)
1602         {
1603            ImGui::CaptureMouseFromApp();
1604         }
1605       if (CanActivate() && type != NONE)
1606       {
1607          gContext.mbUsing = true;
1608          gContext.mCurrentOperation = type;
1609          vec_t movePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir,
1610             gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir,
1611             -gContext.mCameraDir };
1612
1613          vec_t cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye);
1614          for (unsigned int i = 0; i < 3; i++)
1615          {
1616             vec_t orthoVector = Cross(movePlanNormal[i], cameraToModelNormalized);
1617             movePlanNormal[i].Cross(orthoVector);
1618             movePlanNormal[i].Normalize();
1619          }
1620            // pickup plan
1621            gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MOVE_X]);
1622            const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
1623            gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len;
1624            gContext.mMatrixOrigin = gContext.mModel.v.position;
1625
1626            gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor);
1627         }
1628      }
1629   }
1630
1631   static void HandleScale(float *matrix, float *deltaMatrix, int& type, float *snap)
1632   {
1633      ImGuiIO& io = ImGui::GetIO();
1634
1635      if (!gContext.mbUsing)
1636      {
1637         // find new possible way to scale
1638         type = GetScaleType();
1639         if(type != NONE)
1640         {
1641            ImGui::CaptureMouseFromApp();
1642         }
1643         if (CanActivate() && type != NONE)
1644         {
1645            gContext.mbUsing = true;
1646            gContext.mCurrentOperation = type;
1647            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 };
1648            // pickup plan
1649
1650            gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - SCALE_X]);
1651            const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
1652            gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len;
1653            gContext.mMatrixOrigin = gContext.mModel.v.position;
1654            gContext.mScale.Set(1.f, 1.f, 1.f);
1655            gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor);
1656            gContext.mScaleValueOrigin = makeVect(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length());
1657            gContext.mSaveMousePosx = io.MousePos.x;
1658         }
1659      }
1660      // scale
1661      if (gContext.mbUsing)
1662      {
1663         ImGui::CaptureMouseFromApp();
1664         const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
1665         vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
1666         vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor;
1667         vec_t delta = newOrigin - gContext.mModel.v.position;
1668
1669         // 1 axis constraint
1670         if (gContext.mCurrentOperation >= SCALE_X && gContext.mCurrentOperation <= SCALE_Z)
1671         {
1672            int axisIndex = gContext.mCurrentOperation - SCALE_X;
1673            const vec_t& axisValue = *(vec_t*)&gContext.mModel.m[axisIndex];
1674            float lengthOnAxis = Dot(axisValue, delta);
1675            delta = axisValue * lengthOnAxis;
1676
1677            vec_t baseVector = gContext.mTranslationPlanOrigin - gContext.mModel.v.position;
1678            float ratio = Dot(axisValue, baseVector + delta) / Dot(axisValue, baseVector);
1679
1680            gContext.mScale[axisIndex] = max(ratio, 0.001f);
1681         }
1682         else
1683         {
1684            float scaleDelta = (io.MousePos.x - gContext.mSaveMousePosx)  * 0.01f;
1685            gContext.mScale.Set(max(1.f + scaleDelta, 0.001f));
1686         }
1687
1688         // snap
1689         if (snap)
1690         {
1691            float scaleSnap[] = { snap[0], snap[0], snap[0] };
1692            ComputeSnap(gContext.mScale, scaleSnap);
1693         }
1694
1695         // no 0 allowed
1696         for (int i = 0; i < 3;i++)
1697            gContext.mScale[i] = max(gContext.mScale[i], 0.001f);
1698
1699         // compute matrix & delta
1700         matrix_t deltaMatrixScale;
1701         deltaMatrixScale.Scale(gContext.mScale * gContext.mScaleValueOrigin);
1702
1703         matrix_t res = deltaMatrixScale * gContext.mModel;
1704         *(matrix_t*)matrix = res;
1705
1706         if (deltaMatrix)
1707         {
1708            deltaMatrixScale.Scale(gContext.mScale);
1709            memcpy(deltaMatrix, deltaMatrixScale.m16, sizeof(float) * 16);
1710         }
1711
1712         if (!io.MouseDown[0])
1713            gContext.mbUsing = false;
1714
1715         type = gContext.mCurrentOperation;
1716      }
1717   }
1718
1719   static void HandleRotation(float *matrix, float *deltaMatrix, int& type, float *snap)
1720   {
1721      ImGuiIO& io = ImGui::GetIO();
1722      bool applyRotationLocaly = gContext.mMode == LOCAL;
1723
1724      if (!gContext.mbUsing)
1725      {
1726         type = GetRotateType();
1727
1728         if(type != NONE)
1729         {
1730            ImGui::CaptureMouseFromApp();
1731         }
1732
1733         if (type == ROTATE_SCREEN)
1734         {
1735            applyRotationLocaly = true;
1736         }
1737
1738         if (CanActivate() && type != NONE)
1739         {
1740            gContext.mbUsing = true;
1741            gContext.mCurrentOperation = type;
1742            const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir };
1743            // pickup plan
1744            if (applyRotationLocaly)
1745            {
1746               gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, rotatePlanNormal[type - ROTATE_X]);
1747            }
1748            else
1749            {
1750               gContext.mTranslationPlan = BuildPlan(gContext.mModelSource.v.position, directionUnary[type - ROTATE_X]);
1751            }
1752
1753            const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
1754            vec_t localPos = gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position;
1755            gContext.mRotationVectorSource = Normalized(localPos);
1756            gContext.mRotationAngleOrigin = ComputeAngleOnPlan();
1757         }
1758      }
1759
1760      // rotation
1761      if (gContext.mbUsing)
1762      {
1763         ImGui::CaptureMouseFromApp();
1764         gContext.mRotationAngle = ComputeAngleOnPlan();
1765         if (snap)
1766         {
1767            float snapInRadian = snap[0] * DEG2RAD;
1768            ComputeSnap(&gContext.mRotationAngle, snapInRadian);
1769         }
1770         vec_t rotationAxisLocalSpace;
1771
1772         rotationAxisLocalSpace.TransformVector(makeVect(gContext.mTranslationPlan.x, gContext.mTranslationPlan.y, gContext.mTranslationPlan.z, 0.f), gContext.mModelInverse);
1773         rotationAxisLocalSpace.Normalize();
1774
1775         matrix_t deltaRotation;
1776         deltaRotation.RotationAxis(rotationAxisLocalSpace, gContext.mRotationAngle - gContext.mRotationAngleOrigin);
1777         gContext.mRotationAngleOrigin = gContext.mRotationAngle;
1778
1779         matrix_t scaleOrigin;
1780         scaleOrigin.Scale(gContext.mModelScaleOrigin);
1781
1782         if (applyRotationLocaly)
1783         {
1784            *(matrix_t*)matrix = scaleOrigin * deltaRotation * gContext.mModel;
1785         }
1786         else
1787         {
1788            matrix_t res = gContext.mModelSource;
1789            res.v.position.Set(0.f);
1790
1791            *(matrix_t*)matrix = res * deltaRotation;
1792            ((matrix_t*)matrix)->v.position = gContext.mModelSource.v.position;
1793         }
1794
1795         if (deltaMatrix)
1796         {
1797            *(matrix_t*)deltaMatrix = gContext.mModelInverse * deltaRotation * gContext.mModel;
1798         }
1799
1800         if (!io.MouseDown[0])
1801            gContext.mbUsing = false;
1802
1803         type = gContext.mCurrentOperation;
1804      }
1805   }
1806
1807   void DecomposeMatrixToComponents(const float *matrix, float *translation, float *rotation, float *scale)
1808   {
1809      matrix_t mat = *(matrix_t*)matrix;
1810
1811      scale[0] = mat.v.right.Length();
1812      scale[1] = mat.v.up.Length();
1813      scale[2] = mat.v.dir.Length();
1814
1815      mat.OrthoNormalize();
1816
1817      rotation[0] = RAD2DEG * atan2f(mat.m[1][2], mat.m[2][2]);
1818      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]));
1819      rotation[2] = RAD2DEG * atan2f(mat.m[0][1], mat.m[0][0]);
1820
1821      translation[0] = mat.v.position.x;
1822      translation[1] = mat.v.position.y;
1823      translation[2] = mat.v.position.z;
1824   }
1825
1826   void RecomposeMatrixFromComponents(const float *translation, const float *rotation, const float *scale, float *matrix)
1827   {
1828      matrix_t& mat = *(matrix_t*)matrix;
1829
1830      matrix_t rot[3];
1831      for (int i = 0; i < 3;i++)
1832         rot[i].RotationAxis(directionUnary[i], rotation[i] * DEG2RAD);
1833
1834      mat = rot[0] * rot[1] * rot[2];
1835
1836      float validScale[3];
1837      for (int i = 0; i < 3; i++)
1838      {
1839         if (fabsf(scale[i]) < FLT_EPSILON)
1840            validScale[i] = 0.001f;
1841         else
1842            validScale[i] = scale[i];
1843      }
1844      mat.v.right *= validScale[0];
1845      mat.v.up *= validScale[1];
1846      mat.v.dir *= validScale[2];
1847      mat.v.position.Set(translation[0], translation[1], translation[2], 1.f);
1848   }
1849
1850   void Manipulate(const float *view, const float *projection, OPERATION operation, MODE mode, float *matrix, float *deltaMatrix, float *snap, float *localBounds, float *boundsSnap)
1851   {
1852      ComputeContext(view, projection, matrix, mode);
1853
1854      // set delta to identity
1855      if (deltaMatrix)
1856         ((matrix_t*)deltaMatrix)->SetToIdentity();
1857
1858      // behind camera
1859      vec_t camSpacePosition;
1860      camSpacePosition.TransformPoint(makeVect(0.f, 0.f, 0.f), gContext.mMVP);
1861      if (!gContext.mIsOrthographic && camSpacePosition.z < 0.001f)
1862         return;
1863
1864      // --
1865      int type = NONE;
1866      if (gContext.mbEnable)
1867      {
1868          if (!gContext.mbUsingBounds)
1869          {
1870              switch (operation)
1871              {
1872              case ROTATE:
1873                  HandleRotation(matrix, deltaMatrix, type, snap);
1874                  break;
1875              case TRANSLATE:
1876                  HandleTranslation(matrix, deltaMatrix, type, snap);
1877                  break;
1878              case SCALE:
1879                  HandleScale(matrix, deltaMatrix, type, snap);
1880                  break;
1881              case BOUNDS:
1882                  break;
1883              }
1884          }
1885      }
1886
1887      if (localBounds && !gContext.mbUsing)
1888          HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap, operation);
1889
1890      if (!gContext.mbUsingBounds)
1891      {
1892          switch (operation)
1893          {
1894          case ROTATE:
1895              DrawRotationGizmo(type);
1896              break;
1897          case TRANSLATE:
1898              DrawTranslationGizmo(type);
1899              break;
1900          case SCALE:
1901              DrawScaleGizmo(type);
1902              break;
1903          case BOUNDS:
1904              break;
1905          }
1906      }
1907   }
1908
1909   void DrawCube(const float *view, const float *projection, const float *matrix)
1910   {
1911      matrix_t viewInverse;
1912      viewInverse.Inverse(*(matrix_t*)view);
1913      const matrix_t& model = *(matrix_t*)matrix;
1914      matrix_t res = *(matrix_t*)matrix * *(matrix_t*)view * *(matrix_t*)projection;
1915
1916      for (int iFace = 0; iFace < 6; iFace++)
1917      {
1918         const int normalIndex = (iFace % 3);
1919         const int perpXIndex = (normalIndex + 1) % 3;
1920         const int perpYIndex = (normalIndex + 2) % 3;
1921         const float invert = (iFace > 2) ? -1.f : 1.f;
1922
1923         const vec_t faceCoords[4] = { directionUnary[normalIndex] + directionUnary[perpXIndex] + directionUnary[perpYIndex],
1924            directionUnary[normalIndex] + directionUnary[perpXIndex] - directionUnary[perpYIndex],
1925            directionUnary[normalIndex] - directionUnary[perpXIndex] - directionUnary[perpYIndex],
1926            directionUnary[normalIndex] - directionUnary[perpXIndex] + directionUnary[perpYIndex],
1927         };
1928
1929         // clipping
1930         bool skipFace = false;
1931         for (unsigned int iCoord = 0; iCoord < 4; iCoord++)
1932         {
1933            vec_t camSpacePosition;
1934            camSpacePosition.TransformPoint(faceCoords[iCoord] * 0.5f * invert, gContext.mMVP);
1935            if (camSpacePosition.z < 0.001f)
1936            {
1937               skipFace = true;
1938               break;
1939            }
1940         }
1941         if (skipFace)
1942            continue;
1943
1944         // 3D->2D
1945         ImVec2 faceCoordsScreen[4];
1946         for (unsigned int iCoord = 0; iCoord < 4; iCoord++)
1947            faceCoordsScreen[iCoord] = worldToPos(faceCoords[iCoord] * 0.5f * invert, res);
1948
1949         // back face culling
1950         vec_t cullPos, cullNormal;
1951         cullPos.TransformPoint(faceCoords[0] * 0.5f * invert, model);
1952         cullNormal.TransformVector(directionUnary[normalIndex] * invert, model);
1953         float dt = Dot(Normalized(cullPos - viewInverse.v.position), Normalized(cullNormal));
1954         if (dt>0.f)
1955            continue;
1956
1957         // draw face with lighter color
1958         gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, directionColor[normalIndex] | 0x808080);
1959      }
1960   }
1961
1962   void DrawGrid(const float *view, const float *projection, const float *matrix, const float gridSize)
1963   {
1964      matrix_t res = *(matrix_t*)matrix * *(matrix_t*)view * *(matrix_t*)projection;
1965
1966      for (float f = -gridSize; f <= gridSize; f += 1.f)
1967      {
1968         gContext.mDrawList->AddLine(worldToPos(makeVect(f, 0.f, -gridSize), res), worldToPos(makeVect(f, 0.f, gridSize), res), 0xFF808080);
1969         gContext.mDrawList->AddLine(worldToPos(makeVect(-gridSize, 0.f, f), res), worldToPos(makeVect(gridSize, 0.f, f), res), 0xFF808080);
1970      }
1971   }
1972};
1973
1974