1 /** 2 Axis-Aligned Bounding Box. 3 4 Copyright: 5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017-2018 Godot-D contributors 8 9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 10 11 12 */ 13 module godot.core.aabb; 14 15 import godot.core.defs; 16 import godot.core.vector3; 17 import godot.core.plane; 18 19 import std.algorithm.mutation : swap; 20 21 /** 22 AABB consists of a position, a size, and several utility functions. It is typically used for fast overlap tests. 23 */ 24 struct AABB 25 { 26 @nogc nothrow: 27 28 Vector3 pos; 29 Vector3 size; 30 31 bool hasNoArea() const 32 { 33 return (size.x<=CMP_EPSILON || size.y<=CMP_EPSILON || size.z<=CMP_EPSILON); 34 } 35 36 bool hasNoSurface() const 37 { 38 return (size.x<=CMP_EPSILON && size.y<=CMP_EPSILON && size.z<=CMP_EPSILON); 39 } 40 41 bool intersects(in AABB p_aabb) const 42 { 43 if ( pos.x >= (p_aabb.pos.x + p_aabb.size.x) ) 44 return false; 45 if ( (pos.x+size.x) <= p_aabb.pos.x ) 46 return false; 47 if ( pos.y >= (p_aabb.pos.y + p_aabb.size.y) ) 48 return false; 49 if ( (pos.y+size.y) <= p_aabb.pos.y ) 50 return false; 51 if ( pos.z >= (p_aabb.pos.z + p_aabb.size.z) ) 52 return false; 53 if ( (pos.z+size.z) <= p_aabb.pos.z ) 54 return false; 55 return true; 56 } 57 58 bool intersectsInclusive(in AABB p_aabb) const 59 { 60 if ( pos.x > (p_aabb.pos.x + p_aabb.size.x) ) 61 return false; 62 if ( (pos.x+size.x) < p_aabb.pos.x ) 63 return false; 64 if ( pos.y > (p_aabb.pos.y + p_aabb.size.y) ) 65 return false; 66 if ( (pos.y+size.y) < p_aabb.pos.y ) 67 return false; 68 if ( pos.z > (p_aabb.pos.z + p_aabb.size.z) ) 69 return false; 70 if ( (pos.z+size.z) < p_aabb.pos.z ) 71 return false; 72 return true; 73 } 74 75 bool encloses(in AABB p_aabb) const 76 { 77 Vector3 src_min=pos; 78 Vector3 src_max=pos+size; 79 Vector3 dst_min=p_aabb.pos; 80 Vector3 dst_max=p_aabb.pos+p_aabb.size; 81 82 return ( 83 (src_min.x <= dst_min.x) && 84 (src_max.x > dst_max.x) && 85 (src_min.y <= dst_min.y) && 86 (src_max.y > dst_max.y) && 87 (src_min.z <= dst_min.z) && 88 (src_max.z > dst_max.z) ); 89 90 } 91 92 Vector3 getSupport(in Vector3 p_normal) const 93 { 94 Vector3 half_extents = size * 0.5; 95 Vector3 ofs = pos + half_extents; 96 97 return Vector3( 98 (p_normal.x>0) ? -half_extents.x : half_extents.x, 99 (p_normal.y>0) ? -half_extents.y : half_extents.y, 100 (p_normal.z>0) ? -half_extents.z : half_extents.z 101 )+ofs; 102 } 103 104 105 Vector3 getEndpoint(int p_point) const 106 { 107 switch(p_point) 108 { 109 case 0: return Vector3( pos.x, pos.y, pos.z); 110 case 1: return Vector3( pos.x, pos.y, pos.z+size.z); 111 case 2: return Vector3( pos.x, pos.y+size.y, pos.z); 112 case 3: return Vector3( pos.x, pos.y+size.y, pos.z+size.z); 113 case 4: return Vector3( pos.x+size.x, pos.y, pos.z); 114 case 5: return Vector3( pos.x+size.x, pos.y, pos.z+size.z); 115 case 6: return Vector3( pos.x+size.x, pos.y+size.y, pos.z); 116 case 7: return Vector3( pos.x+size.x, pos.y+size.y, pos.z+size.z); 117 default: assert(0); ///ERR_FAIL_V(Vector3()) 118 } 119 } 120 121 bool intersectsConvexShape(in Plane[] p_planes) const 122 { 123 Vector3 half_extents = size * 0.5; 124 Vector3 ofs = pos + half_extents; 125 126 foreach(const ref p; p_planes) 127 { 128 Vector3 point = Vector3( 129 (p.normal.x>0) ? -half_extents.x : half_extents.x, 130 (p.normal.y>0) ? -half_extents.y : half_extents.y, 131 (p.normal.z>0) ? -half_extents.z : half_extents.z 132 ); 133 point+=ofs; 134 if (p.isPointOver(point)) 135 return false; 136 } 137 138 return true; 139 } 140 141 bool hasPoint(in Vector3 p_point) const 142 { 143 if (p_point.x<pos.x) 144 return false; 145 if (p_point.y<pos.y) 146 return false; 147 if (p_point.z<pos.z) 148 return false; 149 if (p_point.x>pos.x+size.x) 150 return false; 151 if (p_point.y>pos.y+size.y) 152 return false; 153 if (p_point.z>pos.z+size.z) 154 return false; 155 156 return true; 157 } 158 159 160 void expandTo(in Vector3 p_vector) 161 { 162 Vector3 begin=pos; 163 Vector3 end=pos+size; 164 165 if (p_vector.x<begin.x) 166 begin.x=p_vector.x; 167 if (p_vector.y<begin.y) 168 begin.y=p_vector.y; 169 if (p_vector.z<begin.z) 170 begin.z=p_vector.z; 171 172 if (p_vector.x>end.x) 173 end.x=p_vector.x; 174 if (p_vector.y>end.y) 175 end.y=p_vector.y; 176 if (p_vector.z>end.z) 177 end.z=p_vector.z; 178 179 pos=begin; 180 size=end-begin; 181 } 182 183 void projectRangeInPlane(in Plane p_plane, out real_t r_min, out real_t r_max) const 184 { 185 Vector3 half_extents = Vector3( size.x * 0.5, size.y * 0.5, size.z * 0.5 ); 186 Vector3 center = Vector3( pos.x + half_extents.x, pos.y + half_extents.y, pos.z + half_extents.z ); 187 188 real_t length = p_plane.normal.abs().dot(half_extents); 189 real_t distance = p_plane.distanceTo( center ); 190 r_min = distance - length; 191 r_max = distance + length; 192 } 193 194 real_t getLongestAxisSize() const 195 { 196 real_t max_size=size.x; 197 198 if (size.y > max_size ) { 199 max_size=size.y; 200 } 201 202 if (size.z > max_size ) { 203 max_size=size.z; 204 } 205 206 return max_size; 207 } 208 209 real_t getShortestAxisSize() const 210 { 211 real_t max_size=size.x; 212 213 if (size.y < max_size ) { 214 max_size=size.y; 215 } 216 217 if (size.z < max_size ) { 218 max_size=size.z; 219 } 220 221 return max_size; 222 } 223 224 bool smitsIntersectRay(in Vector3 from, in Vector3 dir, real_t t0, real_t t1) const 225 { 226 real_t divx=1.0/dir.x; 227 real_t divy=1.0/dir.y; 228 real_t divz=1.0/dir.z; 229 230 Vector3 upbound=pos+size; 231 real_t tmin, tmax, tymin, tymax, tzmin, tzmax; 232 if (dir.x >= 0) { 233 tmin = (pos.x - from.x) * divx; 234 tmax = (upbound.x - from.x) * divx; 235 } 236 else { 237 tmin = (upbound.x - from.x) * divx; 238 tmax = (pos.x - from.x) * divx; 239 } 240 if (dir.y >= 0) { 241 tymin = (pos.y - from.y) * divy; 242 tymax = (upbound.y - from.y) * divy; 243 } 244 else { 245 tymin = (upbound.y - from.y) * divy; 246 tymax = (pos.y - from.y) * divy; 247 } 248 if ( (tmin > tymax) || (tymin > tmax) ) 249 return false; 250 if (tymin > tmin) 251 tmin = tymin; 252 if (tymax < tmax) 253 tmax = tymax; 254 if (dir.z >= 0) { 255 tzmin = (pos.z - from.z) * divz; 256 tzmax = (upbound.z - from.z) * divz; 257 } 258 else { 259 tzmin = (upbound.z - from.z) * divz; 260 tzmax = (pos.z - from.z) * divz; 261 } 262 if ( (tmin > tzmax) || (tzmin > tmax) ) 263 return false; 264 if (tzmin > tmin) 265 tmin = tzmin; 266 if (tzmax < tmax) 267 tmax = tzmax; 268 return ( (tmin < t1) && (tmax > t0) ); 269 } 270 271 void growBy(real_t p_amount) 272 { 273 pos.x-=p_amount; 274 pos.y-=p_amount; 275 pos.z-=p_amount; 276 size.x+=2.0*p_amount; 277 size.y+=2.0*p_amount; 278 size.z+=2.0*p_amount; 279 } 280 281 282 real_t getArea() const 283 { 284 return size.x*size.y*size.z; 285 } 286 287 void mergeWith(in AABB p_aabb) 288 { 289 Vector3 beg_1,beg_2; 290 Vector3 end_1,end_2; 291 Vector3 min,max; 292 293 beg_1=pos; 294 beg_2=p_aabb.pos; 295 end_1=Vector3(size.x,size.y,size.z)+beg_1; 296 end_2=Vector3(p_aabb.size.x,p_aabb.size.y,p_aabb.size.z)+beg_2; 297 298 min.x=(beg_1.x<beg_2.x)?beg_1.x:beg_2.x; 299 min.y=(beg_1.y<beg_2.y)?beg_1.y:beg_2.y; 300 min.z=(beg_1.z<beg_2.z)?beg_1.z:beg_2.z; 301 302 max.x=(end_1.x>end_2.x)?end_1.x:end_2.x; 303 max.y=(end_1.y>end_2.y)?end_1.y:end_2.y; 304 max.z=(end_1.z>end_2.z)?end_1.z:end_2.z; 305 306 pos=min; 307 size=max-min; 308 } 309 310 AABB intersection(in AABB p_aabb) const 311 { 312 Vector3 src_min=pos; 313 Vector3 src_max=pos+size; 314 Vector3 dst_min=p_aabb.pos; 315 Vector3 dst_max=p_aabb.pos+p_aabb.size; 316 317 Vector3 min,max; 318 319 if (src_min.x > dst_max.x || src_max.x < dst_min.x ) 320 return AABB(); 321 else 322 { 323 min.x= ( src_min.x > dst_min.x ) ? src_min.x :dst_min.x; 324 max.x= ( src_max.x < dst_max.x ) ? src_max.x :dst_max.x; 325 } 326 327 if (src_min.y > dst_max.y || src_max.y < dst_min.y ) 328 return AABB(); 329 else 330 { 331 min.y= ( src_min.y > dst_min.y ) ? src_min.y :dst_min.y; 332 max.y= ( src_max.y < dst_max.y ) ? src_max.y :dst_max.y; 333 } 334 335 if (src_min.z > dst_max.z || src_max.z < dst_min.z ) 336 return AABB(); 337 else 338 { 339 min.z= ( src_min.z > dst_min.z ) ? src_min.z :dst_min.z; 340 max.z= ( src_max.z < dst_max.z ) ? src_max.z :dst_max.z; 341 } 342 343 344 return AABB( min, max-min ); 345 } 346 347 bool intersectsRay(in Vector3 p_from, in Vector3 p_dir, Vector3* r_clip = null, Vector3* r_normal = null) const 348 { 349 Vector3 c1, c2; 350 Vector3 end = pos+size; 351 real_t near=-1e20; 352 real_t far=1e20; 353 int axis=0; 354 355 for (int i=0;i<3;i++) 356 { 357 if (p_dir[i] == 0) 358 { 359 if ((p_from[i] < pos[i]) || (p_from[i] > end[i])) 360 { 361 return false; 362 } 363 } else { // ray not parallel to planes in this direction 364 c1[i] = (pos[i] - p_from[i]) / p_dir[i]; 365 c2[i] = (end[i] - p_from[i]) / p_dir[i]; 366 367 if(c1[i] > c2[i]){ 368 swap(c1,c2); 369 } 370 if (c1[i] > near){ 371 near = c1[i]; 372 axis=i; 373 } 374 if (c2[i] < far){ 375 far = c2[i]; 376 } 377 if( (near > far) || (far < 0) ){ 378 return false; 379 } 380 } 381 } 382 if (r_clip) 383 *r_clip=c1; 384 if (r_normal) 385 { 386 *r_normal=Vector3(); 387 (*r_normal)[axis]=p_dir[axis]?-1:1; 388 } 389 return true; 390 } 391 392 bool intersectsSegment(in Vector3 p_from, in Vector3 p_to, Vector3* r_clip = null, Vector3* r_normal = null) const 393 { 394 real_t min=0,max=1; 395 int axis=0; 396 real_t sign=0; 397 398 for(int i=0;i<3;i++) 399 { 400 real_t seg_from=p_from[i]; 401 real_t seg_to=p_to[i]; 402 real_t box_begin=pos[i]; 403 real_t box_end=box_begin+size[i]; 404 real_t cmin,cmax; 405 real_t csign; 406 407 if (seg_from < seg_to) 408 { 409 if (seg_from > box_end || seg_to < box_begin) 410 return false; 411 real_t length=seg_to-seg_from; 412 cmin = (seg_from < box_begin)?((box_begin - seg_from)/length):0; 413 cmax = (seg_to > box_end)?((box_end - seg_from)/length):1; 414 csign=-1.0; 415 } else { 416 if (seg_to > box_end || seg_from < box_begin) 417 return false; 418 real_t length=seg_to-seg_from; 419 cmin = (seg_from > box_end)?(box_end - seg_from)/length:0; 420 cmax = (seg_to < box_begin)?(box_begin - seg_from)/length:1; 421 csign=1.0; 422 } 423 424 if (cmin > min) 425 { 426 min = cmin; 427 axis=i; 428 sign=csign; 429 } 430 if (cmax < max) 431 max = cmax; 432 if (max < min) 433 return false; 434 } 435 436 Vector3 rel=p_to-p_from; 437 438 if (r_normal) 439 { 440 Vector3 normal; 441 normal[axis]=sign; 442 *r_normal=normal; 443 } 444 445 if (r_clip) 446 *r_clip=p_from+rel*min; 447 448 return true; 449 } 450 451 452 bool intersectsPlane(in Plane p_plane) const 453 { 454 Vector3[8] points = [ 455 Vector3( pos.x, pos.y, pos.z), 456 Vector3( pos.x, pos.y, pos.z+size.z), 457 Vector3( pos.x, pos.y+size.y, pos.z), 458 Vector3( pos.x, pos.y+size.y, pos.z+size.z), 459 Vector3( pos.x+size.x, pos.y, pos.z), 460 Vector3( pos.x+size.x, pos.y, pos.z+size.z), 461 Vector3( pos.x+size.x, pos.y+size.y, pos.z), 462 Vector3( pos.x+size.x, pos.y+size.y, pos.z+size.z), 463 ]; 464 465 bool over=false; 466 bool under=false; 467 468 for (int i=0;i<8;i++) 469 { 470 if (p_plane.distanceTo(points[i])>0) 471 over=true; 472 else 473 under=true; 474 } 475 return under && over; 476 } 477 478 Vector3 getLongestAxis() const 479 { 480 Vector3 axis = Vector3(1,0,0); 481 real_t max_size=size.x; 482 483 if (size.y > max_size ) 484 { 485 axis=Vector3(0,1,0); 486 max_size=size.y; 487 } 488 489 if (size.z > max_size ) 490 { 491 axis=Vector3(0,0,1); 492 max_size=size.z; 493 } 494 495 return axis; 496 } 497 int getLongestAxisIndex() const 498 { 499 int axis=0; 500 real_t max_size=size.x; 501 502 if (size.y > max_size ) 503 { 504 axis=1; 505 max_size=size.y; 506 } 507 508 if (size.z > max_size ) 509 { 510 axis=2; 511 max_size=size.z; 512 } 513 514 return axis; 515 } 516 517 Vector3 getShortestAxis() const 518 { 519 Vector3 axis = Vector3(1,0,0); 520 real_t max_size=size.x; 521 522 if (size.y < max_size ) 523 { 524 axis=Vector3(0,1,0); 525 max_size=size.y; 526 } 527 528 if (size.z < max_size ) 529 { 530 axis=Vector3(0,0,1); 531 max_size=size.z; 532 } 533 534 return axis; 535 } 536 int getShortestAxisIndex() const 537 { 538 int axis=0; 539 real_t max_size=size.x; 540 541 if (size.y < max_size ) 542 { 543 axis=1; 544 max_size=size.y; 545 } 546 547 if (size.z < max_size ) 548 { 549 axis=2; 550 max_size=size.z; 551 } 552 553 return axis; 554 } 555 556 AABB merge(in AABB p_with) const 557 { 558 AABB aabb=this; 559 aabb.mergeWith(p_with); 560 return aabb; 561 } 562 AABB expand(in Vector3 p_vector) const 563 { 564 AABB aabb=this; 565 aabb.expandTo(p_vector); 566 return aabb; 567 568 } 569 AABB grow(real_t p_by) const 570 { 571 AABB aabb=this; 572 aabb.growBy(p_by); 573 return aabb; 574 } 575 576 void getEdge(int p_edge, out Vector3 r_from, out Vector3 r_to) const 577 { 578 ///ERR_FAIL_INDEX(p_edge,12); 579 switch(p_edge) 580 { 581 case 0:{ 582 r_from=Vector3( pos.x+size.x , pos.y , pos.z ); 583 r_to=Vector3( pos.x , pos.y , pos.z ); 584 } break; 585 case 1:{ 586 r_from=Vector3( pos.x+size.x , pos.y , pos.z+size.z ); 587 r_to=Vector3( pos.x+size.x , pos.y , pos.z ); 588 } break; 589 case 2:{ 590 r_from=Vector3( pos.x , pos.y , pos.z+size.z ); 591 r_to=Vector3( pos.x+size.x , pos.y , pos.z+size.z ); 592 } break; 593 case 3:{ 594 r_from=Vector3( pos.x , pos.y , pos.z ); 595 r_to=Vector3( pos.x , pos.y , pos.z+size.z ); 596 } break; 597 case 4:{ 598 r_from=Vector3( pos.x , pos.y+size.y , pos.z ); 599 r_to=Vector3( pos.x+size.x , pos.y+size.y , pos.z ); 600 } break; 601 case 5:{ 602 r_from=Vector3( pos.x+size.x , pos.y+size.y , pos.z ); 603 r_to=Vector3( pos.x+size.x , pos.y+size.y , pos.z+size.z ); 604 } break; 605 case 6:{ 606 r_from=Vector3( pos.x+size.x , pos.y+size.y , pos.z+size.z ); 607 r_to=Vector3( pos.x , pos.y+size.y , pos.z+size.z ); 608 } break; 609 case 7:{ 610 r_from=Vector3( pos.x , pos.y+size.y , pos.z+size.z ); 611 r_to=Vector3( pos.x , pos.y+size.y , pos.z ); 612 } break; 613 case 8:{ 614 r_from=Vector3( pos.x , pos.y , pos.z+size.z ); 615 r_to=Vector3( pos.x , pos.y+size.y , pos.z+size.z ); 616 } break; 617 case 9:{ 618 r_from=Vector3( pos.x , pos.y , pos.z ); 619 r_to=Vector3( pos.x , pos.y+size.y , pos.z ); 620 } break; 621 case 10:{ 622 r_from=Vector3( pos.x+size.x , pos.y , pos.z ); 623 r_to=Vector3( pos.x+size.x , pos.y+size.y , pos.z ); 624 } break; 625 case 11:{ 626 r_from=Vector3( pos.x+size.x , pos.y , pos.z+size.z ); 627 r_to=Vector3( pos.x+size.x , pos.y+size.y , pos.z+size.z ); 628 } break; 629 default: assert(0); 630 } 631 } 632 } 633