Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

brush.c (15683B)


      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 #include "qbsp.h"
     23 
     24 
     25 int		c_active_brushes;
     26 
     27 int		c_nodes;
     28 
     29 // if a brush just barely pokes onto the other side,
     30 // let it slide by without chopping
     31 #define	PLANESIDE_EPSILON	0.001
     32 //0.1
     33 
     34 
     35 
     36 
     37 /*
     38 ================
     39 CountBrushList
     40 ================
     41 */
     42 int	CountBrushList (bspbrush_t *brushes)
     43 {
     44 	int	c;
     45 
     46 	c = 0;
     47 	for ( ; brushes ; brushes = brushes->next)
     48 		c++;
     49 	return c;
     50 }
     51 
     52 
     53 /*
     54 ================
     55 AllocBrush
     56 ================
     57 */
     58 bspbrush_t *AllocBrush (int numsides)
     59 {
     60 	bspbrush_t	*bb;
     61 	int			c;
     62 
     63 	c = (int)&(((bspbrush_t *)0)->sides[numsides]);
     64 	bb = malloc(c);
     65 	memset (bb, 0, c);
     66 	if (numthreads == 1)
     67 		c_active_brushes++;
     68 	return bb;
     69 }
     70 
     71 /*
     72 ================
     73 FreeBrush
     74 ================
     75 */
     76 void FreeBrush (bspbrush_t *brushes)
     77 {
     78 	int			i;
     79 
     80 	for (i=0 ; i<brushes->numsides ; i++)
     81 		if (brushes->sides[i].winding)
     82 			FreeWinding(brushes->sides[i].winding);
     83 	free (brushes);
     84 	if (numthreads == 1)
     85 		c_active_brushes--;
     86 }
     87 
     88 
     89 /*
     90 ================
     91 FreeBrushList
     92 ================
     93 */
     94 void FreeBrushList (bspbrush_t *brushes)
     95 {
     96 	bspbrush_t	*next;
     97 
     98 	for ( ; brushes ; brushes = next)
     99 	{
    100 		next = brushes->next;
    101 
    102 		FreeBrush (brushes);
    103 	}		
    104 }
    105 
    106 /*
    107 ==================
    108 CopyBrush
    109 
    110 Duplicates the brush, the sides, and the windings
    111 ==================
    112 */
    113 bspbrush_t *CopyBrush (bspbrush_t *brush)
    114 {
    115 	bspbrush_t *newbrush;
    116 	int			size;
    117 	int			i;
    118 	
    119 	size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);
    120 
    121 	newbrush = AllocBrush (brush->numsides);
    122 	memcpy (newbrush, brush, size);
    123 
    124 	for (i=0 ; i<brush->numsides ; i++)
    125 	{
    126 		if (brush->sides[i].winding)
    127 			newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
    128 	}
    129 
    130 	return newbrush;
    131 }
    132 
    133 
    134 /*
    135 ================
    136 DrawBrushList
    137 ================
    138 */
    139 void DrawBrushList (bspbrush_t *brush)
    140 {
    141 	int		i;
    142 	side_t	*s;
    143 
    144 	GLS_BeginScene ();
    145 	for ( ; brush ; brush=brush->next)
    146 	{
    147 		for (i=0 ; i<brush->numsides ; i++)
    148 		{
    149 			s = &brush->sides[i];
    150 			if (!s->winding)
    151 				continue;
    152 			GLS_Winding (s->winding, 0);
    153 		}
    154 	}
    155 	GLS_EndScene ();
    156 }
    157 
    158 
    159 
    160 /*
    161 ================
    162 WriteBrushList
    163 ================
    164 */
    165 void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
    166 {
    167 	int		i;
    168 	side_t	*s;
    169 	FILE	*f;
    170 
    171 	qprintf ("writing %s\n", name);
    172 	f = SafeOpenWrite (name);
    173 
    174 	for ( ; brush ; brush=brush->next)
    175 	{
    176 		for (i=0 ; i<brush->numsides ; i++)
    177 		{
    178 			s = &brush->sides[i];
    179 			if (!s->winding)
    180 				continue;
    181 			if (onlyvis && !s->visible)
    182 				continue;
    183 			OutputWinding (brush->sides[i].winding, f);
    184 		}
    185 	}
    186 
    187 	fclose (f);
    188 }
    189 
    190 
    191 /*
    192 =============
    193 PrintBrush
    194 =============
    195 */
    196 void PrintBrush (bspbrush_t *brush)
    197 {
    198 	int		i;
    199 
    200 	_printf ("brush: %p\n", brush);
    201 	for (i=0;i<brush->numsides ; i++)
    202 	{
    203 		pw(brush->sides[i].winding);
    204 		_printf ("\n");
    205 	}
    206 }
    207 
    208 /*
    209 ==================
    210 BoundBrush
    211 
    212 Sets the mins/maxs based on the windings
    213 returns false if the brush doesn't enclose a valid volume
    214 ==================
    215 */
    216 qboolean BoundBrush (bspbrush_t *brush)
    217 {
    218 	int			i, j;
    219 	winding_t	*w;
    220 
    221 	ClearBounds (brush->mins, brush->maxs);
    222 	for (i=0 ; i<brush->numsides ; i++)
    223 	{
    224 		w = brush->sides[i].winding;
    225 		if (!w)
    226 			continue;
    227 		for (j=0 ; j<w->numpoints ; j++)
    228 			AddPointToBounds (w->p[j], brush->mins, brush->maxs);
    229 	}
    230 
    231 	for (i=0 ; i<3 ; i++) {
    232 		if (brush->mins[i] < MIN_WORLD_COORD || brush->maxs[i] > MAX_WORLD_COORD
    233 			|| brush->mins[i] >= brush->maxs[i] ) {
    234 			return qfalse;
    235 		}
    236 	}
    237 
    238 	return qtrue;
    239 }
    240 
    241 /*
    242 ==================
    243 CreateBrushWindings
    244 
    245 makes basewindigs for sides and mins / maxs for the brush
    246 returns false if the brush doesn't enclose a valid volume
    247 ==================
    248 */
    249 qboolean CreateBrushWindings (bspbrush_t *brush)
    250 {
    251 	int			i, j;
    252 	winding_t	*w;
    253 	side_t		*side;
    254 	plane_t		*plane;
    255 
    256 	for ( i = 0; i < brush->numsides; i++ )
    257 	{
    258 		side = &brush->sides[i];
    259 		// don't create a winding for a bevel
    260 		if ( side->bevel ) {
    261 			continue;
    262 		}
    263 		plane = &mapplanes[side->planenum];
    264 		w = BaseWindingForPlane (plane->normal, plane->dist);
    265 		for ( j = 0; j < brush->numsides && w; j++ )
    266 		{
    267 			if (i == j)
    268 				continue;
    269 			if ( brush->sides[j].planenum == ( brush->sides[i].planenum ^ 1 ) )
    270 				continue;		// back side clipaway
    271 			if (brush->sides[j].bevel)
    272 				continue;
    273 			if (brush->sides[j].backSide)
    274 				continue;
    275 			plane = &mapplanes[brush->sides[j].planenum^1];
    276 			ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
    277 		}
    278 		// free any existing winding
    279 		if ( side->winding ) {
    280 			FreeWinding( side->winding );
    281 		}
    282 		side->winding = w;
    283 	}
    284 
    285 	return BoundBrush (brush);
    286 }
    287 
    288 /*
    289 ==================
    290 BrushFromBounds
    291 
    292 Creates a new axial brush
    293 ==================
    294 */
    295 bspbrush_t	*BrushFromBounds (vec3_t mins, vec3_t maxs)
    296 {
    297 	bspbrush_t	*b;
    298 	int			i;
    299 	vec3_t		normal;
    300 	vec_t		dist;
    301 
    302 	b = AllocBrush (6);
    303 	b->numsides = 6;
    304 	for (i=0 ; i<3 ; i++)
    305 	{
    306 		VectorClear (normal);
    307 		normal[i] = 1;
    308 		dist = maxs[i];
    309 		b->sides[i].planenum = FindFloatPlane (normal, dist);
    310 
    311 		normal[i] = -1;
    312 		dist = -mins[i];
    313 		b->sides[3+i].planenum = FindFloatPlane (normal, dist);
    314 	}
    315 
    316 	CreateBrushWindings (b);
    317 
    318 	return b;
    319 }
    320 
    321 /*
    322 ==================
    323 BrushVolume
    324 
    325 ==================
    326 */
    327 vec_t BrushVolume (bspbrush_t *brush)
    328 {
    329 	int			i;
    330 	winding_t	*w;
    331 	vec3_t		corner;
    332 	vec_t		d, area, volume;
    333 	plane_t		*plane;
    334 
    335 	if (!brush)
    336 		return 0;
    337 
    338 	// grab the first valid point as the corner
    339 
    340 	w = NULL;
    341 	for (i=0 ; i<brush->numsides ; i++)
    342 	{
    343 		w = brush->sides[i].winding;
    344 		if (w)
    345 			break;
    346 	}
    347 	if (!w)
    348 		return 0;
    349 	VectorCopy (w->p[0], corner);
    350 
    351 	// make tetrahedrons to all other faces
    352 
    353 	volume = 0;
    354 	for ( ; i<brush->numsides ; i++)
    355 	{
    356 		w = brush->sides[i].winding;
    357 		if (!w)
    358 			continue;
    359 		plane = &mapplanes[brush->sides[i].planenum];
    360 		d = -(DotProduct (corner, plane->normal) - plane->dist);
    361 		area = WindingArea (w);
    362 		volume += d*area;
    363 	}
    364 
    365 	volume /= 3;
    366 	return volume;
    367 }
    368 
    369 
    370 /*
    371 ==================
    372 WriteBspBrushMap
    373 ==================
    374 */
    375 void WriteBspBrushMap (char *name, bspbrush_t *list)
    376 {
    377 	FILE	*f;
    378 	side_t	*s;
    379 	int		i;
    380 	winding_t	*w;
    381 
    382 	_printf ("writing %s\n", name);
    383 	f = fopen (name, "wb");
    384 	if (!f)
    385 		Error ("Can't write %s\b", name);
    386 
    387 	fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
    388 
    389 	for ( ; list ; list=list->next )
    390 	{
    391 		fprintf (f, "{\n");
    392 		for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
    393 		{
    394 			w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
    395 
    396 			fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
    397 			fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
    398 			fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
    399 
    400 			fprintf (f, "notexture 0 0 0 1 1\n" );
    401 			FreeWinding (w);
    402 		}
    403 		fprintf (f, "}\n");
    404 	}
    405 	fprintf (f, "}\n");
    406 
    407 	fclose (f);
    408 
    409 }
    410 
    411 
    412 //=====================================================================================
    413 
    414 /*
    415 ====================
    416 FilterBrushIntoTree_r
    417 
    418 ====================
    419 */
    420 int FilterBrushIntoTree_r( bspbrush_t *b, node_t *node ) {
    421 	bspbrush_t		*front, *back;
    422 	int				c;
    423 
    424 	if ( !b ) {
    425 		return 0;
    426 	}
    427 
    428 	// add it to the leaf list
    429 	if ( node->planenum == PLANENUM_LEAF ) {
    430 		b->next = node->brushlist;
    431 		node->brushlist = b;
    432 
    433 		// classify the leaf by the structural brush
    434 		if ( !b->detail ) {
    435 			if ( b->opaque ) {
    436 				node->opaque = qtrue;
    437 				node->areaportal = qfalse;
    438 			} else if ( b->contents & CONTENTS_AREAPORTAL ) {
    439 				if ( !node->opaque ) {
    440 					node->areaportal = qtrue;
    441 				}
    442 			}
    443 		}
    444 
    445 		return 1;
    446 	}
    447 
    448 	// split it by the node plane
    449 	SplitBrush ( b, node->planenum, &front, &back );
    450 	FreeBrush( b );
    451 
    452 	c = 0;
    453 	c += FilterBrushIntoTree_r( front, node->children[0] );
    454 	c += FilterBrushIntoTree_r( back, node->children[1] );
    455 
    456 	return c;
    457 }
    458 
    459 /*
    460 =====================
    461 FilterDetailBrushesIntoTree
    462 
    463 Fragment all the detail brushes into the structural leafs
    464 =====================
    465 */
    466 void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree ) {
    467 	bspbrush_t			*b, *newb;
    468 	int					r;
    469 	int					c_unique, c_clusters;
    470 	int					i;
    471 
    472 	qprintf( "----- FilterDetailBrushesIntoTree -----\n");
    473 
    474 	c_unique = 0;
    475 	c_clusters = 0;
    476 	for ( b = e->brushes ; b ; b = b->next ) {
    477 		if ( !b->detail ) {
    478 			continue;
    479 		}
    480 		c_unique++;
    481 		newb = CopyBrush( b );
    482 		r = FilterBrushIntoTree_r( newb, tree->headnode );
    483 		c_clusters += r;
    484 
    485 		// mark all sides as visible so drawsurfs are created
    486 		if ( r ) {
    487 			for ( i = 0 ; i < b->numsides ; i++ ) {
    488 				if ( b->sides[i].winding ) {
    489 					b->sides[i].visible = qtrue;
    490 				}
    491 			}
    492 		}
    493 	}
    494 
    495 	qprintf( "%5i detail brushes\n", c_unique );
    496 	qprintf( "%5i cluster references\n", c_clusters );
    497 }
    498 
    499 /*
    500 =====================
    501 FilterStructuralBrushesIntoTree
    502 
    503 Mark the leafs as opaque and areaportals
    504 =====================
    505 */
    506 void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree ) {
    507 	bspbrush_t			*b, *newb;
    508 	int					r;
    509 	int					c_unique, c_clusters;
    510 	int					i;
    511 
    512 	qprintf( "----- FilterStructuralBrushesIntoTree -----\n");
    513 
    514 	c_unique = 0;
    515 	c_clusters = 0;
    516 	for ( b = e->brushes ; b ; b = b->next ) {
    517 		if ( b->detail ) {
    518 			continue;
    519 		}
    520 		c_unique++;
    521 		newb = CopyBrush( b );
    522 		r = FilterBrushIntoTree_r( newb, tree->headnode );
    523 		c_clusters += r;
    524 
    525 		// mark all sides as visible so drawsurfs are created
    526 		if ( r ) {
    527 			for ( i = 0 ; i < b->numsides ; i++ ) {
    528 				if ( b->sides[i].winding ) {
    529 					b->sides[i].visible = qtrue;
    530 				}
    531 			}
    532 		}
    533 	}
    534 
    535 	qprintf( "%5i structural brushes\n", c_unique );
    536 	qprintf( "%5i cluster references\n", c_clusters );
    537 }
    538 
    539 
    540 
    541 /*
    542 ================
    543 AllocTree
    544 ================
    545 */
    546 tree_t *AllocTree (void)
    547 {
    548 	tree_t	*tree;
    549 
    550 	tree = malloc(sizeof(*tree));
    551 	memset (tree, 0, sizeof(*tree));
    552 	ClearBounds (tree->mins, tree->maxs);
    553 
    554 	return tree;
    555 }
    556 
    557 /*
    558 ================
    559 AllocNode
    560 ================
    561 */
    562 node_t *AllocNode (void)
    563 {
    564 	node_t	*node;
    565 
    566 	node = malloc(sizeof(*node));
    567 	memset (node, 0, sizeof(*node));
    568 
    569 	return node;
    570 }
    571 
    572 
    573 /*
    574 ================
    575 WindingIsTiny
    576 
    577 Returns true if the winding would be crunched out of
    578 existance by the vertex snapping.
    579 ================
    580 */
    581 #define	EDGE_LENGTH	0.2
    582 qboolean WindingIsTiny (winding_t *w)
    583 {
    584 /*
    585 	if (WindingArea (w) < 1)
    586 		return qtrue;
    587 	return qfalse;
    588 */
    589 	int		i, j;
    590 	vec_t	len;
    591 	vec3_t	delta;
    592 	int		edges;
    593 
    594 	edges = 0;
    595 	for (i=0 ; i<w->numpoints ; i++)
    596 	{
    597 		j = i == w->numpoints - 1 ? 0 : i+1;
    598 		VectorSubtract (w->p[j], w->p[i], delta);
    599 		len = VectorLength (delta);
    600 		if (len > EDGE_LENGTH)
    601 		{
    602 			if (++edges == 3)
    603 				return qfalse;
    604 		}
    605 	}
    606 	return qtrue;
    607 }
    608 
    609 /*
    610 ================
    611 WindingIsHuge
    612 
    613 Returns true if the winding still has one of the points
    614 from basewinding for plane
    615 ================
    616 */
    617 qboolean WindingIsHuge (winding_t *w)
    618 {
    619 	int		i, j;
    620 
    621 	for (i=0 ; i<w->numpoints ; i++)
    622 	{
    623 		for (j=0 ; j<3 ; j++)
    624 			if (w->p[i][j] <= MIN_WORLD_COORD || w->p[i][j] >= MAX_WORLD_COORD)
    625 				return qtrue;
    626 	}
    627 	return qfalse;
    628 }
    629 
    630 //============================================================
    631 
    632 /*
    633 ==================
    634 BrushMostlyOnSide
    635 
    636 ==================
    637 */
    638 int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
    639 {
    640 	int			i, j;
    641 	winding_t	*w;
    642 	vec_t		d, max;
    643 	int			side;
    644 
    645 	max = 0;
    646 	side = PSIDE_FRONT;
    647 	for (i=0 ; i<brush->numsides ; i++)
    648 	{
    649 		w = brush->sides[i].winding;
    650 		if (!w)
    651 			continue;
    652 		for (j=0 ; j<w->numpoints ; j++)
    653 		{
    654 			d = DotProduct (w->p[j], plane->normal) - plane->dist;
    655 			if (d > max)
    656 			{
    657 				max = d;
    658 				side = PSIDE_FRONT;
    659 			}
    660 			if (-d > max)
    661 			{
    662 				max = -d;
    663 				side = PSIDE_BACK;
    664 			}
    665 		}
    666 	}
    667 	return side;
    668 }
    669 
    670 /*
    671 ================
    672 SplitBrush
    673 
    674 Generates two new brushes, leaving the original
    675 unchanged
    676 ================
    677 */
    678 void SplitBrush (bspbrush_t *brush, int planenum,
    679 	bspbrush_t **front, bspbrush_t **back)
    680 {
    681 	bspbrush_t	*b[2];
    682 	int			i, j;
    683 	winding_t	*w, *cw[2], *midwinding;
    684 	plane_t		*plane, *plane2;
    685 	side_t		*s, *cs;
    686 	float		d, d_front, d_back;
    687 
    688 	*front = *back = NULL;
    689 	plane = &mapplanes[planenum];
    690 
    691 	// check all points
    692 	d_front = d_back = 0;
    693 	for (i=0 ; i<brush->numsides ; i++)
    694 	{
    695 		w = brush->sides[i].winding;
    696 		if (!w)
    697 			continue;
    698 		for (j=0 ; j<w->numpoints ; j++)
    699 		{
    700 			d = DotProduct (w->p[j], plane->normal) - plane->dist;
    701 			if (d > 0 && d > d_front)
    702 				d_front = d;
    703 			if (d < 0 && d < d_back)
    704 				d_back = d;
    705 		}
    706 	}
    707 	if (d_front < 0.1) // PLANESIDE_EPSILON)
    708 	{	// only on back
    709 		*back = CopyBrush (brush);
    710 		return;
    711 	}
    712 	if (d_back > -0.1) // PLANESIDE_EPSILON)
    713 	{	// only on front
    714 		*front = CopyBrush (brush);
    715 		return;
    716 	}
    717 
    718 	// create a new winding from the split plane
    719 
    720 	w = BaseWindingForPlane (plane->normal, plane->dist);
    721 	for (i=0 ; i<brush->numsides && w ; i++)
    722 	{
    723 		if ( brush->sides[i].backSide ) {
    724 			continue;	// fake back-sided polygons never split
    725 		}
    726 		plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
    727 		ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
    728 	}
    729 
    730 	if (!w || WindingIsTiny (w) )
    731 	{	// the brush isn't really split
    732 		int		side;
    733 
    734 		side = BrushMostlyOnSide (brush, plane);
    735 		if (side == PSIDE_FRONT)
    736 			*front = CopyBrush (brush);
    737 		if (side == PSIDE_BACK)
    738 			*back = CopyBrush (brush);
    739 		return;
    740 	}
    741 
    742 	if (WindingIsHuge (w))
    743 	{
    744 		qprintf ("WARNING: huge winding\n");
    745 	}
    746 
    747 	midwinding = w;
    748 
    749 	// split it for real
    750 
    751 	for (i=0 ; i<2 ; i++)
    752 	{
    753 		b[i] = AllocBrush (brush->numsides+1);
    754 		memcpy( b[i], brush, sizeof( bspbrush_t ) - sizeof( brush->sides ) );
    755 		b[i]->numsides = 0;
    756 		b[i]->next = NULL;
    757 		b[i]->original = brush->original;
    758 	}
    759 
    760 	// split all the current windings
    761 
    762 	for (i=0 ; i<brush->numsides ; i++)
    763 	{
    764 		s = &brush->sides[i];
    765 		w = s->winding;
    766 		if (!w)
    767 			continue;
    768 		ClipWindingEpsilon (w, plane->normal, plane->dist,
    769 			0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
    770 		for (j=0 ; j<2 ; j++)
    771 		{
    772 			if (!cw[j])
    773 				continue;
    774 /*
    775 			if (WindingIsTiny (cw[j]))
    776 			{
    777 				FreeWinding (cw[j]);
    778 				continue;
    779 			}
    780 */
    781 			cs = &b[j]->sides[b[j]->numsides];
    782 			b[j]->numsides++;
    783 			*cs = *s;
    784 			cs->winding = cw[j];
    785 		}
    786 	}
    787 
    788 
    789 	// see if we have valid polygons on both sides
    790 
    791 	for (i=0 ; i<2 ; i++)
    792 	{
    793 		BoundBrush (b[i]);
    794 		for (j=0 ; j<3 ; j++)
    795 		{
    796 			if (b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD)
    797 			{
    798 				qprintf ("bogus brush after clip\n");
    799 				break;
    800 			}
    801 		}
    802 
    803 		if (b[i]->numsides < 3 || j < 3)
    804 		{
    805 			FreeBrush (b[i]);
    806 			b[i] = NULL;
    807 		}
    808 	}
    809 
    810 	if ( !(b[0] && b[1]) )
    811 	{
    812 		if (!b[0] && !b[1])
    813 			qprintf ("split removed brush\n");
    814 		else
    815 			qprintf ("split not on both sides\n");
    816 		if (b[0])
    817 		{
    818 			FreeBrush (b[0]);
    819 			*front = CopyBrush (brush);
    820 		}
    821 		if (b[1])
    822 		{
    823 			FreeBrush (b[1]);
    824 			*back = CopyBrush (brush);
    825 		}
    826 		return;
    827 	}
    828 
    829 	// add the midwinding to both sides
    830 	for (i=0 ; i<2 ; i++)
    831 	{
    832 		cs = &b[i]->sides[b[i]->numsides];
    833 		b[i]->numsides++;
    834 
    835 		cs->planenum = planenum^i^1;
    836 		cs->shaderInfo = NULL;
    837 		if (i==0)
    838 			cs->winding = CopyWinding (midwinding);
    839 		else
    840 			cs->winding = midwinding;
    841 	}
    842 
    843 {
    844 	vec_t	v1;
    845 	int		i;
    846 
    847 	for (i=0 ; i<2 ; i++)
    848 	{
    849 		v1 = BrushVolume (b[i]);
    850 		if (v1 < 1.0)
    851 		{
    852 			FreeBrush (b[i]);
    853 			b[i] = NULL;
    854 //			qprintf ("tiny volume after clip\n");
    855 		}
    856 	}
    857 }
    858 
    859 	*front = b[0];
    860 	*back = b[1];
    861 }