Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

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 }