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