CSG.CPP (17247B)
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 #include "stdafx.h" 24 #include "qe3.h" 25 #include "winding.h" 26 27 28 /* 29 ============= 30 CSG_MakeHollow 31 ============= 32 */ 33 34 void Brush_Scale(brush_t* b) 35 { 36 for (face_t* f = b->brush_faces ; f ; f=f->next) 37 { 38 for (int i=0 ; i<3 ; i++) 39 { 40 VectorScale (f->planepts[i], g_qeglobals.d_gridsize, f->planepts[i]); 41 } 42 } 43 } 44 45 void CSG_MakeHollow (void) 46 { 47 brush_t *b, *front, *back, *next; 48 face_t *f; 49 face_t split; 50 vec3_t move; 51 int i; 52 53 for (b = selected_brushes.next ; b != &selected_brushes ; b=next) 54 { 55 next = b->next; 56 57 if (b->owner->eclass->fixedsize || b->patchBrush || b->terrainBrush || b->hiddenBrush) 58 continue; 59 60 for (f = b->brush_faces ; f ; f=f->next) 61 { 62 split = *f; 63 VectorScale (f->plane.normal, g_qeglobals.d_gridsize, move); 64 for (i=0 ; i<3 ; i++) 65 VectorSubtract (split.planepts[i], move, split.planepts[i]); 66 67 Brush_SplitBrushByFace (b, &split, &front, &back); 68 if (back) 69 Brush_Free (back); 70 if (front) 71 Brush_AddToList (front, &selected_brushes); 72 } 73 Brush_Free (b); 74 } 75 Sys_UpdateWindows (W_ALL); 76 } 77 78 /* 79 ============= 80 Brush_Merge 81 82 Returns a new brush that is created by merging brush1 and brush2. 83 May return NULL if brush1 and brush2 do not create a convex brush when merged. 84 The input brushes brush1 and brush2 stay intact. 85 86 if onlyshape is true then the merge is allowed based on the shape only 87 otherwise the texture/shader references of faces in the same plane have to 88 be the same as well. 89 ============= 90 */ 91 brush_t *Brush_Merge(brush_t *brush1, brush_t *brush2, int onlyshape) 92 { 93 int i, shared; 94 brush_t *newbrush; 95 face_t *face1, *face2, *newface, *f; 96 97 // check for bounding box overlapp 98 for (i = 0; i < 3; i++) 99 { 100 if (brush1->mins[i] > brush2->maxs[i] + ON_EPSILON 101 || brush1->maxs[i] < brush2->mins[i] - ON_EPSILON) 102 { 103 // never merge if the brushes overlap 104 return NULL; 105 } 106 } 107 // 108 shared = 0; 109 // check if the new brush would be convex... flipped planes make a brush non-convex 110 for (face1 = brush1->brush_faces; face1; face1 = face1->next) 111 { 112 // don't check the faces of brush 1 and 2 touching each other 113 for (face2 = brush2->brush_faces; face2; face2 = face2->next) 114 { 115 if (Plane_Equal(&face1->plane, &face2->plane, true)) 116 { 117 shared++; 118 // there may only be ONE shared side 119 if (shared > 1) 120 return NULL; 121 break; 122 } 123 } 124 // if this face plane is shared 125 if (face2) continue; 126 // 127 for (face2 = brush2->brush_faces; face2; face2 = face2->next) 128 { 129 // don't check the faces of brush 1 and 2 touching each other 130 for (f = brush1->brush_faces; f; f = f->next) 131 { 132 if (Plane_Equal(&face2->plane, &f->plane, true)) break; 133 } 134 if (f) 135 continue; 136 // 137 if (Plane_Equal(&face1->plane, &face2->plane, false)) 138 { 139 //if the texture/shader references should be the same but are not 140 if (!onlyshape && stricmp(face1->texdef.name, face2->texdef.name) != 0) return NULL; 141 continue; 142 } 143 // 144 if (Winding_PlanesConcave(face1->face_winding, face2->face_winding, 145 face1->plane.normal, face2->plane.normal, 146 face1->plane.dist, face2->plane.dist)) 147 { 148 return NULL; 149 } //end if 150 } //end for 151 } //end for 152 // 153 newbrush = Brush_Alloc(); 154 // 155 for (face1 = brush1->brush_faces; face1; face1 = face1->next) 156 { 157 // don't add the faces of brush 1 and 2 touching each other 158 for (face2 = brush2->brush_faces; face2; face2 = face2->next) 159 { 160 if (Plane_Equal(&face1->plane, &face2->plane, true)) 161 break; 162 } 163 if (face2) 164 continue; 165 // don't add faces with the same plane twice 166 for (f = newbrush->brush_faces; f; f = f->next) 167 { 168 if (Plane_Equal(&face1->plane, &f->plane, false)) 169 break; 170 if (Plane_Equal(&face1->plane, &f->plane, true)) 171 break; 172 } 173 if (f) 174 continue; 175 // 176 newface = Face_Alloc(); 177 newface->texdef = face1->texdef; 178 VectorCopy(face1->planepts[0], newface->planepts[0]); 179 VectorCopy(face1->planepts[1], newface->planepts[1]); 180 VectorCopy(face1->planepts[2], newface->planepts[2]); 181 newface->plane = face1->plane; 182 newface->next = newbrush->brush_faces; 183 newbrush->brush_faces = newface; 184 } 185 // 186 for (face2 = brush2->brush_faces; face2; face2 = face2->next) 187 { 188 // don't add the faces of brush 1 and 2 touching each other 189 for (face1 = brush1->brush_faces; face1; face1 = face1->next) 190 { 191 if (Plane_Equal(&face2->plane, &face1->plane, true)) 192 break; 193 } 194 if (face1) 195 continue; 196 // don't add faces with the same plane twice 197 for (f = newbrush->brush_faces; f; f = f->next) 198 { 199 if (Plane_Equal(&face2->plane, &f->plane, false)) 200 break; 201 if (Plane_Equal(&face2->plane, &f->plane, true)) 202 break; 203 } 204 if (f) 205 continue; 206 // 207 newface = Face_Alloc(); 208 newface->texdef = face2->texdef; 209 VectorCopy(face2->planepts[0], newface->planepts[0]); 210 VectorCopy(face2->planepts[1], newface->planepts[1]); 211 VectorCopy(face2->planepts[2], newface->planepts[2]); 212 newface->plane = face2->plane; 213 newface->next = newbrush->brush_faces; 214 newbrush->brush_faces = newface; 215 } 216 // link the new brush to an entity 217 Entity_LinkBrush (brush1->owner, newbrush); 218 // build windings for the faces 219 Brush_BuildWindings( newbrush, false); 220 return newbrush; 221 } 222 223 /* 224 ============= 225 Brush_MergeListPairs 226 227 Returns a list with merged brushes. 228 Tries to merge brushes pair wise. 229 The input list is destroyed. 230 Input and output should be a single linked list using .next 231 ============= 232 */ 233 brush_t *Brush_MergeListPairs(brush_t *brushlist, int onlyshape) 234 { 235 int nummerges, merged; 236 brush_t *b1, *b2, *tail, *newbrush, *newbrushlist; 237 brush_t *lastb2; 238 239 if (!brushlist) return NULL; 240 241 nummerges = 0; 242 do 243 { 244 for (tail = brushlist; tail; tail = tail->next) 245 { 246 if (!tail->next) break; 247 } 248 merged = 0; 249 newbrushlist = NULL; 250 for (b1 = brushlist; b1; b1 = brushlist) 251 { 252 lastb2 = b1; 253 for (b2 = b1->next; b2; b2 = b2->next) 254 { 255 newbrush = Brush_Merge(b1, b2, onlyshape); 256 if (newbrush) 257 { 258 tail->next = newbrush; 259 lastb2->next = b2->next; 260 brushlist = brushlist->next; 261 b1->next = b1->prev = NULL; 262 b2->next = b2->prev = NULL; 263 Brush_Free(b1); 264 Brush_Free(b2); 265 for (tail = brushlist; tail; tail = tail->next) 266 { 267 if (!tail->next) break; 268 } //end for 269 merged++; 270 nummerges++; 271 break; 272 } 273 lastb2 = b2; 274 } 275 //if b1 can't be merged with any of the other brushes 276 if (!b2) 277 { 278 brushlist = brushlist->next; 279 //keep b1 280 b1->next = newbrushlist; 281 newbrushlist = b1; 282 } 283 } 284 brushlist = newbrushlist; 285 } while(merged); 286 return newbrushlist; 287 } 288 289 /* 290 ============= 291 Brush_MergeList 292 293 Tries to merge all brushes in the list into one new brush. 294 The input brush list stays intact. 295 Returns NULL if no merged brush can be created. 296 To create a new brush the brushes in the list may not overlap and 297 the outer faces of the brushes together should make a new convex brush. 298 299 if onlyshape is true then the merge is allowed based on the shape only 300 otherwise the texture/shader references of faces in the same plane have to 301 be the same as well. 302 ============= 303 */ 304 brush_t *Brush_MergeList(brush_t *brushlist, int onlyshape) 305 { 306 brush_t *brush1, *brush2, *brush3, *newbrush; 307 face_t *face1, *face2, *face3, *newface, *f; 308 309 if (!brushlist) return NULL; 310 for (brush1 = brushlist; brush1; brush1 = brush1->next) 311 { 312 // check if the new brush would be convex... flipped planes make a brush concave 313 for (face1 = brush1->brush_faces; face1; face1 = face1->next) 314 { 315 // don't check face1 if it touches another brush 316 for (brush2 = brushlist; brush2; brush2 = brush2->next) 317 { 318 if (brush2 == brush1) continue; 319 for (face2 = brush2->brush_faces; face2; face2 = face2->next) 320 { 321 if (Plane_Equal(&face1->plane, &face2->plane, true)) 322 { 323 break; 324 } 325 } 326 if (face2) break; 327 } 328 // if face1 touches another brush 329 if (brush2) continue; 330 // 331 for (brush2 = brush1->next; brush2; brush2 = brush2->next) 332 { 333 // don't check the faces of brush 2 touching another brush 334 for (face2 = brush2->brush_faces; face2; face2 = face2->next) 335 { 336 for (brush3 = brushlist; brush3; brush3 = brush3->next) 337 { 338 if (brush3 == brush2) continue; 339 for (face3 = brush3->brush_faces; face3; face3 = face3->next) 340 { 341 if (Plane_Equal(&face2->plane, &face3->plane, true)) break; 342 } 343 if (face3) break; 344 } 345 // if face2 touches another brush 346 if (brush3) continue; 347 // 348 if (Plane_Equal(&face1->plane, &face2->plane, false)) 349 { 350 //if the texture/shader references should be the same but are not 351 if (!onlyshape && stricmp(face1->texdef.name, face2->texdef.name) != 0) return NULL; 352 continue; 353 } 354 // 355 if (Winding_PlanesConcave(face1->face_winding, face2->face_winding, 356 face1->plane.normal, face2->plane.normal, 357 face1->plane.dist, face2->plane.dist)) 358 { 359 return NULL; 360 } 361 } 362 } 363 } 364 } 365 // 366 newbrush = Brush_Alloc(); 367 // 368 for (brush1 = brushlist; brush1; brush1 = brush1->next) 369 { 370 for (face1 = brush1->brush_faces; face1; face1 = face1->next) 371 { 372 // don't add face1 to the new brush if it touches another brush 373 for (brush2 = brushlist; brush2; brush2 = brush2->next) 374 { 375 if (brush2 == brush1) continue; 376 for (face2 = brush2->brush_faces; face2; face2 = face2->next) 377 { 378 if (Plane_Equal(&face1->plane, &face2->plane, true)) 379 { 380 break; 381 } 382 } 383 if (face2) break; 384 } 385 if (brush2) continue; 386 // don't add faces with the same plane twice 387 for (f = newbrush->brush_faces; f; f = f->next) 388 { 389 if (Plane_Equal(&face1->plane, &f->plane, false)) 390 break; 391 if (Plane_Equal(&face1->plane, &f->plane, true)) 392 break; 393 } 394 if (f) 395 continue; 396 // 397 newface = Face_Alloc(); 398 newface->texdef = face1->texdef; 399 VectorCopy(face1->planepts[0], newface->planepts[0]); 400 VectorCopy(face1->planepts[1], newface->planepts[1]); 401 VectorCopy(face1->planepts[2], newface->planepts[2]); 402 newface->plane = face1->plane; 403 newface->next = newbrush->brush_faces; 404 newbrush->brush_faces = newface; 405 } 406 } 407 // link the new brush to an entity 408 Entity_LinkBrush (brushlist->owner, newbrush); 409 // build windings for the faces 410 Brush_BuildWindings( newbrush, false); 411 return newbrush; 412 } 413 414 /* 415 ============= 416 Brush_Subtract 417 418 Returns a list of brushes that remain after B is subtracted from A. 419 May by empty if A is contained inside B. 420 The originals are undisturbed. 421 ============= 422 */ 423 brush_t *Brush_Subtract(brush_t *a, brush_t *b) 424 { 425 // a - b = out (list) 426 brush_t *front, *back; 427 brush_t *in, *out, *next; 428 face_t *f; 429 430 in = a; 431 out = NULL; 432 for (f = b->brush_faces; f && in; f = f->next) 433 { 434 Brush_SplitBrushByFace(in, f, &front, &back); 435 if (in != a) Brush_Free(in); 436 if (front) 437 { // add to list 438 front->next = out; 439 out = front; 440 } 441 in = back; 442 } 443 //NOTE: in != a just in case brush b has no faces 444 if (in && in != a) 445 { 446 Brush_Free(in); 447 } 448 else 449 { //didn't really intersect 450 for (b = out; b; b = next) 451 { 452 next = b->next; 453 b->next = b->prev = NULL; 454 Brush_Free(b); 455 } 456 return a; 457 } 458 return out; 459 } 460 461 /* 462 ============= 463 CSG_Subtract 464 ============= 465 */ 466 void CSG_Subtract (void) 467 { 468 brush_t *b, *s, *fragments, *nextfragment, *frag, *next, *snext; 469 brush_t fragmentlist; 470 int i, numfragments, numbrushes; 471 472 Sys_Printf ("Subtracting...\n"); 473 474 if (selected_brushes.next == &selected_brushes) 475 { 476 Sys_Printf("No brushes selected.\n"); 477 return; 478 } 479 480 fragmentlist.next = &fragmentlist; 481 fragmentlist.prev = &fragmentlist; 482 483 numfragments = 0; 484 numbrushes = 0; 485 for (b = selected_brushes.next ; b != &selected_brushes ; b=next) 486 { 487 next = b->next; 488 489 if (b->owner->eclass->fixedsize) 490 continue; // can't use texture from a fixed entity, so don't subtract 491 492 // chop all fragments further up 493 for (s = fragmentlist.next; s != &fragmentlist; s = snext) 494 { 495 snext = s->next; 496 497 for (i=0 ; i<3 ; i++) 498 if (b->mins[i] >= s->maxs[i] - ON_EPSILON 499 || b->maxs[i] <= s->mins[i] + ON_EPSILON) 500 break; 501 if (i != 3) 502 continue; // definately don't touch 503 fragments = Brush_Subtract(s, b); 504 // if the brushes did not really intersect 505 if (fragments == s) 506 continue; 507 // try to merge fragments 508 fragments = Brush_MergeListPairs(fragments, true); 509 // add the fragments to the list 510 for (frag = fragments; frag; frag = nextfragment) 511 { 512 nextfragment = frag->next; 513 frag->next = NULL; 514 frag->owner = s->owner; 515 Brush_AddToList(frag, &fragmentlist); 516 } 517 // free the original brush 518 Brush_Free(s); 519 } 520 521 // chop any active brushes up 522 for (s = active_brushes.next; s != &active_brushes; s = snext) 523 { 524 snext = s->next; 525 526 if (s->owner->eclass->fixedsize || s->patchBrush || s->terrainBrush || s->hiddenBrush) 527 continue; 528 529 //face_t *pFace = s->brush_faces; 530 if (s->brush_faces->d_texture->bFromShader && (s->brush_faces->d_texture->nShaderFlags & QER_NOCARVE)) 531 { 532 continue; 533 } 534 535 for (i=0 ; i<3 ; i++) 536 if (b->mins[i] >= s->maxs[i] - ON_EPSILON 537 || b->maxs[i] <= s->mins[i] + ON_EPSILON) 538 break; 539 if (i != 3) 540 continue; // definately don't touch 541 542 fragments = Brush_Subtract(s, b); 543 // if the brushes did not really intersect 544 if (fragments == s) 545 continue; 546 // 547 Undo_AddBrush(s); 548 // one extra brush chopped up 549 numbrushes++; 550 // try to merge fragments 551 fragments = Brush_MergeListPairs(fragments, true); 552 // add the fragments to the list 553 for (frag = fragments; frag; frag = nextfragment) 554 { 555 nextfragment = frag->next; 556 frag->next = NULL; 557 frag->owner = s->owner; 558 Brush_AddToList(frag, &fragmentlist); 559 } 560 // free the original brush 561 Brush_Free(s); 562 } 563 } 564 565 // move all fragments to the active brush list 566 for (frag = fragmentlist.next; frag != &fragmentlist; frag = nextfragment) 567 { 568 nextfragment = frag->next; 569 numfragments++; 570 Brush_RemoveFromList(frag); 571 Brush_AddToList(frag, &active_brushes); 572 Undo_EndBrush(frag); 573 } 574 575 if (numfragments == 0) 576 { 577 Sys_Printf("Selected brush%s did not intersect with any other brushes.\n", 578 (selected_brushes.next->next == &selected_brushes) ? "":"es"); 579 return; 580 } 581 Sys_Printf("done. (created %d fragment%s out of %d brush%s)\n", numfragments, (numfragments == 1)?"":"s", 582 numbrushes, (numbrushes == 1)?"":"es"); 583 Sys_UpdateWindows(W_ALL); 584 } 585 586 /* 587 ============= 588 CSG_Merge 589 ============= 590 */ 591 void CSG_Merge(void) 592 { 593 brush_t *b, *next, *newlist, *newbrush; 594 struct entity_s *owner; 595 596 Sys_Printf ("Merging...\n"); 597 598 if (selected_brushes.next == &selected_brushes) 599 { 600 Sys_Printf("No brushes selected.\n"); 601 return; 602 } 603 604 if (selected_brushes.next->next == &selected_brushes) 605 { 606 Sys_Printf("At least two brushes have to be selected.\n"); 607 return; 608 } 609 610 owner = selected_brushes.next->owner; 611 612 for (b = selected_brushes.next; b != &selected_brushes; b = next) 613 { 614 next = b->next; 615 616 if (b->owner->eclass->fixedsize) 617 { 618 // can't use texture from a fixed entity, so don't subtract 619 Sys_Printf("Cannot add fixed size entities.\n"); 620 return; 621 } 622 623 if (b->patchBrush) 624 { 625 Sys_Printf("Cannot add patches.\n"); 626 return; 627 } 628 if (b->terrainBrush) 629 { 630 Sys_Printf("Cannot add terrains.\n"); 631 return; 632 } 633 634 if (b->brush_faces->d_texture->bFromShader && (b->brush_faces->d_texture->nShaderFlags & QER_NOCARVE)) 635 { 636 Sys_Printf("Cannot add brushes using shaders that don't allows CSG operations.\n"); 637 return; 638 } 639 640 if (b->owner != owner) 641 { 642 Sys_Printf("Cannot add brushes from different entities.\n"); 643 return; 644 } 645 646 } 647 648 newlist = NULL; 649 for (b = selected_brushes.next; b != &selected_brushes; b = next) 650 { 651 next = b->next; 652 653 Brush_RemoveFromList(b); 654 b->next = newlist; 655 b->prev = NULL; 656 newlist = b; 657 } 658 659 newbrush = Brush_MergeList(newlist, true); 660 // if the new brush would not be convex 661 if (!newbrush) 662 { 663 // add the brushes back into the selection 664 for (b = newlist; b; b = next) 665 { 666 next = b->next; 667 b->next = NULL; 668 b->prev = NULL; 669 Brush_AddToList(b, &selected_brushes); 670 } 671 Sys_Printf("Cannot add a set of brushes with a concave hull.\n"); 672 return; 673 } 674 // free the original brushes 675 for (b = newlist; b; b = next) 676 { 677 next = b->next; 678 b->next = NULL; 679 b->prev = NULL; 680 Brush_Free(b); 681 } 682 Brush_AddToList(newbrush, &selected_brushes); 683 684 Sys_Printf ("done.\n"); 685 Sys_UpdateWindows (W_ALL); 686 }