Winding.cpp (16776B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 23 24 #include "stdafx.h" 25 #include <assert.h> 26 #include "qe3.h" 27 #include "winding.h" 28 29 #define BOGUS_RANGE 18000 30 31 /* 32 ============= 33 Plane_Equal 34 ============= 35 */ 36 #define NORMAL_EPSILON 0.0001 37 #define DIST_EPSILON 0.02 38 39 int Plane_Equal(plane_t *a, plane_t *b, int flip) 40 { 41 vec3_t normal; 42 float dist; 43 44 if (flip) { 45 normal[0] = - b->normal[0]; 46 normal[1] = - b->normal[1]; 47 normal[2] = - b->normal[2]; 48 dist = - b->dist; 49 } 50 else { 51 normal[0] = b->normal[0]; 52 normal[1] = b->normal[1]; 53 normal[2] = b->normal[2]; 54 dist = b->dist; 55 } 56 if ( 57 fabs(a->normal[0] - normal[0]) < NORMAL_EPSILON 58 && fabs(a->normal[1] - normal[1]) < NORMAL_EPSILON 59 && fabs(a->normal[2] - normal[2]) < NORMAL_EPSILON 60 && fabs(a->dist - dist) < DIST_EPSILON ) 61 return true; 62 return false; 63 } 64 65 /* 66 ============ 67 Plane_FromPoints 68 ============ 69 */ 70 int Plane_FromPoints(vec3_t p1, vec3_t p2, vec3_t p3, plane_t *plane) 71 { 72 vec3_t v1, v2; 73 74 VectorSubtract(p2, p1, v1); 75 VectorSubtract(p3, p1, v2); 76 //CrossProduct(v2, v1, plane->normal); 77 CrossProduct(v1, v2, plane->normal); 78 if (VectorNormalize(plane->normal) < 0.1) return false; 79 plane->dist = DotProduct(p1, plane->normal); 80 return true; 81 } 82 83 /* 84 ================= 85 Point_Equal 86 ================= 87 */ 88 int Point_Equal(vec3_t p1, vec3_t p2, float epsilon) 89 { 90 int i; 91 92 for (i = 0; i < 3; i++) 93 { 94 if (fabs(p1[i] - p2[i]) > epsilon) return false; 95 } 96 return true; 97 } 98 99 100 /* 101 ================= 102 Winding_BaseForPlane 103 ================= 104 */ 105 winding_t *Winding_BaseForPlane (plane_t *p) 106 { 107 int i, x; 108 vec_t max, v; 109 vec3_t org, vright, vup; 110 winding_t *w; 111 112 // find the major axis 113 114 max = -BOGUS_RANGE; 115 x = -1; 116 for (i=0 ; i<3; i++) 117 { 118 v = fabs(p->normal[i]); 119 if (v > max) 120 { 121 x = i; 122 max = v; 123 } 124 } 125 if (x==-1) 126 Error ("Winding_BaseForPlane: no axis found"); 127 128 VectorCopy (vec3_origin, vup); 129 switch (x) 130 { 131 case 0: 132 case 1: 133 vup[2] = 1; 134 break; 135 case 2: 136 vup[0] = 1; 137 break; 138 } 139 140 141 v = DotProduct (vup, p->normal); 142 VectorMA (vup, -v, p->normal, vup); 143 VectorNormalize (vup); 144 145 VectorScale (p->normal, p->dist, org); 146 147 CrossProduct (vup, p->normal, vright); 148 149 VectorScale (vup, BOGUS_RANGE, vup); 150 VectorScale (vright, BOGUS_RANGE, vright); 151 152 // project a really big axis aligned box onto the plane 153 w = Winding_Alloc (4); 154 155 VectorSubtract (org, vright, w->points[0]); 156 VectorAdd (w->points[0], vup, w->points[0]); 157 158 VectorAdd (org, vright, w->points[1]); 159 VectorAdd (w->points[1], vup, w->points[1]); 160 161 VectorAdd (org, vright, w->points[2]); 162 VectorSubtract (w->points[2], vup, w->points[2]); 163 164 VectorSubtract (org, vright, w->points[3]); 165 VectorSubtract (w->points[3], vup, w->points[3]); 166 167 w->numpoints = 4; 168 169 return w; 170 } 171 172 /* 173 ================== 174 Winding_Alloc 175 ================== 176 */ 177 winding_t *Winding_Alloc (int points) 178 { 179 winding_t *w; 180 int size; 181 182 if (points > MAX_POINTS_ON_WINDING) 183 Error ("Winding_Alloc: %i points", points); 184 185 size = (int)((winding_t *)0)->points[points]; 186 w = (winding_t*) malloc (size); 187 memset (w, 0, size); 188 w->maxpoints = points; 189 190 return w; 191 } 192 193 194 void Winding_Free (winding_t *w) 195 { 196 free(w); 197 } 198 199 200 /* 201 ================== 202 Winding_Clone 203 ================== 204 */ 205 winding_t *Winding_Clone(winding_t *w) 206 { 207 int size; 208 winding_t *c; 209 210 size = (int)((winding_t *)0)->points[w->numpoints]; 211 c = (winding_t*)qmalloc (size); 212 memcpy (c, w, size); 213 return c; 214 } 215 216 /* 217 ================== 218 ReverseWinding 219 ================== 220 */ 221 winding_t *Winding_Reverse(winding_t *w) 222 { 223 int i; 224 winding_t *c; 225 226 c = Winding_Alloc(w->numpoints); 227 for (i = 0; i < w->numpoints; i++) 228 { 229 VectorCopy (w->points[w->numpoints-1-i], c->points[i]); 230 } 231 c->numpoints = w->numpoints; 232 return c; 233 } 234 235 236 /* 237 ============== 238 Winding_RemovePoint 239 ============== 240 */ 241 void Winding_RemovePoint(winding_t *w, int point) 242 { 243 if (point < 0 || point >= w->numpoints) 244 Error("Winding_RemovePoint: point out of range"); 245 246 if (point < w->numpoints-1) 247 { 248 memmove(&w->points[point], &w->points[point+1], (int)((winding_t *)0)->points[w->numpoints - point - 1]); 249 } 250 w->numpoints--; 251 } 252 253 /* 254 ============= 255 Winding_InsertPoint 256 ============= 257 */ 258 winding_t *Winding_InsertPoint(winding_t *w, vec3_t point, int spot) 259 { 260 int i, j; 261 winding_t *neww; 262 263 if (spot > w->numpoints) 264 { 265 Error("Winding_InsertPoint: spot > w->numpoints"); 266 } //end if 267 if (spot < 0) 268 { 269 Error("Winding_InsertPoint: spot < 0"); 270 } //end if 271 neww = Winding_Alloc(w->numpoints + 1); 272 neww->numpoints = w->numpoints + 1; 273 for (i = 0, j = 0; i < neww->numpoints; i++) 274 { 275 if (i == spot) 276 { 277 VectorCopy(point, neww->points[i]); 278 } 279 else 280 { 281 VectorCopy(w->points[j], neww->points[i]); 282 j++; 283 } 284 } 285 return neww; 286 } 287 288 /* 289 ============== 290 Winding_IsTiny 291 ============== 292 */ 293 #define EDGE_LENGTH 0.2 294 295 int Winding_IsTiny (winding_t *w) 296 { 297 int i, j; 298 vec_t len; 299 vec3_t delta; 300 int edges; 301 302 edges = 0; 303 for (i=0 ; i<w->numpoints ; i++) 304 { 305 j = i == w->numpoints - 1 ? 0 : i+1; 306 VectorSubtract (w->points[j], w->points[i], delta); 307 len = VectorLength (delta); 308 if (len > EDGE_LENGTH) 309 { 310 if (++edges == 3) 311 return false; 312 } 313 } 314 return true; 315 } 316 317 /* 318 ============== 319 Winding_IsHuge 320 ============== 321 */ 322 int Winding_IsHuge(winding_t *w) 323 { 324 int i, j; 325 326 for (i=0 ; i<w->numpoints ; i++) 327 { 328 for (j=0 ; j<3 ; j++) 329 if (w->points[i][j] < -BOGUS_RANGE+1 || w->points[i][j] > BOGUS_RANGE-1) 330 return true; 331 } 332 return false; 333 } 334 335 /* 336 ============= 337 Winding_PlanesConcave 338 ============= 339 */ 340 #define WCONVEX_EPSILON 0.2 341 342 int Winding_PlanesConcave(winding_t *w1, winding_t *w2, 343 vec3_t normal1, vec3_t normal2, 344 float dist1, float dist2) 345 { 346 int i; 347 348 if (!w1 || !w2) return false; 349 350 // check if one of the points of winding 1 is at the back of the plane of winding 2 351 for (i = 0; i < w1->numpoints; i++) 352 { 353 if (DotProduct(normal2, w1->points[i]) - dist2 > WCONVEX_EPSILON) return true; 354 } 355 // check if one of the points of winding 2 is at the back of the plane of winding 1 356 for (i = 0; i < w2->numpoints; i++) 357 { 358 if (DotProduct(normal1, w2->points[i]) - dist1 > WCONVEX_EPSILON) return true; 359 } 360 361 return false; 362 } 363 364 /* 365 ================== 366 Winding_Clip 367 368 Clips the winding to the plane, returning the new winding on the positive side 369 Frees the input winding. 370 If keepon is true, an exactly on-plane winding will be saved, otherwise 371 it will be clipped away. 372 ================== 373 */ 374 winding_t *Winding_Clip (winding_t *in, plane_t *split, qboolean keepon) 375 { 376 vec_t dists[MAX_POINTS_ON_WINDING]; 377 int sides[MAX_POINTS_ON_WINDING]; 378 int counts[3]; 379 vec_t dot; 380 int i, j; 381 vec_t *p1, *p2; 382 vec3_t mid; 383 winding_t *neww; 384 int maxpts; 385 386 counts[0] = counts[1] = counts[2] = 0; 387 388 // determine sides for each point 389 for (i=0 ; i<in->numpoints ; i++) 390 { 391 dot = DotProduct (in->points[i], split->normal); 392 dot -= split->dist; 393 dists[i] = dot; 394 if (dot > ON_EPSILON) 395 sides[i] = SIDE_FRONT; 396 else if (dot < -ON_EPSILON) 397 sides[i] = SIDE_BACK; 398 else 399 { 400 sides[i] = SIDE_ON; 401 } 402 counts[sides[i]]++; 403 } 404 sides[i] = sides[0]; 405 dists[i] = dists[0]; 406 407 if (keepon && !counts[0] && !counts[1]) 408 return in; 409 410 if (!counts[0]) 411 { 412 Winding_Free (in); 413 return NULL; 414 } 415 if (!counts[1]) 416 return in; 417 418 maxpts = in->numpoints+4; // can't use counts[0]+2 because 419 // of fp grouping errors 420 neww = Winding_Alloc (maxpts); 421 422 for (i=0 ; i<in->numpoints ; i++) 423 { 424 p1 = in->points[i]; 425 426 if (sides[i] == SIDE_ON) 427 { 428 VectorCopy (p1, neww->points[neww->numpoints]); 429 neww->numpoints++; 430 continue; 431 } 432 433 if (sides[i] == SIDE_FRONT) 434 { 435 VectorCopy (p1, neww->points[neww->numpoints]); 436 neww->numpoints++; 437 } 438 439 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) 440 continue; 441 442 // generate a split point 443 p2 = in->points[(i+1)%in->numpoints]; 444 445 dot = dists[i] / (dists[i]-dists[i+1]); 446 for (j=0 ; j<3 ; j++) 447 { // avoid round off error when possible 448 if (split->normal[j] == 1) 449 mid[j] = split->dist; 450 else if (split->normal[j] == -1) 451 mid[j] = -split->dist; 452 else 453 mid[j] = p1[j] + dot*(p2[j]-p1[j]); 454 } 455 456 VectorCopy (mid, neww->points[neww->numpoints]); 457 neww->numpoints++; 458 } 459 460 if (neww->numpoints > maxpts) 461 Error ("Winding_Clip: points exceeded estimate"); 462 463 // free the original winding 464 Winding_Free (in); 465 466 return neww; 467 } 468 469 /* 470 ============= 471 Winding_SplitEpsilon 472 473 split the input winding with the plane 474 the input winding stays untouched 475 ============= 476 */ 477 void Winding_SplitEpsilon (winding_t *in, vec3_t normal, double dist, 478 vec_t epsilon, winding_t **front, winding_t **back) 479 { 480 vec_t dists[MAX_POINTS_ON_WINDING+4]; 481 int sides[MAX_POINTS_ON_WINDING+4]; 482 int counts[3]; 483 vec_t dot; 484 int i, j; 485 vec_t *p1, *p2; 486 vec3_t mid; 487 winding_t *f, *b; 488 int maxpts; 489 490 counts[0] = counts[1] = counts[2] = 0; 491 492 // determine sides for each point 493 for (i = 0; i < in->numpoints; i++) 494 { 495 dot = DotProduct (in->points[i], normal); 496 dot -= dist; 497 dists[i] = dot; 498 if (dot > epsilon) 499 sides[i] = SIDE_FRONT; 500 else if (dot < -epsilon) 501 sides[i] = SIDE_BACK; 502 else 503 { 504 sides[i] = SIDE_ON; 505 } 506 counts[sides[i]]++; 507 } 508 sides[i] = sides[0]; 509 dists[i] = dists[0]; 510 511 *front = *back = NULL; 512 513 if (!counts[0]) 514 { 515 *back = Winding_Clone(in); 516 return; 517 } 518 if (!counts[1]) 519 { 520 *front = Winding_Clone(in); 521 return; 522 } 523 524 maxpts = in->numpoints+4; // cant use counts[0]+2 because 525 // of fp grouping errors 526 527 *front = f = Winding_Alloc (maxpts); 528 *back = b = Winding_Alloc (maxpts); 529 530 for (i = 0; i < in->numpoints; i++) 531 { 532 p1 = in->points[i]; 533 534 if (sides[i] == SIDE_ON) 535 { 536 VectorCopy (p1, f->points[f->numpoints]); 537 f->numpoints++; 538 VectorCopy (p1, b->points[b->numpoints]); 539 b->numpoints++; 540 continue; 541 } 542 543 if (sides[i] == SIDE_FRONT) 544 { 545 VectorCopy (p1, f->points[f->numpoints]); 546 f->numpoints++; 547 } 548 if (sides[i] == SIDE_BACK) 549 { 550 VectorCopy (p1, b->points[b->numpoints]); 551 b->numpoints++; 552 } 553 554 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) 555 continue; 556 557 // generate a split point 558 p2 = in->points[(i+1)%in->numpoints]; 559 560 dot = dists[i] / (dists[i]-dists[i+1]); 561 for (j = 0; j < 3; j++) 562 { 563 // avoid round off error when possible 564 if (normal[j] == 1) 565 mid[j] = dist; 566 else if (normal[j] == -1) 567 mid[j] = -dist; 568 else 569 mid[j] = p1[j] + dot*(p2[j]-p1[j]); 570 } 571 572 VectorCopy (mid, f->points[f->numpoints]); 573 f->numpoints++; 574 VectorCopy (mid, b->points[b->numpoints]); 575 b->numpoints++; 576 } 577 578 if (f->numpoints > maxpts || b->numpoints > maxpts) 579 Error ("Winding_Clip: points exceeded estimate"); 580 if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING) 581 Error ("Winding_Clip: MAX_POINTS_ON_WINDING"); 582 } 583 584 /* 585 ============= 586 Winding_TryMerge 587 588 If two windings share a common edge and the edges that meet at the 589 common points are both inside the other polygons, merge them 590 591 Returns NULL if the windings couldn't be merged, or the new winding. 592 The originals will NOT be freed. 593 594 if keep is true no points are ever removed 595 ============= 596 */ 597 #define CONTINUOUS_EPSILON 0.005 598 599 winding_t *Winding_TryMerge(winding_t *f1, winding_t *f2, vec3_t planenormal, int keep) 600 { 601 vec_t *p1, *p2, *p3, *p4, *back; 602 winding_t *newf; 603 int i, j, k, l; 604 vec3_t normal, delta; 605 vec_t dot; 606 qboolean keep1, keep2; 607 608 609 // 610 // find a common edge 611 // 612 p1 = p2 = NULL; // stop compiler warning 613 j = 0; // 614 615 for (i = 0; i < f1->numpoints; i++) 616 { 617 p1 = f1->points[i]; 618 p2 = f1->points[(i+1) % f1->numpoints]; 619 for (j = 0; j < f2->numpoints; j++) 620 { 621 p3 = f2->points[j]; 622 p4 = f2->points[(j+1) % f2->numpoints]; 623 for (k = 0; k < 3; k++) 624 { 625 if (fabs(p1[k] - p4[k]) > 0.1)//EQUAL_EPSILON) //ME 626 break; 627 if (fabs(p2[k] - p3[k]) > 0.1)//EQUAL_EPSILON) //ME 628 break; 629 } //end for 630 if (k==3) 631 break; 632 } //end for 633 if (j < f2->numpoints) 634 break; 635 } //end for 636 637 if (i == f1->numpoints) 638 return NULL; // no matching edges 639 640 // 641 // check slope of connected lines 642 // if the slopes are colinear, the point can be removed 643 // 644 back = f1->points[(i+f1->numpoints-1)%f1->numpoints]; 645 VectorSubtract (p1, back, delta); 646 CrossProduct (planenormal, delta, normal); 647 VectorNormalize (normal); 648 649 back = f2->points[(j+2)%f2->numpoints]; 650 VectorSubtract (back, p1, delta); 651 dot = DotProduct (delta, normal); 652 if (dot > CONTINUOUS_EPSILON) 653 return NULL; // not a convex polygon 654 keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON); 655 656 back = f1->points[(i+2)%f1->numpoints]; 657 VectorSubtract (back, p2, delta); 658 CrossProduct (planenormal, delta, normal); 659 VectorNormalize (normal); 660 661 back = f2->points[(j+f2->numpoints-1)%f2->numpoints]; 662 VectorSubtract (back, p2, delta); 663 dot = DotProduct (delta, normal); 664 if (dot > CONTINUOUS_EPSILON) 665 return NULL; // not a convex polygon 666 keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON); 667 668 // 669 // build the new polygon 670 // 671 newf = Winding_Alloc (f1->numpoints + f2->numpoints); 672 673 // copy first polygon 674 for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints) 675 { 676 if (!keep && k==(i+1)%f1->numpoints && !keep2) 677 continue; 678 679 VectorCopy (f1->points[k], newf->points[newf->numpoints]); 680 newf->numpoints++; 681 } 682 683 // copy second polygon 684 for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints) 685 { 686 if (!keep && l==(j+1)%f2->numpoints && !keep1) 687 continue; 688 VectorCopy (f2->points[l], newf->points[newf->numpoints]); 689 newf->numpoints++; 690 } 691 692 return newf; 693 } 694 695 /* 696 ============ 697 Winding_Plane 698 ============ 699 */ 700 void Winding_Plane (winding_t *w, vec3_t normal, double *dist) 701 { 702 vec3_t v1, v2; 703 int i; 704 705 //find two vectors each longer than 0.5 units 706 for (i = 0; i < w->numpoints; i++) 707 { 708 VectorSubtract(w->points[(i+1) % w->numpoints], w->points[i], v1); 709 VectorSubtract(w->points[(i+2) % w->numpoints], w->points[i], v2); 710 if (VectorLength(v1) > 0.5 && VectorLength(v2) > 0.5) break; 711 } 712 CrossProduct(v2, v1, normal); 713 VectorNormalize(normal); 714 *dist = DotProduct(w->points[0], normal); 715 } 716 717 /* 718 ============= 719 Winding_Area 720 ============= 721 */ 722 float Winding_Area (winding_t *w) 723 { 724 int i; 725 vec3_t d1, d2, cross; 726 float total; 727 728 total = 0; 729 for (i=2 ; i<w->numpoints ; i++) 730 { 731 VectorSubtract (w->points[i-1], w->points[0], d1); 732 VectorSubtract (w->points[i], w->points[0], d2); 733 CrossProduct (d1, d2, cross); 734 total += 0.5 * VectorLength ( cross ); 735 } 736 return total; 737 } 738 739 /* 740 ============= 741 Winding_Bounds 742 ============= 743 */ 744 void Winding_Bounds (winding_t *w, vec3_t mins, vec3_t maxs) 745 { 746 vec_t v; 747 int i,j; 748 749 mins[0] = mins[1] = mins[2] = 99999; 750 maxs[0] = maxs[1] = maxs[2] = -99999; 751 752 for (i=0 ; i<w->numpoints ; i++) 753 { 754 for (j=0 ; j<3 ; j++) 755 { 756 v = w->points[i][j]; 757 if (v < mins[j]) 758 mins[j] = v; 759 if (v > maxs[j]) 760 maxs[j] = v; 761 } 762 } 763 } 764 765 766 /* 767 ================= 768 Winding_PointInside 769 ================= 770 */ 771 int Winding_PointInside(winding_t *w, plane_t *plane, vec3_t point, float epsilon) 772 { 773 int i; 774 vec3_t dir, normal, pointvec; 775 776 for (i = 0; i < w->numpoints; i++) 777 { 778 VectorSubtract(w->points[(i+1) % w->numpoints], w->points[i], dir); 779 VectorSubtract(point, w->points[i], pointvec); 780 // 781 CrossProduct(dir, plane->normal, normal); 782 // 783 if (DotProduct(pointvec, normal) < -epsilon) return false; 784 } 785 return true; 786 } 787 788 /* 789 ================= 790 Winding_VectorIntersect 791 ================= 792 */ 793 int Winding_VectorIntersect(winding_t *w, plane_t *plane, vec3_t p1, vec3_t p2, float epsilon) 794 { 795 float front, back, frac; 796 vec3_t mid; 797 798 front = DotProduct(p1, plane->normal) - plane->dist; 799 back = DotProduct(p2, plane->normal) - plane->dist; 800 //if both points at the same side of the plane 801 if (front < -epsilon && back < -epsilon) return false; 802 if (front > epsilon && back > epsilon) return false; 803 //get point of intersection with winding plane 804 if (fabs(front-back) < 0.001) 805 { 806 VectorCopy(p2, mid); 807 } 808 else 809 { 810 frac = front/(front-back); 811 mid[0] = p1[0] + (p2[0] - p1[0]) * frac; 812 mid[1] = p1[1] + (p2[1] - p1[1]) * frac; 813 mid[2] = p1[2] + (p2[2] - p1[2]) * frac; 814 } 815 return Winding_PointInside(w, plane, mid, epsilon); 816 } 817