1 /** 2 2D Transformation. 3x2 matrix. 3 4 Copyright: 5 Copyright (c) 2007 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017 Godot-D contributors 8 Copyright (c) 2022 Godot-DLang contributors 9 10 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 11 12 13 */ 14 module godot.projection; 15 16 import godot.abi.types; 17 import godot.vector4; 18 import godot.vector2; 19 import godot.vector3; 20 import godot.rect2; 21 import godot.plane; 22 import godot.aabb; 23 import godot.array; 24 import godot.transform; 25 import godot.abi.core; 26 27 import std.math; 28 import std.algorithm.comparison; 29 import std.algorithm.mutation : swap; 30 31 double deg2rad(double p_y) @nogc nothrow { 32 return p_y * PI / 180.0; 33 } 34 35 double rad2deg(double p_y) @nogc nothrow { 36 return p_y * 180.0 / PI; 37 } 38 39 /** 40 Represents projection transformation. It is similar to a 4x4 matrix. 41 */ 42 struct Projection { 43 // Array prohibits this, comment for now 44 //@nogc nothrow: 45 46 enum Planes { 47 near, 48 far, 49 left, 50 top, 51 right, 52 bottom 53 } 54 // Convenience aliases 55 alias PLANE_NEAR = Planes.near; 56 alias PLANE_FAR = Planes.far; 57 alias PLANE_LEFT = Planes.left; 58 alias PLANE_TOP = Planes.top; 59 alias PLANE_RIGHT = Planes.right; 60 alias PLANE_BOTTOM = Planes.bottom; 61 62 union { 63 Vector4[4] matrix = [ 64 Vector4(1, 0, 0, 0), Vector4(0, 1, 0, 0), Vector4(0, 0, 1, 0), 65 Vector4(0, 0, 0, 1) 66 ]; 67 struct { 68 Vector4 x_axis; /// 69 Vector4 y_axis; /// 70 Vector4 z_axis; /// 71 Vector4 w_axis; /// 72 } 73 74 real_t[16] elements; 75 } 76 77 private alias m = elements; 78 79 this(Vector4 x, Vector4 y, Vector4 z, Vector4 w) { 80 matrix[0] = x; 81 matrix[1] = y; 82 matrix[2] = z; 83 matrix[3] = w; 84 } 85 86 this(real_t[16] mat) { 87 elements = mat; 88 } 89 90 this(in Transform3D p_transform) { 91 alias tr = p_transform; 92 93 m[0] = tr.basis.rows[0][0]; 94 m[1] = tr.basis.rows[1][0]; 95 m[2] = tr.basis.rows[2][0]; 96 m[3] = 0.0; 97 m[4] = tr.basis.rows[0][1]; 98 m[5] = tr.basis.rows[1][1]; 99 m[6] = tr.basis.rows[2][1]; 100 m[7] = 0.0; 101 m[8] = tr.basis.rows[0][2]; 102 m[9] = tr.basis.rows[1][2]; 103 m[10] = tr.basis.rows[2][2]; 104 m[11] = 0.0; 105 m[12] = tr.origin.x; 106 m[13] = tr.origin.y; 107 m[14] = tr.origin.z; 108 m[15] = 1.0; 109 } 110 111 const(Vector4) opIndex(int axis) const { 112 return matrix[axis]; 113 } 114 115 ref Vector4 opIndex(int axis) return { 116 return matrix[axis]; 117 } 118 119 float determinant() const { 120 return matrix[0][3] * matrix[1][2] * matrix[2][1] * matrix[3][0] - matrix[0][2] * matrix[1][3] * matrix[2][1] * matrix[3][0] - 121 matrix[0][3] * matrix[1][1] * matrix[2][2] * matrix[3][0] + matrix[0][1] * matrix[1][3] * matrix[2][2] * matrix[3][0] + 122 matrix[0][2] * matrix[1][1] * matrix[2][3] * matrix[3][0] - matrix[0][1] * matrix[1][2] * matrix[2][3] * matrix[3][0] - 123 matrix[0][3] * matrix[1][2] * matrix[2][0] * matrix[3][1] + matrix[0][2] * matrix[1][3] * matrix[2][0] * matrix[3][1] + 124 matrix[0][3] * matrix[1][0] * matrix[2][2] * matrix[3][1] - matrix[0][0] * matrix[1][3] * matrix[2][2] * matrix[3][1] - 125 matrix[0][2] * matrix[1][0] * matrix[2][3] * matrix[3][1] + matrix[0][0] * matrix[1][2] * matrix[2][3] * matrix[3][1] + 126 matrix[0][3] * matrix[1][1] * matrix[2][0] * matrix[3][2] - matrix[0][1] * matrix[1][3] * matrix[2][0] * matrix[3][2] - 127 matrix[0][3] * matrix[1][0] * matrix[2][1] * matrix[3][2] + matrix[0][0] * matrix[1][3] * matrix[2][1] * matrix[3][2] + 128 matrix[0][1] * matrix[1][0] * matrix[2][3] * matrix[3][2] - matrix[0][0] * matrix[1][1] * matrix[2][3] * matrix[3][2] - 129 matrix[0][2] * matrix[1][1] * matrix[2][0] * matrix[3][3] + matrix[0][1] * matrix[1][2] * matrix[2][0] * matrix[3][3] + 130 matrix[0][2] * matrix[1][0] * matrix[2][1] * matrix[3][3] - matrix[0][0] * matrix[1][2] * matrix[2][1] * matrix[3][3] - 131 matrix[0][1] * matrix[1][0] * matrix[2][2] * matrix[3][3] + matrix[0][0] * matrix[1][1] * matrix[2][2] * matrix[3][3]; 132 } 133 134 void setIdentity() { 135 matrix = [ 136 Vector4(1, 0, 0, 0), Vector4(0, 1, 0, 0), Vector4(0, 0, 1, 0), 137 Vector4(0, 0, 0, 1) 138 ]; 139 } 140 141 void setZero() { 142 elements[] = 0; 143 } 144 145 void setLightBias() { 146 m[0] = 0.5; 147 m[1] = 0.0; 148 m[2] = 0.0; 149 m[3] = 0.0; 150 m[4] = 0.0; 151 m[5] = 0.5; 152 m[6] = 0.0; 153 m[7] = 0.0; 154 m[8] = 0.0; 155 m[9] = 0.0; 156 m[10] = 0.5; 157 m[11] = 0.0; 158 m[12] = 0.5; 159 m[13] = 0.5; 160 m[14] = 0.5; 161 m[15] = 1.0; 162 } 163 164 void setDepthCorrection(bool p_flip_y = true) { 165 m[0] = 1; 166 m[1] = 0.0; 167 m[2] = 0.0; 168 m[3] = 0.0; 169 m[4] = 0.0; 170 m[5] = p_flip_y ? -1 : 1; 171 m[6] = 0.0; 172 m[7] = 0.0; 173 m[8] = 0.0; 174 m[9] = 0.0; 175 m[10] = 0.5; 176 m[11] = 0.0; 177 m[12] = 0.0; 178 m[13] = 0.0; 179 m[14] = 0.5; 180 m[15] = 1.0; 181 } 182 183 void setLightAtlasRect(in Rect2 p_rect) { 184 m[0] = p_rect.size.width; 185 m[1] = 0.0; 186 m[2] = 0.0; 187 m[3] = 0.0; 188 m[4] = 0.0; 189 m[5] = p_rect.size.height; 190 m[6] = 0.0; 191 m[7] = 0.0; 192 m[8] = 0.0; 193 m[9] = 0.0; 194 m[10] = 1.0; 195 m[11] = 0.0; 196 m[12] = p_rect.position.x; 197 m[13] = p_rect.position.y; 198 m[14] = 0.0; 199 m[15] = 1.0; 200 } 201 202 void setPerspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false) { 203 if (p_flip_fov) { 204 p_fovy_degrees = getFovY(p_fovy_degrees, 1.0 / p_aspect); 205 } 206 207 real_t sine, cotangent, deltaZ; 208 real_t radians = deg2rad(p_fovy_degrees / 2.0); 209 210 deltaZ = p_z_far - p_z_near; 211 sine = sin(radians); 212 213 if ((deltaZ == 0) || (sine == 0) || (p_aspect == 0)) { 214 return; 215 } 216 cotangent = cos(radians) / sine; 217 218 setIdentity(); 219 220 matrix[0][0] = cotangent / p_aspect; 221 matrix[1][1] = cotangent; 222 matrix[2][2] = -(p_z_far + p_z_near) / deltaZ; 223 matrix[2][3] = -1; 224 matrix[3][2] = -2 * p_z_near * p_z_far / deltaZ; 225 matrix[3][3] = 0; 226 } 227 228 void setPerspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) { 229 if (p_flip_fov) { 230 p_fovy_degrees = getFovY(p_fovy_degrees, 1.0 / p_aspect); 231 } 232 233 real_t left, right, modeltranslation, ymax, xmax, frustumshift; 234 235 ymax = p_z_near * tan(deg2rad(p_fovy_degrees / 2.0)); 236 xmax = ymax * p_aspect; 237 frustumshift = (p_intraocular_dist / 2.0) * p_z_near / p_convergence_dist; 238 239 switch (p_eye) { 240 case 1: { // left eye 241 left = -xmax + frustumshift; 242 right = xmax + frustumshift; 243 modeltranslation = p_intraocular_dist / 2.0; 244 } 245 break; 246 case 2: { // right eye 247 left = -xmax - frustumshift; 248 right = xmax - frustumshift; 249 modeltranslation = -p_intraocular_dist / 2.0; 250 } 251 break; 252 default: { // mono, should give the same result as set_perspective(p_fovy_degrees,p_aspect,p_z_near,p_z_far,p_flip_fov) 253 left = -xmax; 254 right = xmax; 255 modeltranslation = 0.0; 256 } 257 break; 258 } 259 260 setFrustum(left, right, -ymax, ymax, p_z_near, p_z_far); 261 262 // translate matrix by (modeltranslation, 0.0, 0.0) 263 Projection cm; 264 cm.setIdentity(); 265 cm.matrix[3][0] = modeltranslation; 266 this = (this * cm); 267 } 268 269 void setForHMD(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) { 270 // we first calculate our base frustum on our values without taking our lens magnification into account. 271 real_t f1 = (p_intraocular_dist * 0.5) / p_display_to_lens; 272 real_t f2 = ((p_display_width - p_intraocular_dist) * 0.5) / p_display_to_lens; 273 real_t f3 = (p_display_width / 4.0) / p_display_to_lens; 274 275 // now we apply our oversample factor to increase our FOV. how much we oversample is always a balance we strike between performance and how much 276 // we're willing to sacrifice in FOV. 277 real_t add = ((f1 + f2) * (p_oversample - 1.0)) / 2.0; 278 f1 += add; 279 f2 += add; 280 f3 *= p_oversample; 281 282 // always apply KEEP_WIDTH aspect ratio 283 f3 /= p_aspect; 284 285 switch (p_eye) { 286 case 1: { // left eye 287 setFrustum(-f2 * p_z_near, f1 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); 288 } 289 break; 290 case 2: { // right eye 291 setFrustum(-f1 * p_z_near, f2 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); 292 } 293 break; 294 default: { // mono, does not apply here! 295 } 296 break; 297 } 298 } 299 300 void setOrthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) { 301 setIdentity(); 302 303 matrix[0][0] = 2.0 / (p_right - p_left); 304 matrix[3][0] = -((p_right + p_left) / (p_right - p_left)); 305 matrix[1][1] = 2.0 / (p_top - p_bottom); 306 matrix[3][1] = -((p_top + p_bottom) / (p_top - p_bottom)); 307 matrix[2][2] = -2.0 / (p_zfar - p_znear); 308 matrix[3][2] = -((p_zfar + p_znear) / (p_zfar - p_znear)); 309 matrix[3][3] = 1.0; 310 } 311 312 void setOrthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false) { 313 if (!p_flip_fov) { 314 p_size *= p_aspect; 315 } 316 317 setOrthogonal(-p_size / 2, +p_size / 2, -p_size / p_aspect / 2, +p_size / p_aspect / 2, p_znear, p_zfar); 318 } 319 320 void setFrustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) { 321 assert(p_right <= p_left); 322 assert(p_top <= p_bottom); 323 assert(p_far <= p_near); 324 325 real_t x = 2 * p_near / (p_right - p_left); 326 real_t y = 2 * p_near / (p_top - p_bottom); 327 328 real_t a = (p_right + p_left) / (p_right - p_left); 329 real_t b = (p_top + p_bottom) / (p_top - p_bottom); 330 real_t c = -(p_far + p_near) / (p_far - p_near); 331 real_t d = -2 * p_far * p_near / (p_far - p_near); 332 333 m[0] = x; 334 m[1] = 0; 335 m[2] = 0; 336 m[3] = 0; 337 m[4] = 0; 338 m[5] = y; 339 m[6] = 0; 340 m[7] = 0; 341 m[8] = a; 342 m[9] = b; 343 m[10] = c; 344 m[11] = -1; 345 m[12] = 0; 346 m[13] = 0; 347 m[14] = d; 348 m[15] = 0; 349 } 350 351 void setFrustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false) { 352 if (!p_flip_fov) { 353 p_size *= p_aspect; 354 } 355 356 setFrustum(-p_size / 2 + p_offset.x, +p_size / 2 + p_offset.x, -p_size / p_aspect / 2 + p_offset.y, +p_size / p_aspect / 2 + p_offset 357 .y, p_near, p_far); 358 } 359 360 void adjustPerspectiveZNear(real_t p_new_znear) { 361 real_t zfar = getZFar(); 362 real_t znear = p_new_znear; 363 364 real_t deltaZ = zfar - znear; 365 matrix[2][2] = -(zfar + znear) / deltaZ; 366 matrix[3][2] = -2 * znear * zfar / deltaZ; 367 } 368 369 static Projection createDepthCorrection(bool p_flip_y) { 370 Projection proj; 371 proj.setDepthCorrection(p_flip_y); 372 return proj; 373 } 374 375 static Projection createLightAtlasRect(in Rect2 p_rect) { 376 Projection proj; 377 proj.setLightAtlasRect(p_rect); 378 return proj; 379 } 380 381 static Projection createPerspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false) { 382 Projection proj; 383 proj.setPerspective(p_fovy_degrees, p_aspect, p_z_near, p_z_far, p_flip_fov); 384 return proj; 385 } 386 387 static Projection createPerspectiveHMD(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) { 388 Projection proj; 389 proj.setPerspective(p_fovy_degrees, p_aspect, p_z_near, p_z_far, p_flip_fov, p_eye, p_intraocular_dist, p_convergence_dist); 390 return proj; 391 } 392 393 static Projection createForHMD(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) { 394 Projection proj; 395 proj.setForHMD(p_eye, p_aspect, p_intraocular_dist, p_display_width, p_display_to_lens, p_oversample, p_z_near, p_z_far); 396 return proj; 397 } 398 399 static Projection createOrthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) { 400 Projection proj; 401 proj.setOrthogonal(p_left, p_right, p_bottom, p_top, p_zfar, p_zfar); 402 return proj; 403 } 404 405 static Projection createOrthogonalAspect(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false) { 406 Projection proj; 407 proj.setOrthogonal(p_size, p_aspect, p_znear, p_zfar, p_flip_fov); 408 return proj; 409 } 410 411 static Projection createFrustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) { 412 Projection proj; 413 proj.setFrustum(p_left, p_right, p_bottom, p_top, p_near, p_far); 414 return proj; 415 } 416 417 static Projection createFrustumAspect(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false) { 418 Projection proj; 419 proj.setFrustum(p_size, p_aspect, p_offset, p_near, p_far, p_flip_fov); 420 return proj; 421 } 422 423 static Projection createFitAABB(in AABB p_aabb) { 424 Projection proj; 425 proj.scaleTranslateToFit(p_aabb); 426 return proj; 427 } 428 429 Projection perspective_znear_adjusted(real_t p_new_znear) const { 430 Projection proj = this; 431 proj.adjustPerspectiveZNear(p_new_znear); 432 return proj; 433 } 434 435 Plane getProjectionPlane(Planes p_plane) const { 436 const real_t* matrix = m.ptr; 437 438 switch (p_plane) { 439 case Planes.near: { 440 Plane new_plane = Plane(matrix[3] + matrix[2], 441 matrix[7] + matrix[6], 442 matrix[11] + matrix[10], 443 matrix[15] + matrix[14]); 444 445 new_plane.normal = -new_plane.normal; 446 new_plane.normalize(); 447 return new_plane; 448 } 449 case Planes.far: { 450 Plane new_plane = Plane(matrix[3] - matrix[2], 451 matrix[7] - matrix[6], 452 matrix[11] - matrix[10], 453 matrix[15] - matrix[14]); 454 455 new_plane.normal = -new_plane.normal; 456 new_plane.normalize(); 457 return new_plane; 458 } 459 case Planes.left: { 460 Plane new_plane = Plane(matrix[3] + matrix[0], 461 matrix[7] + matrix[4], 462 matrix[11] + matrix[8], 463 matrix[15] + matrix[12]); 464 465 new_plane.normal = -new_plane.normal; 466 new_plane.normalize(); 467 return new_plane; 468 } 469 case Planes.top: { 470 Plane new_plane = Plane(matrix[3] - matrix[1], 471 matrix[7] - matrix[5], 472 matrix[11] - matrix[9], 473 matrix[15] - matrix[13]); 474 475 new_plane.normal = -new_plane.normal; 476 new_plane.normalize(); 477 return new_plane; 478 } 479 case Planes.right: { 480 Plane new_plane = Plane(matrix[3] - matrix[0], 481 matrix[7] - matrix[4], 482 matrix[11] - matrix[8], 483 matrix[15] - matrix[12]); 484 485 new_plane.normal = -new_plane.normal; 486 new_plane.normalize(); 487 return new_plane; 488 } 489 case Planes.bottom: { 490 Plane new_plane = Plane(matrix[3] + matrix[1], 491 matrix[7] + matrix[5], 492 matrix[11] + matrix[9], 493 matrix[15] + matrix[13]); 494 495 new_plane.normal = -new_plane.normal; 496 new_plane.normalize(); 497 return new_plane; 498 } 499 default: 500 break; 501 } 502 503 return Plane(); 504 } 505 506 Projection flippedY() const { 507 Projection proj = this; 508 proj.flipY(); 509 return proj; 510 } 511 512 Projection jitter_offseted(in Vector2 p_offset) const { 513 Projection proj = this; 514 proj.addJitterOffset(p_offset); 515 return proj; 516 } 517 518 static real_t getFovY(real_t p_fovx, real_t p_aspect) { 519 return rad2deg(atan(p_aspect * tan(deg2rad(p_fovx) * 0.5)) * 2.0); 520 } 521 522 real_t getZFar() const { 523 const real_t* matrix = m.ptr; 524 Plane new_plane = Plane(matrix[3] - matrix[2], 525 matrix[7] - matrix[6], 526 matrix[11] - matrix[10], 527 matrix[15] - matrix[14]); 528 529 new_plane.normal = -new_plane.normal; 530 new_plane.normalize(); 531 532 return new_plane.d; 533 } 534 535 real_t getZNear() const { 536 const real_t* matrix = m.ptr; 537 Plane new_plane = Plane(matrix[3] + matrix[2], 538 matrix[7] + matrix[6], 539 matrix[11] + matrix[10], 540 -matrix[15] - matrix[14]); 541 542 new_plane.normalize(); 543 return new_plane.d; 544 } 545 546 real_t getAspect() const { 547 Vector2 vp_he = getViewportHalfExtents(); 548 return vp_he.x / vp_he.y; 549 } 550 551 real_t getFov() const { 552 const real_t* matrix = m.ptr; 553 554 Plane right_plane = Plane(matrix[3] - matrix[0], 555 matrix[7] - matrix[4], 556 matrix[11] - matrix[8], 557 -matrix[15] + matrix[12]); 558 right_plane.normalize(); 559 560 if ((matrix[8] == 0) && (matrix[9] == 0)) { 561 return rad2deg(acos(abs(right_plane.normal.x))) * 2.0; 562 } else { 563 // our frustum is asymmetrical need to calculate the left planes angle separately.. 564 Plane left_plane = Plane(matrix[3] + matrix[0], 565 matrix[7] + matrix[4], 566 matrix[11] + matrix[8], 567 matrix[15] + matrix[12]); 568 left_plane.normalize(); 569 570 return rad2deg(acos(abs(left_plane.normal.x))) + rad2deg( 571 acos(abs(right_plane.normal.x))); 572 } 573 } 574 575 bool isOrthogonal() const { 576 return matrix[3][3] == 1.0; 577 } 578 579 Array getProjectionPlanes(in Transform3D p_transform) const { 580 /** Fast Plane Extraction from combined modelview/projection matrices. 581 * References: 582 * https://web.archive.org/web/20011221205252/https://www.markmorley.com/opengl/frustumculling.html 583 * https://web.archive.org/web/20061020020112/https://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf 584 */ 585 586 Array planes; 587 588 const real_t* matrix = m.ptr; 589 590 Plane new_plane; 591 592 ///////--- Near Plane ---/////// 593 new_plane = Plane(matrix[3] + matrix[2], 594 matrix[7] + matrix[6], 595 matrix[11] + matrix[10], 596 matrix[15] + matrix[14]); 597 598 new_plane.normal = -new_plane.normal; 599 new_plane.normalize(); 600 601 planes.pushBack(p_transform.xform(new_plane)); 602 603 ///////--- Far Plane ---/////// 604 new_plane = Plane(matrix[3] - matrix[2], 605 matrix[7] - matrix[6], 606 matrix[11] - matrix[10], 607 matrix[15] - matrix[14]); 608 609 new_plane.normal = -new_plane.normal; 610 new_plane.normalize(); 611 612 planes.pushBack(p_transform.xform(new_plane)); 613 614 ///////--- Left Plane ---/////// 615 new_plane = Plane(matrix[3] + matrix[0], 616 matrix[7] + matrix[4], 617 matrix[11] + matrix[8], 618 matrix[15] + matrix[12]); 619 620 new_plane.normal = -new_plane.normal; 621 new_plane.normalize(); 622 623 planes.pushBack(p_transform.xform(new_plane)); 624 625 ///////--- Top Plane ---/////// 626 new_plane = Plane(matrix[3] - matrix[1], 627 matrix[7] - matrix[5], 628 matrix[11] - matrix[9], 629 matrix[15] - matrix[13]); 630 631 new_plane.normal = -new_plane.normal; 632 new_plane.normalize(); 633 634 planes.pushBack(p_transform.xform(new_plane)); 635 636 ///////--- Right Plane ---/////// 637 new_plane = Plane(matrix[3] - matrix[0], 638 matrix[7] - matrix[4], 639 matrix[11] - matrix[8], 640 matrix[15] - matrix[12]); 641 642 new_plane.normal = -new_plane.normal; 643 new_plane.normalize(); 644 645 planes.pushBack(p_transform.xform(new_plane)); 646 647 ///////--- Bottom Plane ---/////// 648 new_plane = Plane(matrix[3] + matrix[1], 649 matrix[7] + matrix[5], 650 matrix[11] + matrix[9], 651 matrix[15] + matrix[13]); 652 653 new_plane.normal = -new_plane.normal; 654 new_plane.normalize(); 655 656 planes.pushBack(p_transform.xform(new_plane)); 657 658 return planes; 659 } 660 661 bool getEndpoints(in Transform3D p_transform, Vector3* p_8points) const { 662 import std.array : staticArray; 663 664 Array planes = getProjectionPlanes(Transform3D()); 665 const Planes[3][8] intersections = [ 666 [PLANE_FAR, PLANE_LEFT, PLANE_TOP], 667 [PLANE_FAR, PLANE_LEFT, PLANE_BOTTOM], 668 [PLANE_FAR, PLANE_RIGHT, PLANE_TOP], 669 [PLANE_FAR, PLANE_RIGHT, PLANE_BOTTOM], 670 [PLANE_NEAR, PLANE_LEFT, PLANE_TOP], 671 [PLANE_NEAR, PLANE_LEFT, PLANE_BOTTOM], 672 [PLANE_NEAR, PLANE_RIGHT, PLANE_TOP], 673 [PLANE_NEAR, PLANE_RIGHT, PLANE_BOTTOM], 674 ]; 675 676 for (int i = 0; i < 8; i++) { 677 Vector3 point; 678 bool res = (planes[intersections[i][0]].as!Plane).intersect3( 679 planes[intersections[i][1]].as!Plane, 680 planes[intersections[i][2]].as!Plane, 681 &point); 682 //ERR_FAIL_COND_V(!res, false); 683 p_8points[i] = p_transform.xform(point); 684 } 685 686 return true; 687 } 688 689 Vector2 getViewportHalfExtents() const { 690 const real_t* matrix = m.ptr; 691 ///////--- Near Plane ---/////// 692 Plane near_plane = Plane(matrix[3] + matrix[2], 693 matrix[7] + matrix[6], 694 matrix[11] + matrix[10], 695 -matrix[15] - matrix[14]); 696 near_plane.normalize(); 697 698 ///////--- Right Plane ---/////// 699 Plane right_plane = Plane(matrix[3] - matrix[0], 700 matrix[7] - matrix[4], 701 matrix[11] - matrix[8], 702 -matrix[15] + matrix[12]); 703 right_plane.normalize(); 704 705 Plane top_plane = Plane(matrix[3] - matrix[1], 706 matrix[7] - matrix[5], 707 matrix[11] - matrix[9], 708 -matrix[15] + matrix[13]); 709 top_plane.normalize(); 710 711 Vector3 res; 712 near_plane.intersect3(right_plane, top_plane, &res); 713 714 return Vector2(res.x, res.y); 715 } 716 717 Vector2 getFarPlaneHalfExtents() const { 718 const real_t* matrix = m.ptr; 719 ///////--- Far Plane ---/////// 720 Plane far_plane = Plane(matrix[3] - matrix[2], 721 matrix[7] - matrix[6], 722 matrix[11] - matrix[10], 723 -matrix[15] + matrix[14]); 724 far_plane.normalize(); 725 726 ///////--- Right Plane ---/////// 727 Plane right_plane = Plane(matrix[3] - matrix[0], 728 matrix[7] - matrix[4], 729 matrix[11] - matrix[8], 730 -matrix[15] + matrix[12]); 731 right_plane.normalize(); 732 733 Plane top_plane = Plane(matrix[3] - matrix[1], 734 matrix[7] - matrix[5], 735 matrix[11] - matrix[9], 736 -matrix[15] + matrix[13]); 737 top_plane.normalize(); 738 739 Vector3 res; 740 far_plane.intersect3(right_plane, top_plane, &res); 741 742 return Vector2(res.x, res.y); 743 } 744 745 void invert() { 746 int i, j, k; 747 int[4] pvt_i, pvt_j; /* Locations of pivot matrix */ 748 real_t pvt_val; /* Value of current pivot element */ 749 real_t hold; /* Temporary storage */ 750 real_t determinant = 1.0f; 751 for (k = 0; k < 4; k++) { 752 /** Locate k'th pivot element **/ 753 pvt_val = matrix[k][k]; /** Initialize for search **/ 754 pvt_i[k] = k; 755 pvt_j[k] = k; 756 for (i = k; i < 4; i++) { 757 for (j = k; j < 4; j++) { 758 if (abs(matrix[i][j]) > abs(pvt_val)) { 759 pvt_i[k] = i; 760 pvt_j[k] = j; 761 pvt_val = matrix[i][j]; 762 } 763 } 764 } 765 766 /** Product of pivots, gives determinant when finished **/ 767 determinant *= pvt_val; 768 if (isClose(determinant, 0)) { 769 return; /** Matrix is singular (zero determinant). **/ 770 } 771 772 /** "Interchange" elements (with sign change stuff) **/ 773 i = pvt_i[k]; 774 if (i != k) { /** If elements are different **/ 775 for (j = 0; j < 4; j++) { 776 hold = -matrix[k][j]; 777 matrix[k][j] = matrix[i][j]; 778 matrix[i][j] = hold; 779 } 780 } 781 782 /** "Interchange" columns **/ 783 j = pvt_j[k]; 784 if (j != k) { /** If columns are different **/ 785 for (i = 0; i < 4; i++) { 786 hold = -matrix[i][k]; 787 matrix[i][k] = matrix[i][j]; 788 matrix[i][j] = hold; 789 } 790 } 791 792 /** Divide column by minus pivot value **/ 793 for (i = 0; i < 4; i++) { 794 if (i != k) { 795 matrix[i][k] /= (-pvt_val); 796 } 797 } 798 799 /** Reduce the matrix **/ 800 for (i = 0; i < 4; i++) { 801 hold = matrix[i][k]; 802 for (j = 0; j < 4; j++) { 803 if (i != k && j != k) { 804 matrix[i][j] += hold * matrix[k][j]; 805 } 806 } 807 } 808 809 /** Divide row by pivot **/ 810 for (j = 0; j < 4; j++) { 811 if (j != k) { 812 matrix[k][j] /= pvt_val; 813 } 814 } 815 816 /** Replace pivot by reciprocal (at last we can touch it). **/ 817 matrix[k][k] = 1.0 / pvt_val; 818 } 819 820 /* That was most of the work, one final pass of row/column interchange */ 821 /* to finish */ 822 for (k = 4 - 2; k >= 0; k--) { /* Don't need to work with 1 by 1 corner*/ 823 i = pvt_j[k]; /* Rows to swap correspond to pivot COLUMN */ 824 if (i != k) { /* If elements are different */ 825 for (j = 0; j < 4; j++) { 826 hold = matrix[k][j]; 827 matrix[k][j] = -matrix[i][j]; 828 matrix[i][j] = hold; 829 } 830 } 831 832 j = pvt_i[k]; /* Columns to swap correspond to pivot ROW */ 833 if (j != k) { /* If columns are different */ 834 for (i = 0; i < 4; i++) { 835 hold = matrix[i][k]; 836 matrix[i][k] = -matrix[i][j]; 837 matrix[i][j] = hold; 838 } 839 } 840 } 841 } 842 843 Projection inverse() const { 844 Projection cm = this; 845 cm.invert(); 846 return cm; 847 } 848 849 Projection opBinary(string op : "*")(in Projection p_matrix) const { 850 Projection new_matrix; 851 852 for (int j = 0; j < 4; j++) { 853 for (int i = 0; i < 4; i++) { 854 real_t ab = 0; 855 for (int k = 0; k < 4; k++) { 856 ab += matrix[k][i] * p_matrix.matrix[j][k]; 857 } 858 new_matrix.matrix[j][i] = ab; 859 } 860 } 861 862 return new_matrix; 863 } 864 865 Plane xform4(in Plane p_vec4) const { 866 Plane ret; 867 868 ret.normal.x = matrix[0][0] * p_vec4.normal.x + matrix[1][0] * p_vec4.normal.y + matrix[2][0] * p_vec4 869 .normal.z + matrix[3][0] * p_vec4.d; 870 ret.normal.y = matrix[0][1] * p_vec4.normal.x + matrix[1][1] * p_vec4.normal.y + matrix[2][1] * p_vec4 871 .normal.z + matrix[3][1] * p_vec4.d; 872 ret.normal.z = matrix[0][2] * p_vec4.normal.x + matrix[1][2] * p_vec4.normal.y + matrix[2][2] * p_vec4 873 .normal.z + matrix[3][2] * p_vec4.d; 874 ret.d = matrix[0][3] * p_vec4.normal.x + matrix[1][3] * p_vec4.normal.y + matrix[2][3] * p_vec4.normal.z + matrix[3][3] * p_vec4 875 .d; 876 return ret; 877 } 878 879 Vector3 xform(in Vector3 p_vec3) const { 880 Vector3 ret; 881 ret.x = matrix[0][0] * p_vec3.x + matrix[1][0] * p_vec3.y + matrix[2][0] * p_vec3.z + matrix[3][0]; 882 ret.y = matrix[0][1] * p_vec3.x + matrix[1][1] * p_vec3.y + matrix[2][1] * p_vec3.z + matrix[3][1]; 883 ret.z = matrix[0][2] * p_vec3.x + matrix[1][2] * p_vec3.y + matrix[2][2] * p_vec3.z + matrix[3][2]; 884 real_t w = matrix[0][3] * p_vec3.x + matrix[1][3] * p_vec3.y + matrix[2][3] * p_vec3.z + matrix[3][3]; 885 return ret / w; 886 } 887 888 Vector4 xform(in Vector4 p_vec4) const { 889 return Vector4( 890 matrix[0][0] * p_vec4.x + matrix[1][0] * p_vec4.y + matrix[2][0] * p_vec4.z + matrix[3][0] * p_vec4.w, 891 matrix[0][1] * p_vec4.x + matrix[1][1] * p_vec4.y + matrix[2][1] * p_vec4.z + matrix[3][1] * p_vec4.w, 892 matrix[0][2] * p_vec4.x + matrix[1][2] * p_vec4.y + matrix[2][2] * p_vec4.z + matrix[3][2] * p_vec4.w, 893 matrix[0][3] * p_vec4.x + matrix[1][3] * p_vec4.y + matrix[2][3] * p_vec4.z + matrix[3][3] * p_vec4 894 .w); 895 } 896 897 Vector4 xform_inv(in Vector4 p_vec4) const { 898 return Vector4( 899 matrix[0][0] * p_vec4.x + matrix[0][1] * p_vec4.y + matrix[0][2] * p_vec4.z + matrix[0][3] * p_vec4.w, 900 matrix[1][0] * p_vec4.x + matrix[1][1] * p_vec4.y + matrix[1][2] * p_vec4.z + matrix[1][3] * p_vec4.w, 901 matrix[2][0] * p_vec4.x + matrix[2][1] * p_vec4.y + matrix[2][2] * p_vec4.z + matrix[2][3] * p_vec4.w, 902 matrix[3][0] * p_vec4.x + matrix[3][1] * p_vec4.y + matrix[3][2] * p_vec4.z + matrix[3][3] * p_vec4 903 .w); 904 } 905 906 void scaleTranslateToFit(in AABB p_aabb) { 907 Vector3 min = p_aabb.position; 908 Vector3 max = p_aabb.position + p_aabb.size; 909 910 matrix[0][0] = 2 / (max.x - min.x); 911 matrix[1][0] = 0; 912 matrix[2][0] = 0; 913 matrix[3][0] = -(max.x + min.x) / (max.x - min.x); 914 915 matrix[0][1] = 0; 916 matrix[1][1] = 2 / (max.y - min.y); 917 matrix[2][1] = 0; 918 matrix[3][1] = -(max.y + min.y) / (max.y - min.y); 919 920 matrix[0][2] = 0; 921 matrix[1][2] = 0; 922 matrix[2][2] = 2 / (max.z - min.z); 923 matrix[3][2] = -(max.z + min.z) / (max.z - min.z); 924 925 matrix[0][3] = 0; 926 matrix[1][3] = 0; 927 matrix[2][3] = 0; 928 matrix[3][3] = 1; 929 } 930 931 void addJitterOffset(in Vector2 p_offset) { 932 matrix[3][0] += p_offset.x; 933 matrix[3][1] += p_offset.y; 934 } 935 936 void makeScale(in Vector3 p_scale) { 937 setIdentity(); 938 matrix[0][0] = p_scale.x; 939 matrix[1][1] = p_scale.y; 940 matrix[2][2] = p_scale.z; 941 } 942 943 int get_pixels_per_meter(int p_for_pixel_width) const { 944 Vector3 result = xform(Vector3(1, 0, -1)); 945 946 return cast(int)((result.x * 0.5 + 0.5) * p_for_pixel_width); 947 } 948 949 Transform3D opCast(Transform3D)() const { 950 Transform3D tr; 951 const real_t* matrix = m.ptr; 952 953 tr.basis.rows[0][0] = m[0]; 954 tr.basis.rows[1][0] = m[1]; 955 tr.basis.rows[2][0] = m[2]; 956 957 tr.basis.rows[0][1] = m[4]; 958 tr.basis.rows[1][1] = m[5]; 959 tr.basis.rows[2][1] = m[6]; 960 961 tr.basis.rows[0][2] = m[8]; 962 tr.basis.rows[1][2] = m[9]; 963 tr.basis.rows[2][2] = m[10]; 964 965 tr.origin.x = m[12]; 966 tr.origin.y = m[13]; 967 tr.origin.z = m[14]; 968 969 return tr; 970 } 971 972 void flipY() { 973 for (int i = 0; i < 4; i++) { 974 matrix[1][i] = -matrix[1][i]; 975 } 976 } 977 978 bool opEquals(const Projection p_cam) const { 979 for (uint32_t i = 0; i < 4; i++) { 980 for (uint32_t j = 0; j < 4; j++) { 981 if (matrix[i][j] != p_cam.matrix[i][j]) { 982 return false; 983 } 984 } 985 } 986 return true; 987 } 988 989 float getLODMultiplier() const { 990 if (isOrthogonal()) { 991 return getViewportHalfExtents().x; 992 } else { 993 float zn = getZNear(); 994 float width = getViewportHalfExtents().x * 2.0; 995 return 1.0 / (zn / width); 996 } 997 998 // usage is lod_size / (lod_distance * multiplier) < threshold 999 } 1000 }