PeriDyno 1.0.0
Loading...
Searching...
No Matches
ImGuizmo.cpp
Go to the documentation of this file.
1// https://github.com/CedricGuillemet/ImGuizmo
2// v 1.83
3//
4// The MIT License(MIT)
5//
6// Copyright(c) 2021 Cedric Guillemet
7//
8// Permission is hereby granted, free of charge, to any person obtaining a copy
9// of this software and associated documentation files(the "Software"), to deal
10// in the Software without restriction, including without limitation the rights
11// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
12// copies of the Software, and to permit persons to whom the Software is
13// furnished to do so, subject to the following conditions :
14//
15// The above copyright notice and this permission notice shall be included in all
16// copies or substantial portions of the Software.
17//
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
21// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24// SOFTWARE.
25//
26
27#define IMGUI_DEFINE_MATH_OPERATORS
28#include <imgui.h>
29#include <imgui_internal.h>
30#include "ImGuizmo.h"
31
32#if defined(_MSC_VER) || defined(__MINGW32__)
33#include <malloc.h>
34#endif
35#if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR)
36#define _malloca(x) alloca(x)
37#define _freea(x)
38#endif
39
40// includes patches for multiview from
41// https://github.com/CedricGuillemet/ImGuizmo/issues/15
42
43namespace ImGuizmo
44{
45 static const float ZPI = 3.14159265358979323846f;
46 static const float RAD2DEG = (180.f / ZPI);
47 static const float DEG2RAD = (ZPI / 180.f);
48 const float screenRotateSize = 0.06f;
49
51 {
52 return static_cast<OPERATION>(static_cast<int>(lhs) & static_cast<int>(rhs));
53 }
54
55 static bool operator!=(OPERATION lhs, int rhs)
56 {
57 return static_cast<int>(lhs) != rhs;
58 }
59
60 static bool operator==(OPERATION lhs, int rhs)
61 {
62 return static_cast<int>(lhs) == rhs;
63 }
64
65 static bool Intersects(OPERATION lhs, OPERATION rhs)
66 {
67 return (lhs & rhs) != 0;
68 }
69
70 // True if lhs contains rhs
71 static bool Contains(OPERATION lhs, OPERATION rhs)
72 {
73 return (lhs & rhs) == rhs;
74 }
75
77 // utility and math
78
79 void FPU_MatrixF_x_MatrixF(const float* a, const float* b, float* r)
80 {
81 r[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12];
82 r[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13];
83 r[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14];
84 r[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15];
85
86 r[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12];
87 r[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13];
88 r[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14];
89 r[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15];
90
91 r[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12];
92 r[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13];
93 r[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14];
94 r[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15];
95
96 r[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12];
97 r[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13];
98 r[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14];
99 r[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15];
100 }
101
102 void Frustum(float left, float right, float bottom, float top, float znear, float zfar, float* m16)
103 {
104 float temp, temp2, temp3, temp4;
105 temp = 2.0f * znear;
106 temp2 = right - left;
107 temp3 = top - bottom;
108 temp4 = zfar - znear;
109 m16[0] = temp / temp2;
110 m16[1] = 0.0;
111 m16[2] = 0.0;
112 m16[3] = 0.0;
113 m16[4] = 0.0;
114 m16[5] = temp / temp3;
115 m16[6] = 0.0;
116 m16[7] = 0.0;
117 m16[8] = (right + left) / temp2;
118 m16[9] = (top + bottom) / temp3;
119 m16[10] = (-zfar - znear) / temp4;
120 m16[11] = -1.0f;
121 m16[12] = 0.0;
122 m16[13] = 0.0;
123 m16[14] = (-temp * zfar) / temp4;
124 m16[15] = 0.0;
125 }
126
127 void Perspective(float fovyInDegrees, float aspectRatio, float znear, float zfar, float* m16)
128 {
129 float ymax, xmax;
130 ymax = znear * tanf(fovyInDegrees * DEG2RAD);
131 xmax = ymax * aspectRatio;
132 Frustum(-xmax, xmax, -ymax, ymax, znear, zfar, m16);
133 }
134
135 void Cross(const float* a, const float* b, float* r)
136 {
137 r[0] = a[1] * b[2] - a[2] * b[1];
138 r[1] = a[2] * b[0] - a[0] * b[2];
139 r[2] = a[0] * b[1] - a[1] * b[0];
140 }
141
142 float Dot(const float* a, const float* b)
143 {
144 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
145 }
146
147 void Normalize(const float* a, float* r)
148 {
149 float il = 1.f / (sqrtf(Dot(a, a)) + FLT_EPSILON);
150 r[0] = a[0] * il;
151 r[1] = a[1] * il;
152 r[2] = a[2] * il;
153 }
154
155 void LookAt(const float* eye, const float* at, const float* up, float* m16)
156 {
157 float X[3], Y[3], Z[3], tmp[3];
158
159 tmp[0] = eye[0] - at[0];
160 tmp[1] = eye[1] - at[1];
161 tmp[2] = eye[2] - at[2];
162 Normalize(tmp, Z);
163 Normalize(up, Y);
164 Cross(Y, Z, tmp);
165 Normalize(tmp, X);
166 Cross(Z, X, tmp);
167 Normalize(tmp, Y);
168
169 m16[0] = X[0];
170 m16[1] = Y[0];
171 m16[2] = Z[0];
172 m16[3] = 0.0f;
173 m16[4] = X[1];
174 m16[5] = Y[1];
175 m16[6] = Z[1];
176 m16[7] = 0.0f;
177 m16[8] = X[2];
178 m16[9] = Y[2];
179 m16[10] = Z[2];
180 m16[11] = 0.0f;
181 m16[12] = -Dot(X, eye);
182 m16[13] = -Dot(Y, eye);
183 m16[14] = -Dot(Z, eye);
184 m16[15] = 1.0f;
185 }
186
187 template <typename T> T Clamp(T x, T y, T z) { return ((x < y) ? y : ((x > z) ? z : x)); }
188 template <typename T> T max(T x, T y) { return (x > y) ? x : y; }
189 template <typename T> T min(T x, T y) { return (x < y) ? x : y; }
190 template <typename T> bool IsWithin(T x, T y, T z) { return (x >= y) && (x <= z); }
191
192 struct matrix_t;
193 struct vec_t
194 {
195 public:
196 float x, y, z, w;
197
198 void Lerp(const vec_t& v, float t)
199 {
200 x += (v.x - x) * t;
201 y += (v.y - y) * t;
202 z += (v.z - z) * t;
203 w += (v.w - w) * t;
204 }
205
206 void Set(float v) { x = y = z = w = v; }
207 void Set(float _x, float _y, float _z = 0.f, float _w = 0.f) { x = _x; y = _y; z = _z; w = _w; }
208
209 vec_t& operator -= (const vec_t& v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; }
210 vec_t& operator += (const vec_t& v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; }
211 vec_t& operator *= (const vec_t& v) { x *= v.x; y *= v.y; z *= v.z; w *= v.w; return *this; }
212 vec_t& operator *= (float v) { x *= v; y *= v; z *= v; w *= v; return *this; }
213
214 vec_t operator * (float f) const;
215 vec_t operator - () const;
216 vec_t operator - (const vec_t& v) const;
217 vec_t operator + (const vec_t& v) const;
218 vec_t operator * (const vec_t& v) const;
219
220 const vec_t& operator + () const { return (*this); }
221 float Length() const { return sqrtf(x * x + y * y + z * z); };
222 float LengthSq() const { return (x * x + y * y + z * z); };
223 vec_t Normalize() { (*this) *= (1.f / Length()); return (*this); }
224 vec_t Normalize(const vec_t& v) { this->Set(v.x, v.y, v.z, v.w); this->Normalize(); return (*this); }
225 vec_t Abs() const;
226
227 void Cross(const vec_t& v)
228 {
229 vec_t res;
230 res.x = y * v.z - z * v.y;
231 res.y = z * v.x - x * v.z;
232 res.z = x * v.y - y * v.x;
233
234 x = res.x;
235 y = res.y;
236 z = res.z;
237 w = 0.f;
238 }
239
240 void Cross(const vec_t& v1, const vec_t& v2)
241 {
242 x = v1.y * v2.z - v1.z * v2.y;
243 y = v1.z * v2.x - v1.x * v2.z;
244 z = v1.x * v2.y - v1.y * v2.x;
245 w = 0.f;
246 }
247
248 float Dot(const vec_t& v) const
249 {
250 return (x * v.x) + (y * v.y) + (z * v.z) + (w * v.w);
251 }
252
253 float Dot3(const vec_t& v) const
254 {
255 return (x * v.x) + (y * v.y) + (z * v.z);
256 }
257
258 void Transform(const matrix_t& matrix);
259 void Transform(const vec_t& s, const matrix_t& matrix);
260
261 void TransformVector(const matrix_t& matrix);
262 void TransformPoint(const matrix_t& matrix);
263 void TransformVector(const vec_t& v, const matrix_t& matrix) { (*this) = v; this->TransformVector(matrix); }
264 void TransformPoint(const vec_t& v, const matrix_t& matrix) { (*this) = v; this->TransformPoint(matrix); }
265
266 float& operator [] (size_t index) { return ((float*)&x)[index]; }
267 const float& operator [] (size_t index) const { return ((float*)&x)[index]; }
268 bool operator!=(const vec_t& other) const { return memcmp(this, &other, sizeof(vec_t)); }
269 };
270
271 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; }
272 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; }
273 vec_t vec_t::operator * (float f) const { return makeVect(x * f, y * f, z * f, w * f); }
274 vec_t vec_t::operator - () const { return makeVect(-x, -y, -z, -w); }
275 vec_t vec_t::operator - (const vec_t& v) const { return makeVect(x - v.x, y - v.y, z - v.z, w - v.w); }
276 vec_t vec_t::operator + (const vec_t& v) const { return makeVect(x + v.x, y + v.y, z + v.z, w + v.w); }
277 vec_t vec_t::operator * (const vec_t& v) const { return makeVect(x * v.x, y * v.y, z * v.z, w * v.w); }
278 vec_t vec_t::Abs() const { return makeVect(fabsf(x), fabsf(y), fabsf(z)); }
279
280 vec_t Normalized(const vec_t& v) { vec_t res; res = v; res.Normalize(); return res; }
281 vec_t Cross(const vec_t& v1, const vec_t& v2)
282 {
283 vec_t res;
284 res.x = v1.y * v2.z - v1.z * v2.y;
285 res.y = v1.z * v2.x - v1.x * v2.z;
286 res.z = v1.x * v2.y - v1.y * v2.x;
287 res.w = 0.f;
288 return res;
289 }
290
291 float Dot(const vec_t& v1, const vec_t& v2)
292 {
293 return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z);
294 }
295
296 vec_t BuildPlan(const vec_t& p_point1, const vec_t& p_normal)
297 {
298 vec_t normal, res;
299 normal.Normalize(p_normal);
300 res.w = normal.Dot(p_point1);
301 res.x = normal.x;
302 res.y = normal.y;
303 res.z = normal.z;
304 return res;
305 }
306
307 struct matrix_t
308 {
309 public:
310
311 union
312 {
313 float m[4][4];
314 float m16[16];
315 struct
316 {
318 } v;
320 };
321
322 matrix_t(const matrix_t& other) { memcpy(&m16[0], &other.m16[0], sizeof(float) * 16); }
324
325 operator float* () { return m16; }
326 operator const float* () const { return m16; }
327 void Translation(float _x, float _y, float _z) { this->Translation(makeVect(_x, _y, _z)); }
328
329 void Translation(const vec_t& vt)
330 {
331 v.right.Set(1.f, 0.f, 0.f, 0.f);
332 v.up.Set(0.f, 1.f, 0.f, 0.f);
333 v.dir.Set(0.f, 0.f, 1.f, 0.f);
334 v.position.Set(vt.x, vt.y, vt.z, 1.f);
335 }
336
337 void Scale(float _x, float _y, float _z)
338 {
339 v.right.Set(_x, 0.f, 0.f, 0.f);
340 v.up.Set(0.f, _y, 0.f, 0.f);
341 v.dir.Set(0.f, 0.f, _z, 0.f);
342 v.position.Set(0.f, 0.f, 0.f, 1.f);
343 }
344 void Scale(const vec_t& s) { Scale(s.x, s.y, s.z); }
345
347 {
348 matrix_t tmpMat;
349 tmpMat = *this;
350 tmpMat.Multiply(mat);
351 *this = tmpMat;
352 return *this;
353 }
354 matrix_t operator * (const matrix_t& mat) const
355 {
356 matrix_t matT;
357 matT.Multiply(*this, mat);
358 return matT;
359 }
360
361 void Multiply(const matrix_t& matrix)
362 {
363 matrix_t tmp;
364 tmp = *this;
365
366 FPU_MatrixF_x_MatrixF((float*)&tmp, (float*)&matrix, (float*)this);
367 }
368
369 void Multiply(const matrix_t& m1, const matrix_t& m2)
370 {
371 FPU_MatrixF_x_MatrixF((float*)&m1, (float*)&m2, (float*)this);
372 }
373
374 float GetDeterminant() const
375 {
376 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] -
377 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];
378 }
379
380 float Inverse(const matrix_t& srcMatrix, bool affine = false);
382 {
383 v.right.Set(1.f, 0.f, 0.f, 0.f);
384 v.up.Set(0.f, 1.f, 0.f, 0.f);
385 v.dir.Set(0.f, 0.f, 1.f, 0.f);
386 v.position.Set(0.f, 0.f, 0.f, 1.f);
387 }
389 {
390 matrix_t tmpm;
391 for (int l = 0; l < 4; l++)
392 {
393 for (int c = 0; c < 4; c++)
394 {
395 tmpm.m[l][c] = m[c][l];
396 }
397 }
398 (*this) = tmpm;
399 }
400
401 void RotationAxis(const vec_t& axis, float angle);
402
404 {
405 v.right.Normalize();
406 v.up.Normalize();
407 v.dir.Normalize();
408 }
409 };
410
411 void vec_t::Transform(const matrix_t& matrix)
412 {
413 vec_t out;
414
415 out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + w * matrix.m[3][0];
416 out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + w * matrix.m[3][1];
417 out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + w * matrix.m[3][2];
418 out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + w * matrix.m[3][3];
419
420 x = out.x;
421 y = out.y;
422 z = out.z;
423 w = out.w;
424 }
425
426 void vec_t::Transform(const vec_t& s, const matrix_t& matrix)
427 {
428 *this = s;
429 Transform(matrix);
430 }
431
432 void vec_t::TransformPoint(const matrix_t& matrix)
433 {
434 vec_t out;
435
436 out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + matrix.m[3][0];
437 out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + matrix.m[3][1];
438 out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + matrix.m[3][2];
439 out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + matrix.m[3][3];
440
441 x = out.x;
442 y = out.y;
443 z = out.z;
444 w = out.w;
445 }
446
448 {
449 vec_t out;
450
451 out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0];
452 out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1];
453 out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2];
454 out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3];
455
456 x = out.x;
457 y = out.y;
458 z = out.z;
459 w = out.w;
460 }
461
462 float matrix_t::Inverse(const matrix_t& srcMatrix, bool affine)
463 {
464 float det = 0;
465
466 if (affine)
467 {
468 det = GetDeterminant();
469 float s = 1 / det;
470 m[0][0] = (srcMatrix.m[1][1] * srcMatrix.m[2][2] - srcMatrix.m[1][2] * srcMatrix.m[2][1]) * s;
471 m[0][1] = (srcMatrix.m[2][1] * srcMatrix.m[0][2] - srcMatrix.m[2][2] * srcMatrix.m[0][1]) * s;
472 m[0][2] = (srcMatrix.m[0][1] * srcMatrix.m[1][2] - srcMatrix.m[0][2] * srcMatrix.m[1][1]) * s;
473 m[1][0] = (srcMatrix.m[1][2] * srcMatrix.m[2][0] - srcMatrix.m[1][0] * srcMatrix.m[2][2]) * s;
474 m[1][1] = (srcMatrix.m[2][2] * srcMatrix.m[0][0] - srcMatrix.m[2][0] * srcMatrix.m[0][2]) * s;
475 m[1][2] = (srcMatrix.m[0][2] * srcMatrix.m[1][0] - srcMatrix.m[0][0] * srcMatrix.m[1][2]) * s;
476 m[2][0] = (srcMatrix.m[1][0] * srcMatrix.m[2][1] - srcMatrix.m[1][1] * srcMatrix.m[2][0]) * s;
477 m[2][1] = (srcMatrix.m[2][0] * srcMatrix.m[0][1] - srcMatrix.m[2][1] * srcMatrix.m[0][0]) * s;
478 m[2][2] = (srcMatrix.m[0][0] * srcMatrix.m[1][1] - srcMatrix.m[0][1] * srcMatrix.m[1][0]) * s;
479 m[3][0] = -(m[0][0] * srcMatrix.m[3][0] + m[1][0] * srcMatrix.m[3][1] + m[2][0] * srcMatrix.m[3][2]);
480 m[3][1] = -(m[0][1] * srcMatrix.m[3][0] + m[1][1] * srcMatrix.m[3][1] + m[2][1] * srcMatrix.m[3][2]);
481 m[3][2] = -(m[0][2] * srcMatrix.m[3][0] + m[1][2] * srcMatrix.m[3][1] + m[2][2] * srcMatrix.m[3][2]);
482 }
483 else
484 {
485 // transpose matrix
486 float src[16];
487 for (int i = 0; i < 4; ++i)
488 {
489 src[i] = srcMatrix.m16[i * 4];
490 src[i + 4] = srcMatrix.m16[i * 4 + 1];
491 src[i + 8] = srcMatrix.m16[i * 4 + 2];
492 src[i + 12] = srcMatrix.m16[i * 4 + 3];
493 }
494
495 // calculate pairs for first 8 elements (cofactors)
496 float tmp[12]; // temp array for pairs
497 tmp[0] = src[10] * src[15];
498 tmp[1] = src[11] * src[14];
499 tmp[2] = src[9] * src[15];
500 tmp[3] = src[11] * src[13];
501 tmp[4] = src[9] * src[14];
502 tmp[5] = src[10] * src[13];
503 tmp[6] = src[8] * src[15];
504 tmp[7] = src[11] * src[12];
505 tmp[8] = src[8] * src[14];
506 tmp[9] = src[10] * src[12];
507 tmp[10] = src[8] * src[13];
508 tmp[11] = src[9] * src[12];
509
510 // calculate first 8 elements (cofactors)
511 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]);
512 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]);
513 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]);
514 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]);
515 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]);
516 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]);
517 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]);
518 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]);
519
520 // calculate pairs for second 8 elements (cofactors)
521 tmp[0] = src[2] * src[7];
522 tmp[1] = src[3] * src[6];
523 tmp[2] = src[1] * src[7];
524 tmp[3] = src[3] * src[5];
525 tmp[4] = src[1] * src[6];
526 tmp[5] = src[2] * src[5];
527 tmp[6] = src[0] * src[7];
528 tmp[7] = src[3] * src[4];
529 tmp[8] = src[0] * src[6];
530 tmp[9] = src[2] * src[4];
531 tmp[10] = src[0] * src[5];
532 tmp[11] = src[1] * src[4];
533
534 // calculate second 8 elements (cofactors)
535 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]);
536 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]);
537 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]);
538 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]);
539 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]);
540 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]);
541 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]);
542 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]);
543
544 // calculate determinant
545 det = src[0] * m16[0] + src[1] * m16[1] + src[2] * m16[2] + src[3] * m16[3];
546
547 // calculate matrix inverse
548 float invdet = 1 / det;
549 for (int j = 0; j < 16; ++j)
550 {
551 m16[j] *= invdet;
552 }
553 }
554
555 return det;
556 }
557
558 void matrix_t::RotationAxis(const vec_t& axis, float angle)
559 {
560 float length2 = axis.LengthSq();
561 if (length2 < FLT_EPSILON)
562 {
564 return;
565 }
566
567 vec_t n = axis * (1.f / sqrtf(length2));
568 float s = sinf(angle);
569 float c = cosf(angle);
570 float k = 1.f - c;
571
572 float xx = n.x * n.x * k + c;
573 float yy = n.y * n.y * k + c;
574 float zz = n.z * n.z * k + c;
575 float xy = n.x * n.y * k;
576 float yz = n.y * n.z * k;
577 float zx = n.z * n.x * k;
578 float xs = n.x * s;
579 float ys = n.y * s;
580 float zs = n.z * s;
581
582 m[0][0] = xx;
583 m[0][1] = xy + zs;
584 m[0][2] = zx - ys;
585 m[0][3] = 0.f;
586 m[1][0] = xy - zs;
587 m[1][1] = yy;
588 m[1][2] = yz + xs;
589 m[1][3] = 0.f;
590 m[2][0] = zx + ys;
591 m[2][1] = yz - xs;
592 m[2][2] = zz;
593 m[2][3] = 0.f;
594 m[3][0] = 0.f;
595 m[3][1] = 0.f;
596 m[3][2] = 0.f;
597 m[3][3] = 1.f;
598 }
599
601 //
602
622
623 static bool IsTranslateType(int type)
624 {
625 return type >= MT_MOVE_X && type <= MT_MOVE_SCREEN;
626 }
627
628 static bool IsRotateType(int type)
629 {
630 return type >= MT_ROTATE_X && type <= MT_ROTATE_SCREEN;
631 }
632
633 static bool IsScaleType(int type)
634 {
635 return type >= MT_SCALE_X && type <= MT_SCALE_XYZ;
636 }
637
638 // Matches MT_MOVE_AB order
640
641 struct Context
642 {
643 Context() : mbUsing(false), mbEnable(true), mbUsingBounds(false)
644 {
645 }
646
647 ImDrawList* mDrawList;
648
658
666
671
674
677
678 bool mReversed; // reversed projection matrix
679
680 // translation
685
686 // rotation
690 //vec_t mWorldToLocalAxis;
691
692 // scale
697
698 // save axis factor when using gizmo
701 float mAxisFactor[3];
702
703 // bounds stretching
712
713 //
715
716 float mX = 0.f;
717 float mY = 0.f;
718 float mWidth = 0.f;
719 float mHeight = 0.f;
720 float mXMax = 0.f;
721 float mYMax = 0.f;
722 float mDisplayRatio = 1.f;
723
724 bool mIsOrthographic = false;
725
726 int mActualID = -1;
727 int mEditingID = -1;
729
730 bool mAllowAxisFlip = true;
732 };
733
735
736 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) };
737 static const ImU32 directionColor[3] = { IM_COL32(0xAA, 0, 0, 0xFF), IM_COL32(0, 0xAA, 0, 0xFF), IM_COL32(0, 0, 0xAA, 0XFF) };
738
739 // Alpha: 100%: FF, 87%: DE, 70%: B3, 54%: 8A, 50%: 80, 38%: 61, 12%: 1F
740 static const ImU32 planeColor[3] = { IM_COL32(0xAA, 0, 0, 0x61), IM_COL32(0, 0xAA, 0, 0x61), IM_COL32(0, 0, 0xAA, 0x61) };
741 static const ImU32 selectionColor = IM_COL32(0xFF, 0x80, 0x10, 0x8A);
742 static const ImU32 inactiveColor = IM_COL32(0x99, 0x99, 0x99, 0x99);
743 static const ImU32 translationLineColor = IM_COL32(0xAA, 0xAA, 0xAA, 0xAA);
744 static const char* translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f",
745 "Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f",
746 "X : %5.3f Y : %5.3f Z : %5.3f" };
747 static const char* scaleInfoMask[] = { "X : %5.2f", "Y : %5.2f", "Z : %5.2f", "XYZ : %5.2f" };
748 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" };
749 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 };
750 static const float quadMin = 0.5f;
751 static const float quadMax = 0.8f;
752 static const float quadUV[8] = { quadMin, quadMin, quadMin, quadMax, quadMax, quadMax, quadMax, quadMin };
753 static const int halfCircleSegmentCount = 64;
754 static const float snapTension = 0.5f;
755
757 //
758 static int GetMoveType(OPERATION op, vec_t* gizmoHitProportion);
759 static int GetRotateType(OPERATION op);
760 static int GetScaleType(OPERATION op);
761
762 static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat, ImVec2 position = ImVec2(gContext.mX, gContext.mY), ImVec2 size = ImVec2(gContext.mWidth, gContext.mHeight))
763 {
764 vec_t trans;
765 trans.TransformPoint(worldPos, mat);
766 trans *= 0.5f / trans.w;
767 trans += makeVect(0.5f, 0.5f);
768 trans.y = 1.f - trans.y;
769 trans.x *= size.x;
770 trans.y *= size.y;
771 trans.x += position.x;
772 trans.y += position.y;
773 return ImVec2(trans.x, trans.y);
774 }
775
776 static void ComputeCameraRay(vec_t& rayOrigin, vec_t& rayDir, ImVec2 position = ImVec2(gContext.mX, gContext.mY), ImVec2 size = ImVec2(gContext.mWidth, gContext.mHeight))
777 {
778 ImGuiIO& io = ImGui::GetIO();
779
780 matrix_t mViewProjInverse;
781 mViewProjInverse.Inverse(gContext.mViewMat * gContext.mProjectionMat);
782
783 const float mox = ((io.MousePos.x - position.x) / size.x) * 2.f - 1.f;
784 const float moy = (1.f - ((io.MousePos.y - position.y) / size.y)) * 2.f - 1.f;
785
786 const float zNear = gContext.mReversed ? (1.f - FLT_EPSILON) : 0.f;
787 const float zFar = gContext.mReversed ? 0.f : (1.f - FLT_EPSILON);
788
789 rayOrigin.Transform(makeVect(mox, moy, zNear, 1.f), mViewProjInverse);
790 rayOrigin *= 1.f / rayOrigin.w;
791 vec_t rayEnd;
792 rayEnd.Transform(makeVect(mox, moy, zFar, 1.f), mViewProjInverse);
793 rayEnd *= 1.f / rayEnd.w;
794 rayDir = Normalized(rayEnd - rayOrigin);
795 }
796
797 static float GetSegmentLengthClipSpace(const vec_t& start, const vec_t& end)
798 {
799 vec_t startOfSegment = start;
800 startOfSegment.TransformPoint(gContext.mMVP);
801 if (fabsf(startOfSegment.w) > FLT_EPSILON) // check for axis aligned with camera direction
802 {
803 startOfSegment *= 1.f / startOfSegment.w;
804 }
805
806 vec_t endOfSegment = end;
807 endOfSegment.TransformPoint(gContext.mMVP);
808 if (fabsf(endOfSegment.w) > FLT_EPSILON) // check for axis aligned with camera direction
809 {
810 endOfSegment *= 1.f / endOfSegment.w;
811 }
812
813 vec_t clipSpaceAxis = endOfSegment - startOfSegment;
814 clipSpaceAxis.y /= gContext.mDisplayRatio;
815 float segmentLengthInClipSpace = sqrtf(clipSpaceAxis.x * clipSpaceAxis.x + clipSpaceAxis.y * clipSpaceAxis.y);
816 return segmentLengthInClipSpace;
817 }
818
819 static float GetParallelogram(const vec_t& ptO, const vec_t& ptA, const vec_t& ptB)
820 {
821 vec_t pts[] = { ptO, ptA, ptB };
822 for (unsigned int i = 0; i < 3; i++)
823 {
824 pts[i].TransformPoint(gContext.mMVP);
825 if (fabsf(pts[i].w) > FLT_EPSILON) // check for axis aligned with camera direction
826 {
827 pts[i] *= 1.f / pts[i].w;
828 }
829 }
830 vec_t segA = pts[1] - pts[0];
831 vec_t segB = pts[2] - pts[0];
832 segA.y /= gContext.mDisplayRatio;
833 segB.y /= gContext.mDisplayRatio;
834 vec_t segAOrtho = makeVect(-segA.y, segA.x);
835 segAOrtho.Normalize();
836 float dt = segAOrtho.Dot3(segB);
837 float surface = sqrtf(segA.x * segA.x + segA.y * segA.y) * fabsf(dt);
838 return surface;
839 }
840
841 inline vec_t PointOnSegment(const vec_t& point, const vec_t& vertPos1, const vec_t& vertPos2)
842 {
843 vec_t c = point - vertPos1;
844 vec_t V;
845
846 V.Normalize(vertPos2 - vertPos1);
847 float d = (vertPos2 - vertPos1).Length();
848 float t = V.Dot3(c);
849
850 if (t < 0.f)
851 {
852 return vertPos1;
853 }
854
855 if (t > d)
856 {
857 return vertPos2;
858 }
859
860 return vertPos1 + V * t;
861 }
862
863 static float IntersectRayPlane(const vec_t& rOrigin, const vec_t& rVector, const vec_t& plan)
864 {
865 float numer = plan.Dot3(rOrigin) - plan.w;
866 float denom = plan.Dot3(rVector);
867
868 if (fabsf(denom) < FLT_EPSILON) // normal is orthogonal to vector, cant intersect
869 {
870 return -1.0f;
871 }
872
873 return -(numer / denom);
874 }
875
876 static float DistanceToPlane(const vec_t& point, const vec_t& plan)
877 {
878 return plan.Dot3(point) + plan.w;
879 }
880
881 static bool IsInContextRect(ImVec2 p)
882 {
883 return IsWithin(p.x, gContext.mX, gContext.mXMax) && IsWithin(p.y, gContext.mY, gContext.mYMax);
884 }
885
886 void SetRect(float x, float y, float width, float height)
887 {
888 gContext.mX = x;
889 gContext.mY = y;
890 gContext.mWidth = width;
891 gContext.mHeight = height;
892 gContext.mXMax = gContext.mX + gContext.mWidth;
893 gContext.mYMax = gContext.mY + gContext.mXMax;
894 gContext.mDisplayRatio = width / height;
895 }
896
897 void SetOrthographic(bool isOrthographic)
898 {
899 gContext.mIsOrthographic = isOrthographic;
900 }
901
902 void SetDrawlist(ImDrawList* drawlist)
903 {
904 gContext.mDrawList = drawlist ? drawlist : ImGui::GetWindowDrawList();
905 }
906
907 void SetImGuiContext(ImGuiContext* ctx)
908 {
909 ImGui::SetCurrentContext(ctx);
910 }
911
913 {
914 const ImU32 flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus;
915
916#ifdef IMGUI_HAS_VIEWPORT
917 ImGui::SetNextWindowSize(ImGui::GetMainViewport()->Size);
918 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->Pos);
919#else
920 ImGuiIO& io = ImGui::GetIO();
921 ImGui::SetNextWindowSize(io.DisplaySize);
922 ImGui::SetNextWindowPos(ImVec2(0, 0));
923#endif
924
925 ImGui::PushStyleColor(ImGuiCol_WindowBg, 0);
926 ImGui::PushStyleColor(ImGuiCol_Border, 0);
927 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
928
929 ImGui::Begin("gizmo", NULL, flags);
930 gContext.mDrawList = ImGui::GetWindowDrawList();
931 ImGui::End();
932 ImGui::PopStyleVar();
933 ImGui::PopStyleColor(2);
934 }
935
936 bool IsUsing()
937 {
938 return gContext.mbUsing || gContext.mbUsingBounds;
939 }
940
941 bool IsOver()
942 {
943 return (Intersects(gContext.mOperation, TRANSLATE) && GetMoveType(gContext.mOperation, NULL) != MT_NONE) ||
944 (Intersects(gContext.mOperation, ROTATE) && GetRotateType(gContext.mOperation) != MT_NONE) ||
945 (Intersects(gContext.mOperation, SCALE) && GetScaleType(gContext.mOperation) != MT_NONE) || IsUsing();
946 }
947
949 {
950 if(IsUsing())
951 {
952 return true;
953 }
954 if(Intersects(op, SCALE) && GetScaleType(op) != MT_NONE)
955 {
956 return true;
957 }
958 if(Intersects(op, ROTATE) && GetRotateType(op) != MT_NONE)
959 {
960 return true;
961 }
962 if(Intersects(op, TRANSLATE) && GetMoveType(op, NULL) != MT_NONE)
963 {
964 return true;
965 }
966 return false;
967 }
968
969 void Enable(bool enable)
970 {
971 gContext.mbEnable = enable;
972 if (!enable)
973 {
974 gContext.mbUsing = false;
975 gContext.mbUsingBounds = false;
976 }
977 }
978
979 static void ComputeContext(const float* view, const float* projection, float* matrix, MODE mode)
980 {
981 gContext.mMode = mode;
982 gContext.mViewMat = *(matrix_t*)view;
983 gContext.mProjectionMat = *(matrix_t*)projection;
984
985 if (mode == LOCAL)
986 {
987 gContext.mModel = *(matrix_t*)matrix;
988 gContext.mModel.OrthoNormalize();
989 }
990 else
991 {
992 gContext.mModel.Translation(((matrix_t*)matrix)->v.position);
993 }
994 gContext.mModelSource = *(matrix_t*)matrix;
995 gContext.mModelScaleOrigin.Set(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length());
996
997 gContext.mModelInverse.Inverse(gContext.mModel);
998 gContext.mModelSourceInverse.Inverse(gContext.mModelSource);
999 gContext.mViewProjection = gContext.mViewMat * gContext.mProjectionMat;
1000 gContext.mMVP = gContext.mModel * gContext.mViewProjection;
1001
1002 matrix_t viewInverse;
1003 viewInverse.Inverse(gContext.mViewMat);
1004 gContext.mCameraDir = viewInverse.v.dir;
1005 gContext.mCameraEye = viewInverse.v.position;
1006 gContext.mCameraRight = viewInverse.v.right;
1007 gContext.mCameraUp = viewInverse.v.up;
1008
1009 // projection reverse
1010 vec_t nearPos, farPos;
1011 nearPos.Transform(makeVect(0, 0, 1.f, 1.f), gContext.mProjectionMat);
1012 farPos.Transform(makeVect(0, 0, 2.f, 1.f), gContext.mProjectionMat);
1013
1014 gContext.mReversed = (nearPos.z/nearPos.w) > (farPos.z / farPos.w);
1015
1016 // compute scale from the size of camera right vector projected on screen at the matrix position
1017 vec_t pointRight = viewInverse.v.right;
1018 pointRight.TransformPoint(gContext.mViewProjection);
1019 gContext.mScreenFactor = gContext.mGizmoSizeClipSpace / (pointRight.x / pointRight.w - gContext.mMVP.v.position.x / gContext.mMVP.v.position.w);
1020
1021 vec_t rightViewInverse = viewInverse.v.right;
1022 rightViewInverse.TransformVector(gContext.mModelInverse);
1023 float rightLength = GetSegmentLengthClipSpace(makeVect(0.f, 0.f), rightViewInverse);
1024 gContext.mScreenFactor = gContext.mGizmoSizeClipSpace / rightLength;
1025
1026 ImVec2 centerSSpace = worldToPos(makeVect(0.f, 0.f), gContext.mMVP);
1027 gContext.mScreenSquareCenter = centerSSpace;
1028 gContext.mScreenSquareMin = ImVec2(centerSSpace.x - 10.f, centerSSpace.y - 10.f);
1029 gContext.mScreenSquareMax = ImVec2(centerSSpace.x + 10.f, centerSSpace.y + 10.f);
1030
1031 ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector);
1032 }
1033
1034 static void ComputeColors(ImU32* colors, int type, OPERATION operation)
1035 {
1036 if (gContext.mbEnable)
1037 {
1038 switch (operation)
1039 {
1040 case TRANSLATE:
1041 colors[0] = (type == MT_MOVE_SCREEN) ? selectionColor : IM_COL32_WHITE;
1042 for (int i = 0; i < 3; i++)
1043 {
1044 colors[i + 1] = (type == (int)(MT_MOVE_X + i)) ? selectionColor : directionColor[i];
1045 colors[i + 4] = (type == (int)(MT_MOVE_YZ + i)) ? selectionColor : planeColor[i];
1046 colors[i + 4] = (type == MT_MOVE_SCREEN) ? selectionColor : colors[i + 4];
1047 }
1048 break;
1049 case ROTATE:
1050 colors[0] = (type == MT_ROTATE_SCREEN) ? selectionColor : IM_COL32_WHITE;
1051 for (int i = 0; i < 3; i++)
1052 {
1053 colors[i + 1] = (type == (int)(MT_ROTATE_X + i)) ? selectionColor : directionColor[i];
1054 }
1055 break;
1056 case SCALE:
1057 colors[0] = (type == MT_SCALE_XYZ) ? selectionColor : IM_COL32_WHITE;
1058 for (int i = 0; i < 3; i++)
1059 {
1060 colors[i + 1] = (type == (int)(MT_SCALE_X + i)) ? selectionColor : directionColor[i];
1061 }
1062 break;
1063 // note: this internal function is only called with three possible values for operation
1064 default:
1065 break;
1066 }
1067 }
1068 else
1069 {
1070 for (int i = 0; i < 7; i++)
1071 {
1072 colors[i] = inactiveColor;
1073 }
1074 }
1075 }
1076
1077 static void ComputeTripodAxisAndVisibility(int axisIndex, vec_t& dirAxis, vec_t& dirPlaneX, vec_t& dirPlaneY, bool& belowAxisLimit, bool& belowPlaneLimit)
1078 {
1079 dirAxis = directionUnary[axisIndex];
1080 dirPlaneX = directionUnary[(axisIndex + 1) % 3];
1081 dirPlaneY = directionUnary[(axisIndex + 2) % 3];
1082
1083 if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID))
1084 {
1085 // when using, use stored factors so the gizmo doesn't flip when we translate
1086 belowAxisLimit = gContext.mBelowAxisLimit[axisIndex];
1087 belowPlaneLimit = gContext.mBelowPlaneLimit[axisIndex];
1088
1089 dirAxis *= gContext.mAxisFactor[axisIndex];
1090 dirPlaneX *= gContext.mAxisFactor[(axisIndex + 1) % 3];
1091 dirPlaneY *= gContext.mAxisFactor[(axisIndex + 2) % 3];
1092 }
1093 else
1094 {
1095 // new method
1096 float lenDir = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis);
1097 float lenDirMinus = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirAxis);
1098
1099 float lenDirPlaneX = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirPlaneX);
1100 float lenDirMinusPlaneX = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirPlaneX);
1101
1102 float lenDirPlaneY = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirPlaneY);
1103 float lenDirMinusPlaneY = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirPlaneY);
1104
1105 // For readability
1106 bool & allowFlip = gContext.mAllowAxisFlip;
1107 float mulAxis = (allowFlip && lenDir < lenDirMinus&& fabsf(lenDir - lenDirMinus) > FLT_EPSILON) ? -1.f : 1.f;
1108 float mulAxisX = (allowFlip && lenDirPlaneX < lenDirMinusPlaneX&& fabsf(lenDirPlaneX - lenDirMinusPlaneX) > FLT_EPSILON) ? -1.f : 1.f;
1109 float mulAxisY = (allowFlip && lenDirPlaneY < lenDirMinusPlaneY&& fabsf(lenDirPlaneY - lenDirMinusPlaneY) > FLT_EPSILON) ? -1.f : 1.f;
1110 dirAxis *= mulAxis;
1111 dirPlaneX *= mulAxisX;
1112 dirPlaneY *= mulAxisY;
1113
1114 // for axis
1115 float axisLengthInClipSpace = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis * gContext.mScreenFactor);
1116
1117 float paraSurf = GetParallelogram(makeVect(0.f, 0.f, 0.f), dirPlaneX * gContext.mScreenFactor, dirPlaneY * gContext.mScreenFactor);
1118 belowPlaneLimit = (paraSurf > 0.0025f);
1119 belowAxisLimit = (axisLengthInClipSpace > 0.02f);
1120
1121 // and store values
1122 gContext.mAxisFactor[axisIndex] = mulAxis;
1123 gContext.mAxisFactor[(axisIndex + 1) % 3] = mulAxisX;
1124 gContext.mAxisFactor[(axisIndex + 2) % 3] = mulAxisY;
1125 gContext.mBelowAxisLimit[axisIndex] = belowAxisLimit;
1126 gContext.mBelowPlaneLimit[axisIndex] = belowPlaneLimit;
1127 }
1128 }
1129
1130 static void ComputeSnap(float* value, float snap)
1131 {
1132 if (snap <= FLT_EPSILON)
1133 {
1134 return;
1135 }
1136
1137 float modulo = fmodf(*value, snap);
1138 float moduloRatio = fabsf(modulo) / snap;
1139 if (moduloRatio < snapTension)
1140 {
1141 *value -= modulo;
1142 }
1143 else if (moduloRatio > (1.f - snapTension))
1144 {
1145 *value = *value - modulo + snap * ((*value < 0.f) ? -1.f : 1.f);
1146 }
1147 }
1148 static void ComputeSnap(vec_t& value, const float* snap)
1149 {
1150 for (int i = 0; i < 3; i++)
1151 {
1152 ComputeSnap(&value[i], snap[i]);
1153 }
1154 }
1155
1156 static float ComputeAngleOnPlan()
1157 {
1158 const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
1159 vec_t localPos = Normalized(gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position);
1160
1161 vec_t perpendicularVector;
1162 perpendicularVector.Cross(gContext.mRotationVectorSource, gContext.mTranslationPlan);
1163 perpendicularVector.Normalize();
1164 float acosAngle = Clamp(Dot(localPos, gContext.mRotationVectorSource), -1.f, 1.f);
1165 float angle = acosf(acosAngle);
1166 angle *= (Dot(localPos, perpendicularVector) < 0.f) ? 1.f : -1.f;
1167 return angle;
1168 }
1169
1170 static void DrawRotationGizmo(OPERATION op, int type)
1171 {
1172 if(!Intersects(op, ROTATE))
1173 {
1174 return;
1175 }
1176 ImDrawList* drawList = gContext.mDrawList;
1177
1178 // colors
1179 ImU32 colors[7];
1180 ComputeColors(colors, type, ROTATE);
1181
1182 vec_t cameraToModelNormalized;
1183 if (gContext.mIsOrthographic)
1184 {
1185 matrix_t viewInverse;
1186 viewInverse.Inverse(*(matrix_t*)&gContext.mViewMat);
1187 cameraToModelNormalized = viewInverse.v.dir;
1188 }
1189 else
1190 {
1191 cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye);
1192 }
1193
1194 cameraToModelNormalized.TransformVector(gContext.mModelInverse);
1195
1196 gContext.mRadiusSquareCenter = screenRotateSize * gContext.mHeight;
1197
1198 bool hasRSC = Intersects(op, ROTATE_SCREEN);
1199 int circleMul = hasRSC ? 1 : 2;
1200 for (int axis = 0; axis < 3; axis++)
1201 {
1202 if(!Intersects(op, static_cast<OPERATION>(ROTATE_Z >> axis)))
1203 {
1204 continue;
1205 }
1206 ImVec2* circlePos = (ImVec2*) alloca(sizeof(ImVec2) * (circleMul * halfCircleSegmentCount + 1));
1207
1208 float angleStart = atan2f(cameraToModelNormalized[(4 - axis) % 3], cameraToModelNormalized[(3 - axis) % 3]) + ZPI * 0.5f;
1209
1210 for (int i = 0; i < circleMul * halfCircleSegmentCount + 1; i++)
1211 {
1212 float ng = angleStart + circleMul * ZPI * ((float)i / (float)halfCircleSegmentCount);
1213 vec_t axisPos = makeVect(cosf(ng), sinf(ng), 0.f);
1214 vec_t pos = makeVect(axisPos[axis], axisPos[(axis + 1) % 3], axisPos[(axis + 2) % 3]) * gContext.mScreenFactor;
1215 circlePos[i] = worldToPos(pos, gContext.mMVP);
1216 }
1217
1218 float radiusAxis = sqrtf((ImLengthSqr(worldToPos(gContext.mModel.v.position, gContext.mViewProjection) - circlePos[0])));
1219 if (radiusAxis > gContext.mRadiusSquareCenter)
1220 {
1221 gContext.mRadiusSquareCenter = radiusAxis;
1222 }
1223
1224 drawList->AddPolyline(circlePos, circleMul * halfCircleSegmentCount + 1, colors[3 - axis], false, 2);
1225 }
1226 if(hasRSC)
1227 {
1228 drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), gContext.mRadiusSquareCenter, colors[0], 64, 3.f);
1229 }
1230
1231 if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsRotateType(type))
1232 {
1233 ImVec2 circlePos[halfCircleSegmentCount + 1];
1234
1235 circlePos[0] = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
1236 for (unsigned int i = 1; i < halfCircleSegmentCount; i++)
1237 {
1238 float ng = gContext.mRotationAngle * ((float)(i - 1) / (float)(halfCircleSegmentCount - 1));
1239 matrix_t rotateVectorMatrix;
1240 rotateVectorMatrix.RotationAxis(gContext.mTranslationPlan, ng);
1241 vec_t pos;
1242 pos.TransformPoint(gContext.mRotationVectorSource, rotateVectorMatrix);
1243 pos *= gContext.mScreenFactor;
1244 circlePos[i] = worldToPos(pos + gContext.mModel.v.position, gContext.mViewProjection);
1245 }
1246 drawList->AddConvexPolyFilled(circlePos, halfCircleSegmentCount, IM_COL32(0xFF, 0x80, 0x10, 0x80));
1247 drawList->AddPolyline(circlePos, halfCircleSegmentCount, IM_COL32(0xFF, 0x80, 0x10, 0xFF), true, 2);
1248
1249 ImVec2 destinationPosOnScreen = circlePos[1];
1250 char tmps[512];
1251 ImFormatString(tmps, sizeof(tmps), rotationInfoMask[type - MT_ROTATE_X], (gContext.mRotationAngle / ZPI) * 180.f, gContext.mRotationAngle);
1252 drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), IM_COL32_BLACK, tmps);
1253 drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), IM_COL32_WHITE, tmps);
1254 }
1255 }
1256
1257 static void DrawHatchedAxis(const vec_t& axis)
1258 {
1259 for (int j = 1; j < 10; j++)
1260 {
1261 ImVec2 baseSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2) * gContext.mScreenFactor, gContext.mMVP);
1262 ImVec2 worldDirSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2 + 1) * gContext.mScreenFactor, gContext.mMVP);
1263 gContext.mDrawList->AddLine(baseSSpace2, worldDirSSpace2, IM_COL32(0, 0, 0, 0x80), 6.f);
1264 }
1265 }
1266
1267 static void DrawScaleGizmo(OPERATION op, int type)
1268 {
1269 ImDrawList* drawList = gContext.mDrawList;
1270
1271 if(!Intersects(op, SCALE))
1272 {
1273 return;
1274 }
1275
1276 // colors
1277 ImU32 colors[7];
1278 ComputeColors(colors, type, SCALE);
1279
1280 // draw
1281 vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f };
1282
1283 if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID))
1284 {
1285 scaleDisplay = gContext.mScale;
1286 }
1287
1288 for (unsigned int i = 0; i < 3; i++)
1289 {
1290 if(!Intersects(op, static_cast<OPERATION>(SCALE_X << i)))
1291 {
1292 continue;
1293 }
1294 vec_t dirPlaneX, dirPlaneY, dirAxis;
1295 bool belowAxisLimit, belowPlaneLimit;
1296 ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
1297
1298 // draw axis
1299 if (belowAxisLimit)
1300 {
1301 bool hasTranslateOnAxis = Contains(op, static_cast<OPERATION>(TRANSLATE_X << i)) ;
1302 float markerScale = hasTranslateOnAxis ? 1.4f : 1.0f;
1303 ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP);
1304 ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * gContext.mScreenFactor, gContext.mMVP);
1305 ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVP);
1306
1307 if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID))
1308 {
1309 drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, IM_COL32(0x40, 0x40, 0x40, 0xFF), 3.f);
1310 drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, IM_COL32(0x40, 0x40, 0x40, 0xFF));
1311 }
1312
1313 if(!hasTranslateOnAxis || gContext.mbUsing)
1314 {
1315 drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f);
1316 }
1317 drawList->AddCircleFilled(worldDirSSpace, 6.f, colors[i + 1]);
1318
1319 if (gContext.mAxisFactor[i] < 0.f)
1320 {
1321 DrawHatchedAxis(dirAxis * scaleDisplay[i]);
1322 }
1323 }
1324 }
1325
1326 // draw screen cirle
1327 drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32);
1328
1329 if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsScaleType(type))
1330 {
1331 //ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection);
1332 ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
1333 /*vec_t dif(destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y);
1334 dif.Normalize();
1335 dif *= 5.f;
1336 drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor);
1337 drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor);
1338 drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f);
1339 */
1340 char tmps[512];
1341 //vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin;
1342 int componentInfoIndex = (type - MT_SCALE_X) * 3;
1343 ImFormatString(tmps, sizeof(tmps), scaleInfoMask[type - MT_SCALE_X], scaleDisplay[translationInfoIndex[componentInfoIndex]]);
1344 drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), IM_COL32_BLACK, tmps);
1345 drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), IM_COL32_WHITE, tmps);
1346 }
1347 }
1348
1349
1350 static void DrawTranslationGizmo(OPERATION op, int type)
1351 {
1352 ImDrawList* drawList = gContext.mDrawList;
1353 if (!drawList)
1354 {
1355 return;
1356 }
1357
1358 if(!Intersects(op, TRANSLATE))
1359 {
1360 return;
1361 }
1362
1363 // colors
1364 ImU32 colors[7];
1365 ComputeColors(colors, type, TRANSLATE);
1366
1367 const ImVec2 origin = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
1368
1369 // draw
1370 bool belowAxisLimit = false;
1371 bool belowPlaneLimit = false;
1372 for (unsigned int i = 0; i < 3; ++i)
1373 {
1374 vec_t dirPlaneX, dirPlaneY, dirAxis;
1375 ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
1376
1377 // draw axis
1378 if (belowAxisLimit && Intersects(op, static_cast<OPERATION>(TRANSLATE_X << i)))
1379 {
1380 ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP);
1381 ImVec2 worldDirSSpace = worldToPos(dirAxis * gContext.mScreenFactor, gContext.mMVP);
1382
1383 drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f);
1384
1385 // Arrow head begin
1386 ImVec2 dir(origin - worldDirSSpace);
1387
1388 float d = sqrtf(ImLengthSqr(dir));
1389 dir /= d; // Normalize
1390 dir *= 6.0f;
1391
1392 ImVec2 ortogonalDir(dir.y, -dir.x); // Perpendicular vector
1393 ImVec2 a(worldDirSSpace + dir);
1394 drawList->AddTriangleFilled(worldDirSSpace - dir, a + ortogonalDir, a - ortogonalDir, colors[i + 1]);
1395 // Arrow head end
1396
1397 if (gContext.mAxisFactor[i] < 0.f)
1398 {
1399 DrawHatchedAxis(dirAxis);
1400 }
1401 }
1402
1403 // draw plane
1404 if (belowPlaneLimit && Contains(op, TRANSLATE_PLANS[i]))
1405 {
1406 ImVec2 screenQuadPts[4];
1407 for (int j = 0; j < 4; ++j)
1408 {
1409 vec_t cornerWorldPos = (dirPlaneX * quadUV[j * 2] + dirPlaneY * quadUV[j * 2 + 1]) * gContext.mScreenFactor;
1410 screenQuadPts[j] = worldToPos(cornerWorldPos, gContext.mMVP);
1411 }
1412 drawList->AddPolyline(screenQuadPts, 4, directionColor[i], true, 1.0f);
1413 drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4]);
1414 }
1415 }
1416
1417 drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32);
1418
1419 if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsTranslateType(type))
1420 {
1421 ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection);
1422 ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
1423 vec_t dif = { destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y, 0.f, 0.f };
1424 dif.Normalize();
1425 dif *= 5.f;
1426 drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor);
1427 drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor);
1428 drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f);
1429
1430 char tmps[512];
1431 vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin;
1432 int componentInfoIndex = (type - MT_MOVE_X) * 3;
1433 ImFormatString(tmps, sizeof(tmps), translationInfoMask[type - MT_MOVE_X], deltaInfo[translationInfoIndex[componentInfoIndex]], deltaInfo[translationInfoIndex[componentInfoIndex + 1]], deltaInfo[translationInfoIndex[componentInfoIndex + 2]]);
1434 drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), IM_COL32_BLACK, tmps);
1435 drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), IM_COL32_WHITE, tmps);
1436 }
1437 }
1438
1439 static bool CanActivate()
1440 {
1441 if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && !ImGui::IsAnyItemActive())
1442 {
1443 return true;
1444 }
1445 return false;
1446 }
1447
1448 static void HandleAndDrawLocalBounds(const float* bounds, matrix_t* matrix, const float* snapValues, OPERATION operation)
1449 {
1450 ImGuiIO& io = ImGui::GetIO();
1451 ImDrawList* drawList = gContext.mDrawList;
1452
1453 // compute best projection axis
1454 vec_t axesWorldDirections[3];
1455 vec_t bestAxisWorldDirection = { 0.0f, 0.0f, 0.0f, 0.0f };
1456 int axes[3];
1457 unsigned int numAxes = 1;
1458 axes[0] = gContext.mBoundsBestAxis;
1459 int bestAxis = axes[0];
1460 if (!gContext.mbUsingBounds)
1461 {
1462 numAxes = 0;
1463 float bestDot = 0.f;
1464 for (unsigned int i = 0; i < 3; i++)
1465 {
1466 vec_t dirPlaneNormalWorld;
1467 dirPlaneNormalWorld.TransformVector(directionUnary[i], gContext.mModelSource);
1468 dirPlaneNormalWorld.Normalize();
1469
1470 float dt = fabsf(Dot(Normalized(gContext.mCameraEye - gContext.mModelSource.v.position), dirPlaneNormalWorld));
1471 if (dt >= bestDot)
1472 {
1473 bestDot = dt;
1474 bestAxis = i;
1475 bestAxisWorldDirection = dirPlaneNormalWorld;
1476 }
1477
1478 if (dt >= 0.1f)
1479 {
1480 axes[numAxes] = i;
1481 axesWorldDirections[numAxes] = dirPlaneNormalWorld;
1482 ++numAxes;
1483 }
1484 }
1485 }
1486
1487 if (numAxes == 0)
1488 {
1489 axes[0] = bestAxis;
1490 axesWorldDirections[0] = bestAxisWorldDirection;
1491 numAxes = 1;
1492 }
1493
1494 else if (bestAxis != axes[0])
1495 {
1496 unsigned int bestIndex = 0;
1497 for (unsigned int i = 0; i < numAxes; i++)
1498 {
1499 if (axes[i] == bestAxis)
1500 {
1501 bestIndex = i;
1502 break;
1503 }
1504 }
1505 int tempAxis = axes[0];
1506 axes[0] = axes[bestIndex];
1507 axes[bestIndex] = tempAxis;
1508 vec_t tempDirection = axesWorldDirections[0];
1509 axesWorldDirections[0] = axesWorldDirections[bestIndex];
1510 axesWorldDirections[bestIndex] = tempDirection;
1511 }
1512
1513 for (unsigned int axisIndex = 0; axisIndex < numAxes; ++axisIndex)
1514 {
1515 bestAxis = axes[axisIndex];
1516 bestAxisWorldDirection = axesWorldDirections[axisIndex];
1517
1518 // corners
1519 vec_t aabb[4];
1520
1521 int secondAxis = (bestAxis + 1) % 3;
1522 int thirdAxis = (bestAxis + 2) % 3;
1523
1524 for (int i = 0; i < 4; i++)
1525 {
1526 aabb[i][3] = aabb[i][bestAxis] = 0.f;
1527 aabb[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)];
1528 aabb[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))];
1529 }
1530
1531 // draw bounds
1532 unsigned int anchorAlpha = gContext.mbEnable ? IM_COL32_BLACK : IM_COL32(0, 0, 0, 0x80);
1533
1534 matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection;
1535 for (int i = 0; i < 4; i++)
1536 {
1537 ImVec2 worldBound1 = worldToPos(aabb[i], boundsMVP);
1538 ImVec2 worldBound2 = worldToPos(aabb[(i + 1) % 4], boundsMVP);
1539 if (!IsInContextRect(worldBound1) || !IsInContextRect(worldBound2))
1540 {
1541 continue;
1542 }
1543 float boundDistance = sqrtf(ImLengthSqr(worldBound1 - worldBound2));
1544 int stepCount = (int)(boundDistance / 10.f);
1545 stepCount = min(stepCount, 1000);
1546 float stepLength = 1.f / (float)stepCount;
1547 for (int j = 0; j < stepCount; j++)
1548 {
1549 float t1 = (float)j * stepLength;
1550 float t2 = (float)j * stepLength + stepLength * 0.5f;
1551 ImVec2 worldBoundSS1 = ImLerp(worldBound1, worldBound2, ImVec2(t1, t1));
1552 ImVec2 worldBoundSS2 = ImLerp(worldBound1, worldBound2, ImVec2(t2, t2));
1553 //drawList->AddLine(worldBoundSS1, worldBoundSS2, IM_COL32(0, 0, 0, 0) + anchorAlpha, 3.f);
1554 drawList->AddLine(worldBoundSS1, worldBoundSS2, IM_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha, 2.f);
1555 }
1556 vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4]) * 0.5f;
1557 ImVec2 midBound = worldToPos(midPoint, boundsMVP);
1558 static const float AnchorBigRadius = 8.f;
1559 static const float AnchorSmallRadius = 6.f;
1560 bool overBigAnchor = ImLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius * AnchorBigRadius);
1561 bool overSmallAnchor = ImLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius * AnchorBigRadius);
1562
1563 int type = MT_NONE;
1564 vec_t gizmoHitProportion;
1565
1566 if(Intersects(operation, TRANSLATE))
1567 {
1568 type = GetMoveType(operation, &gizmoHitProportion);
1569 }
1570 if(Intersects(operation, ROTATE) && type == MT_NONE)
1571 {
1572 type = GetRotateType(operation);
1573 }
1574 if(Intersects(operation, SCALE) && type == MT_NONE)
1575 {
1576 type = GetScaleType(operation);
1577 }
1578
1579 if (type != MT_NONE)
1580 {
1581 overBigAnchor = false;
1582 overSmallAnchor = false;
1583 }
1584
1585 unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (IM_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha);
1586 unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (IM_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha);
1587
1588 drawList->AddCircleFilled(worldBound1, AnchorBigRadius, IM_COL32_BLACK);
1589 drawList->AddCircleFilled(worldBound1, AnchorBigRadius - 1.2f, bigAnchorColor);
1590
1591 drawList->AddCircleFilled(midBound, AnchorSmallRadius, IM_COL32_BLACK);
1592 drawList->AddCircleFilled(midBound, AnchorSmallRadius - 1.2f, smallAnchorColor);
1593 int oppositeIndex = (i + 2) % 4;
1594 // big anchor on corners
1595 if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && CanActivate())
1596 {
1597 gContext.mBoundsPivot.TransformPoint(aabb[(i + 2) % 4], gContext.mModelSource);
1598 gContext.mBoundsAnchor.TransformPoint(aabb[i], gContext.mModelSource);
1599 gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
1600 gContext.mBoundsBestAxis = bestAxis;
1601 gContext.mBoundsAxis[0] = secondAxis;
1602 gContext.mBoundsAxis[1] = thirdAxis;
1603
1604 gContext.mBoundsLocalPivot.Set(0.f);
1605 gContext.mBoundsLocalPivot[secondAxis] = aabb[oppositeIndex][secondAxis];
1606 gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis];
1607
1608 gContext.mbUsingBounds = true;
1609 gContext.mEditingID = gContext.mActualID;
1610 gContext.mBoundsMatrix = gContext.mModelSource;
1611 }
1612 // small anchor on middle of segment
1613 if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && CanActivate())
1614 {
1615 vec_t midPointOpposite = (aabb[(i + 2) % 4] + aabb[(i + 3) % 4]) * 0.5f;
1616 gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource);
1617 gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource);
1618 gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
1619 gContext.mBoundsBestAxis = bestAxis;
1620 int indices[] = { secondAxis , thirdAxis };
1621 gContext.mBoundsAxis[0] = indices[i % 2];
1622 gContext.mBoundsAxis[1] = -1;
1623
1624 gContext.mBoundsLocalPivot.Set(0.f);
1625 gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f);
1626
1627 gContext.mbUsingBounds = true;
1628 gContext.mEditingID = gContext.mActualID;
1629 gContext.mBoundsMatrix = gContext.mModelSource;
1630 }
1631 }
1632
1633 if (gContext.mbUsingBounds && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID))
1634 {
1635 matrix_t scale;
1636 scale.SetToIdentity();
1637
1638 // compute projected mouse position on plan
1639 const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mBoundsPlan);
1640 vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
1641
1642 // compute a reference and delta vectors base on mouse move
1643 vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs();
1644 vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs();
1645
1646 // for 1 or 2 axes, compute a ratio that's used for scale and snap it based on resulting length
1647 for (int i = 0; i < 2; i++)
1648 {
1649 int axisIndex1 = gContext.mBoundsAxis[i];
1650 if (axisIndex1 == -1)
1651 {
1652 continue;
1653 }
1654
1655 float ratioAxis = 1.f;
1656 vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex1].Abs();
1657
1658 float dtAxis = axisDir.Dot(referenceVector);
1659 float boundSize = bounds[axisIndex1 + 3] - bounds[axisIndex1];
1660 if (dtAxis > FLT_EPSILON)
1661 {
1662 ratioAxis = axisDir.Dot(deltaVector) / dtAxis;
1663 }
1664
1665 if (snapValues)
1666 {
1667 float length = boundSize * ratioAxis;
1668 ComputeSnap(&length, snapValues[axisIndex1]);
1669 if (boundSize > FLT_EPSILON)
1670 {
1671 ratioAxis = length / boundSize;
1672 }
1673 }
1674 scale.component[axisIndex1] *= ratioAxis;
1675 }
1676
1677 // transform matrix
1678 matrix_t preScale, postScale;
1679 preScale.Translation(-gContext.mBoundsLocalPivot);
1680 postScale.Translation(gContext.mBoundsLocalPivot);
1681 matrix_t res = preScale * scale * postScale * gContext.mBoundsMatrix;
1682 *matrix = res;
1683
1684 // info text
1685 char tmps[512];
1686 ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
1687 ImFormatString(tmps, sizeof(tmps), "X: %.2f Y: %.2f Z:%.2f"
1688 , (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length() * scale.component[0].Length()
1689 , (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length() * scale.component[1].Length()
1690 , (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length() * scale.component[2].Length()
1691 );
1692 drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), IM_COL32_BLACK, tmps);
1693 drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), IM_COL32_WHITE, tmps);
1694 }
1695
1696 if (!io.MouseDown[0]) {
1697 gContext.mbUsingBounds = false;
1698 gContext.mEditingID = -1;
1699 }
1700 if (gContext.mbUsingBounds)
1701 {
1702 break;
1703 }
1704 }
1705 }
1706
1708 //
1709
1711 {
1712 ImGuiIO& io = ImGui::GetIO();
1713 int type = MT_NONE;
1714
1715 // screen
1716 if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x &&
1717 io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y &&
1718 Contains(op, SCALE))
1719 {
1720 type = MT_SCALE_XYZ;
1721 }
1722
1723 // compute
1724 for (unsigned int i = 0; i < 3 && type == MT_NONE; i++)
1725 {
1726 if(!Intersects(op, static_cast<OPERATION>(SCALE_X << i)))
1727 {
1728 continue;
1729 }
1730 vec_t dirPlaneX, dirPlaneY, dirAxis;
1731 bool belowAxisLimit, belowPlaneLimit;
1732 ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
1733 dirAxis.TransformVector(gContext.mModel);
1734 dirPlaneX.TransformVector(gContext.mModel);
1735 dirPlaneY.TransformVector(gContext.mModel);
1736
1737 const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, dirAxis));
1738 vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len;
1739
1740 const float startOffset = Contains(op, static_cast<OPERATION>(TRANSLATE_X << i)) ? 1.0f : 0.1f;
1741 const float endOffset = Contains(op, static_cast<OPERATION>(TRANSLATE_X << i)) ? 1.4f : 1.0f;
1742 const ImVec2 posOnPlanScreen = worldToPos(posOnPlan, gContext.mViewProjection);
1743 const ImVec2 axisStartOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor * startOffset, gContext.mViewProjection);
1744 const ImVec2 axisEndOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor * endOffset, gContext.mViewProjection);
1745
1746 vec_t closestPointOnAxis = PointOnSegment(makeVect(posOnPlanScreen), makeVect(axisStartOnScreen), makeVect(axisEndOnScreen));
1747
1748 if ((closestPointOnAxis - makeVect(posOnPlanScreen)).Length() < 12.f) // pixel size
1749 {
1750 type = MT_SCALE_X + i;
1751 }
1752 }
1753 return type;
1754 }
1755
1757 {
1758 ImGuiIO& io = ImGui::GetIO();
1759 int type = MT_NONE;
1760
1761 vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f };
1762 float dist = deltaScreen.Length();
1763 if (Intersects(op, ROTATE_SCREEN) && dist >= (gContext.mRadiusSquareCenter - 1.0f) && dist < (gContext.mRadiusSquareCenter + 1.0f))
1764 {
1765 type = MT_ROTATE_SCREEN;
1766 }
1767
1768 const vec_t planNormals[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir };
1769
1770 vec_t modelViewPos;
1771 modelViewPos.TransformPoint(gContext.mModel.v.position, gContext.mViewMat);
1772
1773 for (unsigned int i = 0; i < 3 && type == MT_NONE; i++)
1774 {
1775 if(!Intersects(op, static_cast<OPERATION>(ROTATE_X << i)))
1776 {
1777 continue;
1778 }
1779 // pickup plan
1780 vec_t pickupPlan = BuildPlan(gContext.mModel.v.position, planNormals[i]);
1781
1782 const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, pickupPlan);
1783 const vec_t intersectWorldPos = gContext.mRayOrigin + gContext.mRayVector * len;
1784 vec_t intersectViewPos;
1785 intersectViewPos.TransformPoint(intersectWorldPos, gContext.mViewMat);
1786
1787 if (ImAbs(modelViewPos.z) - ImAbs(intersectViewPos.z) < -FLT_EPSILON)
1788 {
1789 continue;
1790 }
1791
1792 const vec_t localPos = intersectWorldPos - gContext.mModel.v.position;
1793 vec_t idealPosOnCircle = Normalized(localPos);
1794 idealPosOnCircle.TransformVector(gContext.mModelInverse);
1795 const ImVec2 idealPosOnCircleScreen = worldToPos(idealPosOnCircle * gContext.mScreenFactor, gContext.mMVP);
1796
1797 //gContext.mDrawList->AddCircle(idealPosOnCircleScreen, 5.f, IM_COL32_WHITE);
1798 const ImVec2 distanceOnScreen = idealPosOnCircleScreen - io.MousePos;
1799
1800 const float distance = makeVect(distanceOnScreen).Length();
1801 if (distance < 8.f) // pixel size
1802 {
1803 type = MT_ROTATE_X + i;
1804 }
1805 }
1806
1807 return type;
1808 }
1809
1810 static int GetMoveType(OPERATION op, vec_t* gizmoHitProportion)
1811 {
1812 if(!Intersects(op, TRANSLATE))
1813 {
1814 return MT_NONE;
1815 }
1816 ImGuiIO& io = ImGui::GetIO();
1817 int type = MT_NONE;
1818
1819 // screen
1820 if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x &&
1821 io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y &&
1822 Contains(op, TRANSLATE))
1823 {
1824 type = MT_MOVE_SCREEN;
1825 }
1826
1827 const vec_t screenCoord = makeVect(io.MousePos - ImVec2(gContext.mX, gContext.mY));
1828
1829 // compute
1830 for (unsigned int i = 0; i < 3 && type == MT_NONE; i++)
1831 {
1832 vec_t dirPlaneX, dirPlaneY, dirAxis;
1833 bool belowAxisLimit, belowPlaneLimit;
1834 ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
1835 dirAxis.TransformVector(gContext.mModel);
1836 dirPlaneX.TransformVector(gContext.mModel);
1837 dirPlaneY.TransformVector(gContext.mModel);
1838
1839 const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, dirAxis));
1840 vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len;
1841
1842 const ImVec2 axisStartOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor * 0.1f, gContext.mViewProjection) - ImVec2(gContext.mX, gContext.mY);
1843 const ImVec2 axisEndOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor, gContext.mViewProjection) - ImVec2(gContext.mX, gContext.mY);
1844
1845 vec_t closestPointOnAxis = PointOnSegment(screenCoord, makeVect(axisStartOnScreen), makeVect(axisEndOnScreen));
1846 if ((closestPointOnAxis - screenCoord).Length() < 12.f && Intersects(op, static_cast<OPERATION>(TRANSLATE_X << i))) // pixel size
1847 {
1848 type = MT_MOVE_X + i;
1849 }
1850
1851 const float dx = dirPlaneX.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor));
1852 const float dy = dirPlaneY.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor));
1853 if (belowPlaneLimit && dx >= quadUV[0] && dx <= quadUV[4] && dy >= quadUV[1] && dy <= quadUV[3] && Contains(op, TRANSLATE_PLANS[i]))
1854 {
1855 type = MT_MOVE_YZ + i;
1856 }
1857
1858 if (gizmoHitProportion)
1859 {
1860 *gizmoHitProportion = makeVect(dx, dy, 0.f);
1861 }
1862 }
1863 return type;
1864 }
1865
1866 static bool HandleTranslation(float* matrix, float* deltaMatrix, OPERATION op, int& type, const float* snap)
1867 {
1868 if(!Intersects(op, TRANSLATE) || type != MT_NONE)
1869 {
1870 return false;
1871 }
1872 ImGuiIO& io = ImGui::GetIO();
1873 bool applyRotationLocaly = gContext.mMode == LOCAL || type == MT_MOVE_SCREEN;
1874 bool modified = false;
1875
1876 // move
1877 if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsTranslateType(gContext.mCurrentOperation))
1878 {
1879 ImGui::SetNextFrameWantCaptureMouse(true);
1880 const float len = fabsf(IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan)); // near plan
1881 vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
1882
1883 // compute delta
1884 vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor;
1885 vec_t delta = newOrigin - gContext.mModel.v.position;
1886
1887 // 1 axis constraint
1888 if (gContext.mCurrentOperation >= MT_MOVE_X && gContext.mCurrentOperation <= MT_MOVE_Z)
1889 {
1890 int axisIndex = gContext.mCurrentOperation - MT_MOVE_X;
1891 const vec_t& axisValue = *(vec_t*)&gContext.mModel.m[axisIndex];
1892 float lengthOnAxis = Dot(axisValue, delta);
1893 delta = axisValue * lengthOnAxis;
1894 }
1895
1896 // snap
1897 if (snap)
1898 {
1899 vec_t cumulativeDelta = gContext.mModel.v.position + delta - gContext.mMatrixOrigin;
1900 if (applyRotationLocaly)
1901 {
1902 matrix_t modelSourceNormalized = gContext.mModelSource;
1903 modelSourceNormalized.OrthoNormalize();
1904 matrix_t modelSourceNormalizedInverse;
1905 modelSourceNormalizedInverse.Inverse(modelSourceNormalized);
1906 cumulativeDelta.TransformVector(modelSourceNormalizedInverse);
1907 ComputeSnap(cumulativeDelta, snap);
1908 cumulativeDelta.TransformVector(modelSourceNormalized);
1909 }
1910 else
1911 {
1912 ComputeSnap(cumulativeDelta, snap);
1913 }
1914 delta = gContext.mMatrixOrigin + cumulativeDelta - gContext.mModel.v.position;
1915
1916 }
1917
1918 if (delta != gContext.mTranslationLastDelta)
1919 {
1920 modified = true;
1921 }
1922 gContext.mTranslationLastDelta = delta;
1923
1924 // compute matrix & delta
1925 matrix_t deltaMatrixTranslation;
1926 deltaMatrixTranslation.Translation(delta);
1927 if (deltaMatrix)
1928 {
1929 memcpy(deltaMatrix, deltaMatrixTranslation.m16, sizeof(float) * 16);
1930 }
1931
1932 matrix_t res = gContext.mModelSource * deltaMatrixTranslation;
1933 *(matrix_t*)matrix = res;
1934
1935 if (!io.MouseDown[0])
1936 {
1937 gContext.mbUsing = false;
1938 }
1939
1940 type = gContext.mCurrentOperation;
1941 }
1942 else
1943 {
1944 // find new possible way to move
1945 vec_t gizmoHitProportion;
1946 type = GetMoveType(op, &gizmoHitProportion);
1947 if (type != MT_NONE)
1948 {
1949 ImGui::SetNextFrameWantCaptureMouse(true);
1950 }
1951 if (CanActivate() && type != MT_NONE)
1952 {
1953 gContext.mbUsing = true;
1954 gContext.mEditingID = gContext.mActualID;
1955 gContext.mCurrentOperation = type;
1956 vec_t movePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir,
1957 gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir,
1958 -gContext.mCameraDir };
1959
1960 vec_t cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye);
1961 for (unsigned int i = 0; i < 3; i++)
1962 {
1963 vec_t orthoVector = Cross(movePlanNormal[i], cameraToModelNormalized);
1964 movePlanNormal[i].Cross(orthoVector);
1965 movePlanNormal[i].Normalize();
1966 }
1967 // pickup plan
1968 gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MT_MOVE_X]);
1969 const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
1970 gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len;
1971 gContext.mMatrixOrigin = gContext.mModel.v.position;
1972
1973 gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor);
1974 }
1975 }
1976 return modified;
1977 }
1978
1979 static bool HandleScale(float* matrix, float* deltaMatrix, OPERATION op, int& type, const float* snap)
1980 {
1981 if(!Intersects(op, SCALE) || type != MT_NONE)
1982 {
1983 return false;
1984 }
1985 ImGuiIO& io = ImGui::GetIO();
1986 bool modified = false;
1987
1988 if (!gContext.mbUsing)
1989 {
1990 // find new possible way to scale
1991 type = GetScaleType(op);
1992 if (type != MT_NONE)
1993 {
1994 ImGui::SetNextFrameWantCaptureMouse(true);
1995 }
1996 if (CanActivate() && type != MT_NONE)
1997 {
1998 gContext.mbUsing = true;
1999 gContext.mEditingID = gContext.mActualID;
2000 gContext.mCurrentOperation = type;
2001 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 };
2002 // pickup plan
2003
2004 gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MT_SCALE_X]);
2005 const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
2006 gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len;
2007 gContext.mMatrixOrigin = gContext.mModel.v.position;
2008 gContext.mScale.Set(1.f, 1.f, 1.f);
2009 gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor);
2010 gContext.mScaleValueOrigin = makeVect(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length());
2011 gContext.mSaveMousePosx = io.MousePos.x;
2012 }
2013 }
2014 // scale
2015 if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsScaleType(gContext.mCurrentOperation))
2016 {
2017 ImGui::SetNextFrameWantCaptureMouse(true);
2018 const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
2019 vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
2020 vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor;
2021 vec_t delta = newOrigin - gContext.mModel.v.position;
2022
2023 // 1 axis constraint
2024 if (gContext.mCurrentOperation >= MT_SCALE_X && gContext.mCurrentOperation <= MT_SCALE_Z)
2025 {
2026 int axisIndex = gContext.mCurrentOperation - MT_SCALE_X;
2027 const vec_t& axisValue = *(vec_t*)&gContext.mModel.m[axisIndex];
2028 float lengthOnAxis = Dot(axisValue, delta);
2029 delta = axisValue * lengthOnAxis;
2030
2031 vec_t baseVector = gContext.mTranslationPlanOrigin - gContext.mModel.v.position;
2032 float ratio = Dot(axisValue, baseVector + delta) / Dot(axisValue, baseVector);
2033
2034 gContext.mScale[axisIndex] = max(ratio, 0.001f);
2035 }
2036 else
2037 {
2038 float scaleDelta = (io.MousePos.x - gContext.mSaveMousePosx) * 0.01f;
2039 gContext.mScale.Set(max(1.f + scaleDelta, 0.001f));
2040 }
2041
2042 // snap
2043 if (snap)
2044 {
2045 float scaleSnap[] = { snap[0], snap[0], snap[0] };
2046 ComputeSnap(gContext.mScale, scaleSnap);
2047 }
2048
2049 // no 0 allowed
2050 for (int i = 0; i < 3; i++)
2051 gContext.mScale[i] = max(gContext.mScale[i], 0.001f);
2052
2053 if (gContext.mScaleLast != gContext.mScale)
2054 {
2055 modified = true;
2056 }
2057 gContext.mScaleLast = gContext.mScale;
2058
2059 // compute matrix & delta
2060 matrix_t deltaMatrixScale;
2061 deltaMatrixScale.Scale(gContext.mScale * gContext.mScaleValueOrigin);
2062
2063 matrix_t res = deltaMatrixScale * gContext.mModel;
2064 *(matrix_t*)matrix = res;
2065
2066 if (deltaMatrix)
2067 {
2068 vec_t deltaScale = gContext.mScale * gContext.mScaleValueOrigin;
2069
2070 vec_t originalScaleDivider;
2071 originalScaleDivider.x = 1 / gContext.mModelScaleOrigin.x;
2072 originalScaleDivider.y = 1 / gContext.mModelScaleOrigin.y;
2073 originalScaleDivider.z = 1 / gContext.mModelScaleOrigin.z;
2074
2075 deltaScale = deltaScale * originalScaleDivider;
2076
2077 deltaMatrixScale.Scale(deltaScale);
2078 memcpy(deltaMatrix, deltaMatrixScale.m16, sizeof(float) * 16);
2079 }
2080
2081 if (!io.MouseDown[0])
2082 {
2083 gContext.mbUsing = false;
2084 gContext.mScale.Set(1.f, 1.f, 1.f);
2085 }
2086
2087 type = gContext.mCurrentOperation;
2088 }
2089 return modified;
2090 }
2091
2092 static bool HandleRotation(float* matrix, float* deltaMatrix, OPERATION op, int& type, const float* snap)
2093 {
2094 if(!Intersects(op, ROTATE) || type != MT_NONE)
2095 {
2096 return false;
2097 }
2098 ImGuiIO& io = ImGui::GetIO();
2099 bool applyRotationLocaly = gContext.mMode == LOCAL;
2100 bool modified = false;
2101
2102 if (!gContext.mbUsing)
2103 {
2104 type = GetRotateType(op);
2105
2106 if (type != MT_NONE)
2107 {
2108 ImGui::SetNextFrameWantCaptureMouse(true);
2109 }
2110
2111 if (type == MT_ROTATE_SCREEN)
2112 {
2113 applyRotationLocaly = true;
2114 }
2115
2116 if (CanActivate() && type != MT_NONE)
2117 {
2118 gContext.mbUsing = true;
2119 gContext.mEditingID = gContext.mActualID;
2120 gContext.mCurrentOperation = type;
2121 const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir };
2122 // pickup plan
2123 if (applyRotationLocaly)
2124 {
2125 gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, rotatePlanNormal[type - MT_ROTATE_X]);
2126 }
2127 else
2128 {
2129 gContext.mTranslationPlan = BuildPlan(gContext.mModelSource.v.position, directionUnary[type - MT_ROTATE_X]);
2130 }
2131
2132 const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
2133 vec_t localPos = gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position;
2134 gContext.mRotationVectorSource = Normalized(localPos);
2135 gContext.mRotationAngleOrigin = ComputeAngleOnPlan();
2136 }
2137 }
2138
2139 // rotation
2140 if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsRotateType(gContext.mCurrentOperation))
2141 {
2142 ImGui::SetNextFrameWantCaptureMouse(true);
2143 gContext.mRotationAngle = ComputeAngleOnPlan();
2144 if (snap)
2145 {
2146 float snapInRadian = snap[0] * DEG2RAD;
2147 ComputeSnap(&gContext.mRotationAngle, snapInRadian);
2148 }
2149 vec_t rotationAxisLocalSpace;
2150
2151 rotationAxisLocalSpace.TransformVector(makeVect(gContext.mTranslationPlan.x, gContext.mTranslationPlan.y, gContext.mTranslationPlan.z, 0.f), gContext.mModelInverse);
2152 rotationAxisLocalSpace.Normalize();
2153
2154 matrix_t deltaRotation;
2155 deltaRotation.RotationAxis(rotationAxisLocalSpace, gContext.mRotationAngle - gContext.mRotationAngleOrigin);
2156 if (gContext.mRotationAngle != gContext.mRotationAngleOrigin)
2157 {
2158 modified = true;
2159 }
2160 gContext.mRotationAngleOrigin = gContext.mRotationAngle;
2161
2162 matrix_t scaleOrigin;
2163 scaleOrigin.Scale(gContext.mModelScaleOrigin);
2164
2165 if (applyRotationLocaly)
2166 {
2167 *(matrix_t*)matrix = scaleOrigin * deltaRotation * gContext.mModel;
2168 }
2169 else
2170 {
2171 matrix_t res = gContext.mModelSource;
2172 res.v.position.Set(0.f);
2173
2174 *(matrix_t*)matrix = res * deltaRotation;
2175 ((matrix_t*)matrix)->v.position = gContext.mModelSource.v.position;
2176 }
2177
2178 if (deltaMatrix)
2179 {
2180 *(matrix_t*)deltaMatrix = gContext.mModelInverse * deltaRotation * gContext.mModel;
2181 }
2182
2183 if (!io.MouseDown[0])
2184 {
2185 gContext.mbUsing = false;
2186 gContext.mEditingID = -1;
2187 }
2188 type = gContext.mCurrentOperation;
2189 }
2190 return modified;
2191 }
2192
2193 void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale)
2194 {
2195 matrix_t mat = *(matrix_t*)matrix;
2196
2197 scale[0] = mat.v.right.Length();
2198 scale[1] = mat.v.up.Length();
2199 scale[2] = mat.v.dir.Length();
2200
2201 mat.OrthoNormalize();
2202
2203 rotation[0] = RAD2DEG * atan2f(mat.m[1][2], mat.m[2][2]);
2204 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]));
2205 rotation[2] = RAD2DEG * atan2f(mat.m[0][1], mat.m[0][0]);
2206
2207 translation[0] = mat.v.position.x;
2208 translation[1] = mat.v.position.y;
2209 translation[2] = mat.v.position.z;
2210 }
2211
2212 void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix)
2213 {
2214 matrix_t& mat = *(matrix_t*)matrix;
2215
2216 matrix_t rot[3];
2217 for (int i = 0; i < 3; i++)
2218 {
2219 rot[i].RotationAxis(directionUnary[i], rotation[i] * DEG2RAD);
2220 }
2221
2222 mat = rot[0] * rot[1] * rot[2];
2223
2224 float validScale[3];
2225 for (int i = 0; i < 3; i++)
2226 {
2227 if (fabsf(scale[i]) < FLT_EPSILON)
2228 {
2229 validScale[i] = 0.001f;
2230 }
2231 else
2232 {
2233 validScale[i] = scale[i];
2234 }
2235 }
2236 mat.v.right *= validScale[0];
2237 mat.v.up *= validScale[1];
2238 mat.v.dir *= validScale[2];
2239 mat.v.position.Set(translation[0], translation[1], translation[2], 1.f);
2240 }
2241
2242 void SetID(int id)
2243 {
2244 gContext.mActualID = id;
2245 }
2246
2247 void AllowAxisFlip(bool value)
2248 {
2249 gContext.mAllowAxisFlip = value;
2250 }
2251
2252 bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix, const float* snap, const float* localBounds, const float* boundsSnap)
2253 {
2254 ComputeContext(view, projection, matrix, mode);
2255
2256 // set delta to identity
2257 if (deltaMatrix)
2258 {
2259 ((matrix_t*)deltaMatrix)->SetToIdentity();
2260 }
2261
2262 // behind camera
2263 vec_t camSpacePosition;
2264 camSpacePosition.TransformPoint(makeVect(0.f, 0.f, 0.f), gContext.mMVP);
2265 if (!gContext.mIsOrthographic && camSpacePosition.z < 0.001f)
2266 {
2267 return false;
2268 }
2269
2270 // --
2271 int type = MT_NONE;
2272 bool manipulated = false;
2273 if (gContext.mbEnable)
2274 {
2275 if (!gContext.mbUsingBounds)
2276 {
2277 manipulated = HandleTranslation(matrix, deltaMatrix, operation, type, snap) ||
2278 HandleScale(matrix, deltaMatrix, operation, type, snap) ||
2279 HandleRotation(matrix, deltaMatrix, operation, type, snap);
2280 }
2281 }
2282
2283 if (localBounds && !gContext.mbUsing)
2284 {
2285 HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap, operation);
2286 }
2287
2288 gContext.mOperation = operation;
2289 if (!gContext.mbUsingBounds)
2290 {
2291 DrawRotationGizmo(operation, type);
2292 DrawTranslationGizmo(operation, type);
2293 DrawScaleGizmo(operation, type);
2294 }
2295 return manipulated;
2296 }
2297
2298 void SetGizmoSizeClipSpace(float value)
2299 {
2300 gContext.mGizmoSizeClipSpace = value;
2301 }
2302
2304 void ComputeFrustumPlanes(vec_t* frustum, const float* clip)
2305 {
2306 frustum[0].x = clip[3] - clip[0];
2307 frustum[0].y = clip[7] - clip[4];
2308 frustum[0].z = clip[11] - clip[8];
2309 frustum[0].w = clip[15] - clip[12];
2310
2311 frustum[1].x = clip[3] + clip[0];
2312 frustum[1].y = clip[7] + clip[4];
2313 frustum[1].z = clip[11] + clip[8];
2314 frustum[1].w = clip[15] + clip[12];
2315
2316 frustum[2].x = clip[3] + clip[1];
2317 frustum[2].y = clip[7] + clip[5];
2318 frustum[2].z = clip[11] + clip[9];
2319 frustum[2].w = clip[15] + clip[13];
2320
2321 frustum[3].x = clip[3] - clip[1];
2322 frustum[3].y = clip[7] - clip[5];
2323 frustum[3].z = clip[11] - clip[9];
2324 frustum[3].w = clip[15] - clip[13];
2325
2326 frustum[4].x = clip[3] - clip[2];
2327 frustum[4].y = clip[7] - clip[6];
2328 frustum[4].z = clip[11] - clip[10];
2329 frustum[4].w = clip[15] - clip[14];
2330
2331 frustum[5].x = clip[3] + clip[2];
2332 frustum[5].y = clip[7] + clip[6];
2333 frustum[5].z = clip[11] + clip[10];
2334 frustum[5].w = clip[15] + clip[14];
2335
2336 for (int i = 0; i < 6; i++)
2337 {
2338 frustum[i].Normalize();
2339 }
2340 }
2341
2342 void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount)
2343 {
2344 matrix_t viewInverse;
2345 viewInverse.Inverse(*(matrix_t*)view);
2346
2347 struct CubeFace
2348 {
2349 float z;
2350 ImVec2 faceCoordsScreen[4];
2351 ImU32 color;
2352 };
2353 CubeFace* faces = (CubeFace*)_malloca(sizeof(CubeFace) * matrixCount * 6);
2354
2355 if (!faces)
2356 {
2357 return;
2358 }
2359
2360 vec_t frustum[6];
2361 matrix_t viewProjection = *(matrix_t*)view * *(matrix_t*)projection;
2362 ComputeFrustumPlanes(frustum, viewProjection.m16);
2363
2364 int cubeFaceCount = 0;
2365 for (int cube = 0; cube < matrixCount; cube++)
2366 {
2367 const float* matrix = &matrices[cube * 16];
2368
2369 matrix_t res = *(matrix_t*)matrix * *(matrix_t*)view * *(matrix_t*)projection;
2370
2371 for (int iFace = 0; iFace < 6; iFace++)
2372 {
2373 const int normalIndex = (iFace % 3);
2374 const int perpXIndex = (normalIndex + 1) % 3;
2375 const int perpYIndex = (normalIndex + 2) % 3;
2376 const float invert = (iFace > 2) ? -1.f : 1.f;
2377
2378 const vec_t faceCoords[4] = { directionUnary[normalIndex] + directionUnary[perpXIndex] + directionUnary[perpYIndex],
2379 directionUnary[normalIndex] + directionUnary[perpXIndex] - directionUnary[perpYIndex],
2380 directionUnary[normalIndex] - directionUnary[perpXIndex] - directionUnary[perpYIndex],
2381 directionUnary[normalIndex] - directionUnary[perpXIndex] + directionUnary[perpYIndex],
2382 };
2383
2384 // clipping
2385 /*
2386 bool skipFace = false;
2387 for (unsigned int iCoord = 0; iCoord < 4; iCoord++)
2388 {
2389 vec_t camSpacePosition;
2390 camSpacePosition.TransformPoint(faceCoords[iCoord] * 0.5f * invert, res);
2391 if (camSpacePosition.z < 0.001f)
2392 {
2393 skipFace = true;
2394 break;
2395 }
2396 }
2397 if (skipFace)
2398 {
2399 continue;
2400 }
2401 */
2402 vec_t centerPosition, centerPositionVP;
2403 centerPosition.TransformPoint(directionUnary[normalIndex] * 0.5f * invert, *(matrix_t*)matrix);
2404 centerPositionVP.TransformPoint(directionUnary[normalIndex] * 0.5f * invert, res);
2405
2406 bool inFrustum = true;
2407 for (int iFrustum = 0; iFrustum < 6; iFrustum++)
2408 {
2409 float dist = DistanceToPlane(centerPosition, frustum[iFrustum]);
2410 if (dist < 0.f)
2411 {
2412 inFrustum = false;
2413 break;
2414 }
2415 }
2416
2417 if (!inFrustum)
2418 {
2419 continue;
2420 }
2421 CubeFace& cubeFace = faces[cubeFaceCount];
2422
2423 // 3D->2D
2424 //ImVec2 faceCoordsScreen[4];
2425 for (unsigned int iCoord = 0; iCoord < 4; iCoord++)
2426 {
2427 cubeFace.faceCoordsScreen[iCoord] = worldToPos(faceCoords[iCoord] * 0.5f * invert, res);
2428 }
2429 cubeFace.color = directionColor[normalIndex] | IM_COL32(0x80, 0x80, 0x80, 0);
2430
2431 cubeFace.z = centerPositionVP.z / centerPositionVP.w;
2432 cubeFaceCount++;
2433 }
2434 }
2435 qsort(faces, cubeFaceCount, sizeof(CubeFace), [](void const* _a, void const* _b) {
2436 CubeFace* a = (CubeFace*)_a;
2437 CubeFace* b = (CubeFace*)_b;
2438 if (a->z < b->z)
2439 {
2440 return 1;
2441 }
2442 return -1;
2443 });
2444 // draw face with lighter color
2445 for (int iFace = 0; iFace < cubeFaceCount; iFace++)
2446 {
2447 const CubeFace& cubeFace = faces[iFace];
2448 gContext.mDrawList->AddConvexPolyFilled(cubeFace.faceCoordsScreen, 4, cubeFace.color);
2449 }
2450
2451 _freea(faces);
2452 }
2453
2454 void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize)
2455 {
2456 matrix_t viewProjection = *(matrix_t*)view * *(matrix_t*)projection;
2457 vec_t frustum[6];
2458 ComputeFrustumPlanes(frustum, viewProjection.m16);
2459 matrix_t res = *(matrix_t*)matrix * viewProjection;
2460
2461 for (float f = -gridSize; f <= gridSize; f += 1.f)
2462 {
2463 for (int dir = 0; dir < 2; dir++)
2464 {
2465 vec_t ptA = makeVect(dir ? -gridSize : f, 0.f, dir ? f : -gridSize);
2466 vec_t ptB = makeVect(dir ? gridSize : f, 0.f, dir ? f : gridSize);
2467 bool visible = true;
2468 for (int i = 0; i < 6; i++)
2469 {
2470 float dA = DistanceToPlane(ptA, frustum[i]);
2471 float dB = DistanceToPlane(ptB, frustum[i]);
2472 if (dA < 0.f && dB < 0.f)
2473 {
2474 visible = false;
2475 break;
2476 }
2477 if (dA > 0.f && dB > 0.f)
2478 {
2479 continue;
2480 }
2481 if (dA < 0.f)
2482 {
2483 float len = fabsf(dA - dB);
2484 float t = fabsf(dA) / len;
2485 ptA.Lerp(ptB, t);
2486 }
2487 if (dB < 0.f)
2488 {
2489 float len = fabsf(dB - dA);
2490 float t = fabsf(dB) / len;
2491 ptB.Lerp(ptA, t);
2492 }
2493 }
2494 if (visible)
2495 {
2496 ImU32 col = IM_COL32(0x80, 0x80, 0x80, 0xFF);
2497 col = (fmodf(fabsf(f), 10.f) < FLT_EPSILON) ? IM_COL32(0x90, 0x90, 0x90, 0xFF) : col;
2498 col = (fabsf(f) < FLT_EPSILON) ? IM_COL32(0x40, 0x40, 0x40, 0xFF): col;
2499
2500 float thickness = 1.f;
2501 thickness = (fmodf(fabsf(f), 10.f) < FLT_EPSILON) ? 1.5f : thickness;
2502 thickness = (fabsf(f) < FLT_EPSILON) ? 2.3f : thickness;
2503
2504 gContext.mDrawList->AddLine(worldToPos(ptA, res), worldToPos(ptB, res), col, thickness);
2505 }
2506 }
2507 }
2508 }
2509
2510 void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor)
2511 {
2512 static bool isDraging = false;
2513 static bool isClicking = false;
2514 static bool isInside = false;
2515 static vec_t interpolationUp;
2516 static vec_t interpolationDir;
2517 static int interpolationFrames = 0;
2518 const vec_t referenceUp = makeVect(0.f, 1.f, 0.f);
2519
2520 matrix_t svgView, svgProjection;
2521 svgView = gContext.mViewMat;
2522 svgProjection = gContext.mProjectionMat;
2523
2524 ImGuiIO& io = ImGui::GetIO();
2525 gContext.mDrawList->AddRectFilled(position, position + size, backgroundColor);
2526 matrix_t viewInverse;
2527 viewInverse.Inverse(*(matrix_t*)view);
2528
2529 const vec_t camTarget = viewInverse.v.position - viewInverse.v.dir * length;
2530
2531 // view/projection matrices
2532 const float distance = 3.f;
2533 matrix_t cubeProjection, cubeView;
2534 float fov = acosf(distance / (sqrtf(distance * distance + 3.f))) * RAD2DEG;
2535 Perspective(fov / sqrtf(2.f), size.x / size.y, 0.01f, 1000.f, cubeProjection.m16);
2536
2537 vec_t dir = makeVect(viewInverse.m[2][0], viewInverse.m[2][1], viewInverse.m[2][2]);
2538 vec_t up = makeVect(viewInverse.m[1][0], viewInverse.m[1][1], viewInverse.m[1][2]);
2539 vec_t eye = dir * distance;
2540 vec_t zero = makeVect(0.f, 0.f);
2541 LookAt(&eye.x, &zero.x, &up.x, cubeView.m16);
2542
2543 // set context
2544 gContext.mViewMat = cubeView;
2545 gContext.mProjectionMat = cubeProjection;
2546 ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector, position, size);
2547
2548 const matrix_t res = cubeView * cubeProjection;
2549
2550 // panels
2551 static const ImVec2 panelPosition[9] = { ImVec2(0.75f,0.75f), ImVec2(0.25f, 0.75f), ImVec2(0.f, 0.75f),
2552 ImVec2(0.75f, 0.25f), ImVec2(0.25f, 0.25f), ImVec2(0.f, 0.25f),
2553 ImVec2(0.75f, 0.f), ImVec2(0.25f, 0.f), ImVec2(0.f, 0.f) };
2554
2555 static const ImVec2 panelSize[9] = { ImVec2(0.25f,0.25f), ImVec2(0.5f, 0.25f), ImVec2(0.25f, 0.25f),
2556 ImVec2(0.25f, 0.5f), ImVec2(0.5f, 0.5f), ImVec2(0.25f, 0.5f),
2557 ImVec2(0.25f, 0.25f), ImVec2(0.5f, 0.25f), ImVec2(0.25f, 0.25f) };
2558
2559 // tag faces
2560 bool boxes[27]{};
2561 for (int iPass = 0; iPass < 2; iPass++)
2562 {
2563 for (int iFace = 0; iFace < 6; iFace++)
2564 {
2565 const int normalIndex = (iFace % 3);
2566 const int perpXIndex = (normalIndex + 1) % 3;
2567 const int perpYIndex = (normalIndex + 2) % 3;
2568 const float invert = (iFace > 2) ? -1.f : 1.f;
2569 const vec_t indexVectorX = directionUnary[perpXIndex] * invert;
2570 const vec_t indexVectorY = directionUnary[perpYIndex] * invert;
2571 const vec_t boxOrigin = directionUnary[normalIndex] * -invert - indexVectorX - indexVectorY;
2572
2573 // plan local space
2574 const vec_t n = directionUnary[normalIndex] * invert;
2575 vec_t viewSpaceNormal = n;
2576 vec_t viewSpacePoint = n * 0.5f;
2577 viewSpaceNormal.TransformVector(cubeView);
2578 viewSpaceNormal.Normalize();
2579 viewSpacePoint.TransformPoint(cubeView);
2580 const vec_t viewSpaceFacePlan = BuildPlan(viewSpacePoint, viewSpaceNormal);
2581
2582 // back face culling
2583 if (viewSpaceFacePlan.w > 0.f)
2584 {
2585 continue;
2586 }
2587
2588 const vec_t facePlan = BuildPlan(n * 0.5f, n);
2589
2590 const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, facePlan);
2591 vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len - (n * 0.5f);
2592
2593 float localx = Dot(directionUnary[perpXIndex], posOnPlan) * invert + 0.5f;
2594 float localy = Dot(directionUnary[perpYIndex], posOnPlan) * invert + 0.5f;
2595
2596 // panels
2597 const vec_t dx = directionUnary[perpXIndex];
2598 const vec_t dy = directionUnary[perpYIndex];
2599 const vec_t origin = directionUnary[normalIndex] - dx - dy;
2600 for (int iPanel = 0; iPanel < 9; iPanel++)
2601 {
2602 vec_t boxCoord = boxOrigin + indexVectorX * float(iPanel % 3) + indexVectorY * float(iPanel / 3) + makeVect(1.f, 1.f, 1.f);
2603 const ImVec2 p = panelPosition[iPanel] * 2.f;
2604 const ImVec2 s = panelSize[iPanel] * 2.f;
2605 ImVec2 faceCoordsScreen[4];
2606 vec_t panelPos[4] = { dx * p.x + dy * p.y,
2607 dx * p.x + dy * (p.y + s.y),
2608 dx * (p.x + s.x) + dy * (p.y + s.y),
2609 dx * (p.x + s.x) + dy * p.y };
2610
2611 for (unsigned int iCoord = 0; iCoord < 4; iCoord++)
2612 {
2613 faceCoordsScreen[iCoord] = worldToPos((panelPos[iCoord] + origin) * 0.5f * invert, res, position, size);
2614 }
2615
2616 const ImVec2 panelCorners[2] = { panelPosition[iPanel], panelPosition[iPanel] + panelSize[iPanel] };
2617 bool insidePanel = localx > panelCorners[0].x && localx < panelCorners[1].x&& localy > panelCorners[0].y && localy < panelCorners[1].y;
2618 int boxCoordInt = int(boxCoord.x * 9.f + boxCoord.y * 3.f + boxCoord.z);
2619 assert(boxCoordInt < 27);
2620 boxes[boxCoordInt] |= insidePanel && (!isDraging);
2621
2622 // draw face with lighter color
2623 if (iPass)
2624 {
2625 gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, (directionColor[normalIndex] | IM_COL32(0x80, 0x80, 0x80, 0x80)) | (isInside ? IM_COL32(0x08, 0x08, 0x08, 0) : 0));
2626 if (boxes[boxCoordInt])
2627 {
2628 gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, IM_COL32(0xF0, 0xA0, 0x60, 0x80));
2629
2630 if (!io.MouseDown[0] && !isDraging && isClicking)
2631 {
2632 // apply new view direction
2633 int cx = boxCoordInt / 9;
2634 int cy = (boxCoordInt - cx * 9) / 3;
2635 int cz = boxCoordInt % 3;
2636 interpolationDir = makeVect(1.f - cx, 1.f - cy, 1.f - cz);
2637 interpolationDir.Normalize();
2638
2639 if (fabsf(Dot(interpolationDir, referenceUp)) > 1.0f - 0.01f)
2640 {
2641 vec_t right = viewInverse.v.right;
2642 if (fabsf(right.x) > fabsf(right.z))
2643 {
2644 right.z = 0.f;
2645 }
2646 else
2647 {
2648 right.x = 0.f;
2649 }
2650 right.Normalize();
2651 interpolationUp = Cross(interpolationDir, right);
2652 interpolationUp.Normalize();
2653 }
2654 else
2655 {
2656 interpolationUp = referenceUp;
2657 }
2658 interpolationFrames = 40;
2659 isClicking = false;
2660 }
2661 if (io.MouseDown[0] && !isDraging)
2662 {
2663 isClicking = true;
2664 }
2665 }
2666 }
2667 }
2668 }
2669 }
2670 if (interpolationFrames)
2671 {
2672 interpolationFrames--;
2673 vec_t newDir = viewInverse.v.dir;
2674 newDir.Lerp(interpolationDir, 0.2f);
2675 newDir.Normalize();
2676
2677 vec_t newUp = viewInverse.v.up;
2678 newUp.Lerp(interpolationUp, 0.3f);
2679 newUp.Normalize();
2680 newUp = interpolationUp;
2681 vec_t newEye = camTarget + newDir * length;
2682 LookAt(&newEye.x, &camTarget.x, &newUp.x, view);
2683 }
2684 isInside = ImRect(position, position + size).Contains(io.MousePos);
2685
2686 // drag view
2687 if (!isDraging && io.MouseDown[0] && isInside && (fabsf(io.MouseDelta.x) > 0.f || fabsf(io.MouseDelta.y) > 0.f))
2688 {
2689 isDraging = true;
2690 isClicking = false;
2691 }
2692 else if (isDraging && !io.MouseDown[0])
2693 {
2694 isDraging = false;
2695 }
2696
2697 if (isDraging)
2698 {
2699 matrix_t rx, ry, roll;
2700
2701 rx.RotationAxis(referenceUp, -io.MouseDelta.x * 0.01f);
2702 ry.RotationAxis(viewInverse.v.right, -io.MouseDelta.y * 0.01f);
2703
2704 roll = rx * ry;
2705
2706 vec_t newDir = viewInverse.v.dir;
2707 newDir.TransformVector(roll);
2708 newDir.Normalize();
2709
2710 // clamp
2711 vec_t planDir = Cross(viewInverse.v.right, referenceUp);
2712 planDir.y = 0.f;
2713 planDir.Normalize();
2714 float dt = Dot(planDir, newDir);
2715 if (dt < 0.0f)
2716 {
2717 newDir += planDir * dt;
2718 newDir.Normalize();
2719 }
2720
2721 vec_t newEye = camTarget + newDir * length;
2722 LookAt(&newEye.x, &camTarget.x, &referenceUp.x, view);
2723 }
2724
2725 // restore view/projection because it was used to compute ray
2726 ComputeContext(svgView.m16, svgProjection.m16, gContext.mModelSource.m16, gContext.mMode);
2727 }
2728};
#define _malloca(x)
Definition ImGuizmo.cpp:36
#define _freea(x)
Definition ImGuizmo.cpp:37
assert(queueCount >=1)
#define V(a, b, c)
#define T(t)
void SetGizmoSizeClipSpace(float value)
static const float DEG2RAD
Definition ImGuizmo.cpp:47
static void ComputeColors(ImU32 *colors, int type, OPERATION operation)
static const char * rotationInfoMask[]
Definition ImGuizmo.cpp:748
void ComputeFrustumPlanes(vec_t *frustum, const float *clip)
static const ImU32 translationLineColor
Definition ImGuizmo.cpp:743
bool IsOver()
Definition ImGuizmo.cpp:941
T max(T x, T y)
Definition ImGuizmo.cpp:188
void SetRect(float x, float y, float width, float height)
Definition ImGuizmo.cpp:886
static void DrawTranslationGizmo(OPERATION op, int type)
const float screenRotateSize
Definition ImGuizmo.cpp:48
void SetID(int id)
static bool IsScaleType(int type)
Definition ImGuizmo.cpp:633
vec_t Normalized(const vec_t &v)
Definition ImGuizmo.cpp:280
static void DrawRotationGizmo(OPERATION op, int type)
static bool Intersects(OPERATION lhs, OPERATION rhs)
Definition ImGuizmo.cpp:65
static bool IsTranslateType(int type)
Definition ImGuizmo.cpp:623
static void HandleAndDrawLocalBounds(const float *bounds, matrix_t *matrix, const float *snapValues, OPERATION operation)
bool IsWithin(T x, T y, T z)
Definition ImGuizmo.cpp:190
static float DistanceToPlane(const vec_t &point, const vec_t &plan)
Definition ImGuizmo.cpp:876
void AllowAxisFlip(bool value)
static void ComputeCameraRay(vec_t &rayOrigin, vec_t &rayDir, ImVec2 position=ImVec2(gContext.mX, gContext.mY), ImVec2 size=ImVec2(gContext.mWidth, gContext.mHeight))
Definition ImGuizmo.cpp:776
bool Manipulate(const float *view, const float *projection, OPERATION operation, MODE mode, float *matrix, float *deltaMatrix, const float *snap, const float *localBounds, const float *boundsSnap)
@ TRANSLATE_Y
Definition ImGuizmo.h:169
@ ROTATE_SCREEN
Definition ImGuizmo.h:174
@ TRANSLATE
Definition ImGuizmo.h:179
@ TRANSLATE_Z
Definition ImGuizmo.h:170
@ TRANSLATE_X
Definition ImGuizmo.h:168
static float ComputeAngleOnPlan()
static int GetScaleType(OPERATION op)
static bool IsInContextRect(ImVec2 p)
Definition ImGuizmo.cpp:881
T min(T x, T y)
Definition ImGuizmo.cpp:189
static const float ZPI
Definition ImGuizmo.cpp:45
static bool Contains(OPERATION lhs, OPERATION rhs)
Definition ImGuizmo.cpp:71
static bool IsRotateType(int type)
Definition ImGuizmo.cpp:628
void Cross(const float *a, const float *b, float *r)
Definition ImGuizmo.cpp:135
static const ImU32 directionColor[3]
Definition ImGuizmo.cpp:737
T Clamp(T x, T y, T z)
Definition ImGuizmo.cpp:187
void SetImGuiContext(ImGuiContext *ctx)
Definition ImGuizmo.cpp:907
static const vec_t directionUnary[3]
Definition ImGuizmo.cpp:736
vec_t makeVect(float _x, float _y, float _z=0.f, float _w=0.f)
Definition ImGuizmo.cpp:271
static float GetSegmentLengthClipSpace(const vec_t &start, const vec_t &end)
Definition ImGuizmo.cpp:797
void FPU_MatrixF_x_MatrixF(const float *a, const float *b, float *r)
Definition ImGuizmo.cpp:79
bool IsUsing()
Definition ImGuizmo.cpp:936
static float GetParallelogram(const vec_t &ptO, const vec_t &ptA, const vec_t &ptB)
Definition ImGuizmo.cpp:819
static bool HandleScale(float *matrix, float *deltaMatrix, OPERATION op, int &type, const float *snap)
static bool HandleTranslation(float *matrix, float *deltaMatrix, OPERATION op, int &type, const float *snap)
void Perspective(float fovyInDegrees, float aspectRatio, float znear, float zfar, float *m16)
Definition ImGuizmo.cpp:127
static const char * scaleInfoMask[]
Definition ImGuizmo.cpp:747
static bool operator==(OPERATION lhs, int rhs)
Definition ImGuizmo.cpp:60
void SetOrthographic(bool isOrthographic)
Definition ImGuizmo.cpp:897
static const float quadUV[8]
Definition ImGuizmo.cpp:752
static bool HandleRotation(float *matrix, float *deltaMatrix, OPERATION op, int &type, const float *snap)
static void ComputeSnap(float *value, float snap)
vec_t BuildPlan(const vec_t &p_point1, const vec_t &p_normal)
Definition ImGuizmo.cpp:296
static bool CanActivate()
static int GetRotateType(OPERATION op)
static Context gContext
Definition ImGuizmo.cpp:734
static OPERATION operator&(OPERATION lhs, OPERATION rhs)
Definition ImGuizmo.cpp:50
void Enable(bool enable)
Definition ImGuizmo.cpp:969
@ MT_ROTATE_SCREEN
Definition ImGuizmo.cpp:616
@ MT_MOVE_SCREEN
Definition ImGuizmo.cpp:612
void DrawCubes(const float *view, const float *projection, const float *matrices, int matrixCount)
static void DrawHatchedAxis(const vec_t &axis)
static bool operator!=(OPERATION lhs, int rhs)
Definition ImGuizmo.cpp:55
float Dot(const float *a, const float *b)
Definition ImGuizmo.cpp:142
static const float quadMin
Definition ImGuizmo.cpp:750
static int GetMoveType(OPERATION op, vec_t *gizmoHitProportion)
static ImVec2 worldToPos(const vec_t &worldPos, const matrix_t &mat, ImVec2 position=ImVec2(gContext.mX, gContext.mY), ImVec2 size=ImVec2(gContext.mWidth, gContext.mHeight))
Definition ImGuizmo.cpp:762
void DrawGrid(const float *view, const float *projection, const float *matrix, const float gridSize)
static void ComputeTripodAxisAndVisibility(int axisIndex, vec_t &dirAxis, vec_t &dirPlaneX, vec_t &dirPlaneY, bool &belowAxisLimit, bool &belowPlaneLimit)
void BeginFrame()
Definition ImGuizmo.cpp:912
void DecomposeMatrixToComponents(const float *matrix, float *translation, float *rotation, float *scale)
void RecomposeMatrixFromComponents(const float *translation, const float *rotation, const float *scale, float *matrix)
void SetDrawlist(ImDrawList *drawlist)
Definition ImGuizmo.cpp:902
static const int halfCircleSegmentCount
Definition ImGuizmo.cpp:753
void Normalize(const float *a, float *r)
Definition ImGuizmo.cpp:147
static const float quadMax
Definition ImGuizmo.cpp:751
static const int translationInfoIndex[]
Definition ImGuizmo.cpp:749
static void DrawScaleGizmo(OPERATION op, int type)
static const float RAD2DEG
Definition ImGuizmo.cpp:46
static const ImU32 planeColor[3]
Definition ImGuizmo.cpp:740
static void ComputeContext(const float *view, const float *projection, float *matrix, MODE mode)
Definition ImGuizmo.cpp:979
void LookAt(const float *eye, const float *at, const float *up, float *m16)
Definition ImGuizmo.cpp:155
void Frustum(float left, float right, float bottom, float top, float znear, float zfar, float *m16)
Definition ImGuizmo.cpp:102
static const ImU32 selectionColor
Definition ImGuizmo.cpp:741
static const OPERATION TRANSLATE_PLANS[3]
Definition ImGuizmo.cpp:639
static const char * translationInfoMask[]
Definition ImGuizmo.cpp:744
static const float snapTension
Definition ImGuizmo.cpp:754
void ViewManipulate(float *view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor)
static const ImU32 inactiveColor
Definition ImGuizmo.cpp:742
vec_t PointOnSegment(const vec_t &point, const vec_t &vertPos1, const vec_t &vertPos2)
Definition ImGuizmo.cpp:841
static float IntersectRayPlane(const vec_t &rOrigin, const vec_t &rVector, const vec_t &plan)
Definition ImGuizmo.cpp:863
matrix_t mBoundsMatrix
Definition ImGuizmo.cpp:711
ImVec2 mScreenSquareCenter
Definition ImGuizmo.cpp:668
float mAxisFactor[3]
Definition ImGuizmo.cpp:701
vec_t mTranslationPlanOrigin
Definition ImGuizmo.cpp:682
matrix_t mModelSourceInverse
Definition ImGuizmo.cpp:655
float mRotationAngleOrigin
Definition ImGuizmo.cpp:689
matrix_t mViewProjection
Definition ImGuizmo.cpp:657
ImVec2 mScreenSquareMin
Definition ImGuizmo.cpp:669
matrix_t mModelInverse
Definition ImGuizmo.cpp:653
bool mBelowPlaneLimit[3]
Definition ImGuizmo.cpp:700
float mRadiusSquareCenter
Definition ImGuizmo.cpp:667
float mGizmoSizeClipSpace
Definition ImGuizmo.cpp:731
vec_t mRotationVectorSource
Definition ImGuizmo.cpp:687
vec_t mTranslationLastDelta
Definition ImGuizmo.cpp:684
matrix_t mModelSource
Definition ImGuizmo.cpp:654
ImDrawList * mDrawList
Definition ImGuizmo.cpp:647
ImVec2 mScreenSquareMax
Definition ImGuizmo.cpp:670
OPERATION mOperation
Definition ImGuizmo.cpp:728
matrix_t mProjectionMat
Definition ImGuizmo.cpp:651
bool mBelowAxisLimit[3]
Definition ImGuizmo.cpp:699
void Translation(const vec_t &vt)
Definition ImGuizmo.cpp:329
matrix_t operator*(const matrix_t &mat) const
Definition ImGuizmo.cpp:354
void Translation(float _x, float _y, float _z)
Definition ImGuizmo.cpp:327
void Scale(const vec_t &s)
Definition ImGuizmo.cpp:344
void Multiply(const matrix_t &matrix)
Definition ImGuizmo.cpp:361
void Multiply(const matrix_t &m1, const matrix_t &m2)
Definition ImGuizmo.cpp:369
matrix_t(const matrix_t &other)
Definition ImGuizmo.cpp:322
float GetDeterminant() const
Definition ImGuizmo.cpp:374
float Inverse(const matrix_t &srcMatrix, bool affine=false)
Definition ImGuizmo.cpp:462
matrix_t & operator*=(const matrix_t &mat)
Definition ImGuizmo.cpp:346
void Scale(float _x, float _y, float _z)
Definition ImGuizmo.cpp:337
void RotationAxis(const vec_t &axis, float angle)
Definition ImGuizmo.cpp:558
struct ImGuizmo::matrix_t::@361143343175260347357032320102377242354047307227::@301362307350360221377203214104274101264331001140 v
void Cross(const vec_t &v)
Definition ImGuizmo.cpp:227
vec_t & operator*=(const vec_t &v)
Definition ImGuizmo.cpp:211
void Set(float _x, float _y, float _z=0.f, float _w=0.f)
Definition ImGuizmo.cpp:207
void Lerp(const vec_t &v, float t)
Definition ImGuizmo.cpp:198
void Set(float v)
Definition ImGuizmo.cpp:206
vec_t Normalize(const vec_t &v)
Definition ImGuizmo.cpp:224
float Dot(const vec_t &v) const
Definition ImGuizmo.cpp:248
float Length() const
Definition ImGuizmo.cpp:221
const vec_t & operator+() const
Definition ImGuizmo.cpp:220
vec_t & operator+=(const vec_t &v)
Definition ImGuizmo.cpp:210
void TransformPoint(const vec_t &v, const matrix_t &matrix)
Definition ImGuizmo.cpp:264
vec_t & operator-=(const vec_t &v)
Definition ImGuizmo.cpp:209
bool operator!=(const vec_t &other) const
Definition ImGuizmo.cpp:268
void TransformPoint(const matrix_t &matrix)
Definition ImGuizmo.cpp:432
float & operator[](size_t index)
Definition ImGuizmo.cpp:266
void TransformVector(const vec_t &v, const matrix_t &matrix)
Definition ImGuizmo.cpp:263
void Transform(const matrix_t &matrix)
Definition ImGuizmo.cpp:411
vec_t operator-() const
Definition ImGuizmo.cpp:274
vec_t Abs() const
Definition ImGuizmo.cpp:278
void Cross(const vec_t &v1, const vec_t &v2)
Definition ImGuizmo.cpp:240
vec_t operator*(float f) const
Definition ImGuizmo.cpp:273
float LengthSq() const
Definition ImGuizmo.cpp:222
vec_t Normalize()
Definition ImGuizmo.cpp:223
float Dot3(const vec_t &v) const
Definition ImGuizmo.cpp:253
void TransformVector(const matrix_t &matrix)
Definition ImGuizmo.cpp:447
#define max(x, y)
Definition svd3_cuda.h:41