1 /* -------------------------------------------------------------------------------
2
3 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
5
6 This file is part of GtkRadiant.
7
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
22 ----------------------------------------------------------------------------------
23
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
26
27 ------------------------------------------------------------------------------- */
28
29
30
31 /* marker */
32 #define BRUSH_C
33
34
35
36 /* dependencies */
37 #include "q3map2.h"
38
39
40
41 /* -------------------------------------------------------------------------------
42
43 functions
44
45 ------------------------------------------------------------------------------- */
46
47 /*
48 AllocSideRef() - ydnar
49 allocates and assigns a brush side reference
50 */
51
AllocSideRef(side_t * side,sideRef_t * next)52 sideRef_t *AllocSideRef( side_t *side, sideRef_t *next )
53 {
54 sideRef_t *sideRef;
55
56
57 /* dummy check */
58 if( side == NULL )
59 return next;
60
61 /* allocate and return */
62 sideRef = safe_malloc( sizeof( *sideRef ) );
63 sideRef->side = side;
64 sideRef->next = next;
65 return sideRef;
66 }
67
68
69
70 /*
71 CountBrushList()
72 counts the number of brushes in a brush linked list
73 */
74
CountBrushList(brush_t * brushes)75 int CountBrushList( brush_t *brushes )
76 {
77 int c = 0;
78
79
80 /* count brushes */
81 for( brushes; brushes != NULL; brushes = brushes->next )
82 c++;
83 return c;
84 }
85
86
87
88 /*
89 AllocBrush()
90 allocates a new brush
91 */
92
AllocBrush(int numSides)93 brush_t *AllocBrush( int numSides )
94 {
95 brush_t *bb;
96 int c;
97
98
99 /* allocate and clear */
100 if( numSides <= 0 )
101 Error( "AllocBrush called with numsides = %d", numSides );
102 c = (int) &(((brush_t*) 0)->sides[ numSides ]);
103 bb = safe_malloc( c );
104 memset( bb, 0, c );
105 if( numthreads == 1 )
106 numActiveBrushes++;
107
108 /* return it */
109 return bb;
110 }
111
112
113
114 /*
115 FreeBrush()
116 frees a single brush and all sides/windings
117 */
118
FreeBrush(brush_t * b)119 void FreeBrush( brush_t *b )
120 {
121 int i;
122
123
124 /* error check */
125 if( *((int*) b) == 0xFEFEFEFE )
126 {
127 Sys_FPrintf( SYS_VRB, "WARNING: Attempt to free an already freed brush!\n" );
128 return;
129 }
130
131 /* free brush sides */
132 for( i = 0; i < b->numsides; i++ )
133 if( b->sides[i].winding != NULL )
134 FreeWinding( b->sides[ i ].winding );
135
136 /* ydnar: overwrite it */
137 memset( b, 0xFE, (int) &(((brush_t*) 0)->sides[ b->numsides ]) );
138 *((int*) b) = 0xFEFEFEFE;
139
140 /* free it */
141 free( b );
142 if( numthreads == 1 )
143 numActiveBrushes--;
144 }
145
146
147
148 /*
149 FreeBrushList()
150 frees a linked list of brushes
151 */
152
FreeBrushList(brush_t * brushes)153 void FreeBrushList( brush_t *brushes )
154 {
155 brush_t *next;
156
157
158 /* walk brush list */
159 for( brushes; brushes != NULL; brushes = next )
160 {
161 next = brushes->next;
162 FreeBrush( brushes );
163 }
164 }
165
166
167
168 /*
169 CopyBrush()
170 duplicates the brush, sides, and windings
171 */
172
CopyBrush(brush_t * brush)173 brush_t *CopyBrush( brush_t *brush )
174 {
175 brush_t *newBrush;
176 int size;
177 int i;
178
179
180 /* copy brush */
181 size = (int) &(((brush_t*) 0)->sides[ brush->numsides ]);
182 newBrush = AllocBrush( brush->numsides );
183 memcpy( newBrush, brush, size );
184
185 /* ydnar: nuke linked list */
186 newBrush->next = NULL;
187
188 /* copy sides */
189 for( i = 0; i < brush->numsides; i++ )
190 {
191 if( brush->sides[ i ].winding != NULL )
192 newBrush->sides[ i ].winding = CopyWinding( brush->sides[ i ].winding );
193 }
194
195 /* return it */
196 return newBrush;
197 }
198
199
200
201
202 /*
203 BoundBrush()
204 sets the mins/maxs based on the windings
205 returns false if the brush doesn't enclose a valid volume
206 */
207
BoundBrush(brush_t * brush)208 qboolean BoundBrush( brush_t *brush )
209 {
210 int i, j;
211 winding_t *w;
212
213
214 ClearBounds( brush->mins, brush->maxs );
215 for( i = 0; i < brush->numsides; i++ )
216 {
217 w = brush->sides[ i ].winding;
218 if( w == NULL )
219 continue;
220 for( j = 0; j < w->numpoints; j++ )
221 AddPointToBounds( w->p[ j ], brush->mins, brush->maxs );
222 }
223
224 for( i = 0; i < 3; i++ )
225 {
226 if( brush->mins[ i ] < MIN_WORLD_COORD || brush->maxs[ i ] > MAX_WORLD_COORD || brush->mins[i] >= brush->maxs[ i ] )
227 return qfalse;
228 }
229
230 return qtrue;
231 }
232
233
234
235
236 /*
237 SnapWeldVector() - ydnar
238 welds two vec3_t's into a third, taking into account nearest-to-integer
239 instead of averaging
240 */
241
242 #define SNAP_EPSILON 0.01
243
SnapWeldVector(vec3_t a,vec3_t b,vec3_t out)244 void SnapWeldVector( vec3_t a, vec3_t b, vec3_t out )
245 {
246 int i;
247 vec_t ai, bi, outi;
248
249
250 /* dummy check */
251 if( a == NULL || b == NULL || out == NULL )
252 return;
253
254 /* do each element */
255 for( i = 0; i < 3; i++ )
256 {
257 /* round to integer */
258 ai = Q_rint( a[ i ] );
259 bi = Q_rint( a[ i ] );
260
261 /* prefer exact integer */
262 if( ai == a[ i ] )
263 out[ i ] = a[ i ];
264 else if( bi == b[ i ] )
265 out[ i ] = b[ i ];
266
267 /* use nearest */
268 else if( fabs( ai - a[ i ] ) < fabs( bi < b[ i ] ) )
269 out[ i ] = a[ i ];
270 else
271 out[ i ] = b[ i ];
272
273 /* snap */
274 outi = Q_rint( out[ i ] );
275 if( fabs( outi - out[ i ] ) <= SNAP_EPSILON )
276 out[ i ] = outi;
277 }
278 }
279
280
281
282 /*
283 FixWinding() - ydnar
284 removes degenerate edges from a winding
285 returns qtrue if the winding is valid
286 */
287
288 #define DEGENERATE_EPSILON 0.1
289
FixWinding(winding_t * w)290 qboolean FixWinding( winding_t *w )
291 {
292 qboolean valid = qtrue;
293 int i, j, k;
294 vec3_t vec;
295 float dist;
296
297
298 /* dummy check */
299 if( !w )
300 return qfalse;
301
302 /* check all verts */
303 for( i = 0; i < w->numpoints; i++ )
304 {
305 /* don't remove points if winding is a triangle */
306 if( w->numpoints == 3 )
307 return valid;
308
309 /* get second point index */
310 j = (i + 1) % w->numpoints;
311
312 /* degenerate edge? */
313 VectorSubtract( w->p[ i ], w->p[ j ], vec );
314 dist = VectorLength( vec );
315 if( dist < DEGENERATE_EPSILON )
316 {
317 valid = qfalse;
318 //Sys_FPrintf( SYS_VRB, "WARNING: Degenerate winding edge found, fixing...\n" );
319
320 /* create an average point (ydnar 2002-01-26: using nearest-integer weld preference) */
321 SnapWeldVector( w->p[ i ], w->p[ j ], vec );
322 VectorCopy( vec, w->p[ i ] );
323 //VectorAdd( w->p[ i ], w->p[ j ], vec );
324 //VectorScale( vec, 0.5, w->p[ i ] );
325
326 /* move the remaining verts */
327 for( k = i + 2; k < w->numpoints; k++ )
328 {
329 VectorCopy( w->p[ k ], w->p[ k - 1 ] );
330 }
331 w->numpoints--;
332 }
333 }
334
335 /* one last check and return */
336 if( w->numpoints < 3 )
337 valid = qfalse;
338 return valid;
339 }
340
341
342
343
344
345
346
347 /*
348 CreateBrushWindings()
349 makes basewindigs for sides and mins/maxs for the brush
350 returns false if the brush doesn't enclose a valid volume
351 */
352
CreateBrushWindings(brush_t * brush)353 qboolean CreateBrushWindings( brush_t *brush )
354 {
355 int i, j;
356 winding_t *w;
357 side_t *side;
358 plane_t *plane;
359
360
361 /* walk the list of brush sides */
362 for( i = 0; i < brush->numsides; i++ )
363 {
364 /* get side and plane */
365 side = &brush->sides[ i ];
366 plane = &mapplanes[ side->planenum ];
367
368 /* make huge winding */
369 w = BaseWindingForPlane( plane->normal, plane->dist );
370
371 /* walk the list of brush sides */
372 for( j = 0; j < brush->numsides && w != NULL; j++ )
373 {
374 if( i == j )
375 continue;
376 if( brush->sides[ j ].planenum == (brush->sides[ i ].planenum ^ 1) )
377 continue; /* back side clipaway */
378 if( brush->sides[ j ].bevel )
379 continue;
380 plane = &mapplanes[ brush->sides[ j ].planenum ^ 1 ];
381 ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); // CLIP_EPSILON );
382
383 /* ydnar: fix broken windings that would generate trifans */
384 FixWinding( w );
385 }
386
387 /* set side winding */
388 side->winding = w;
389 }
390
391 /* find brush bounds */
392 return BoundBrush( brush );
393 }
394
395
396
397
398 /*
399 ==================
400 BrushFromBounds
401
402 Creates a new axial brush
403 ==================
404 */
BrushFromBounds(vec3_t mins,vec3_t maxs)405 brush_t *BrushFromBounds (vec3_t mins, vec3_t maxs)
406 {
407 brush_t *b;
408 int i;
409 vec3_t normal;
410 vec_t dist;
411
412 b = AllocBrush (6);
413 b->numsides = 6;
414 for (i=0 ; i<3 ; i++)
415 {
416 VectorClear (normal);
417 normal[i] = 1;
418 dist = maxs[i];
419 b->sides[i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &maxs );
420
421 normal[i] = -1;
422 dist = -mins[i];
423 b->sides[3+i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &mins );
424 }
425
426 CreateBrushWindings (b);
427
428 return b;
429 }
430
431 /*
432 ==================
433 BrushVolume
434
435 ==================
436 */
BrushVolume(brush_t * brush)437 vec_t BrushVolume (brush_t *brush)
438 {
439 int i;
440 winding_t *w;
441 vec3_t corner;
442 vec_t d, area, volume;
443 plane_t *plane;
444
445 if (!brush)
446 return 0;
447
448 // grab the first valid point as the corner
449
450 w = NULL;
451 for (i=0 ; i<brush->numsides ; i++)
452 {
453 w = brush->sides[i].winding;
454 if (w)
455 break;
456 }
457 if (!w)
458 return 0;
459 VectorCopy (w->p[0], corner);
460
461 // make tetrahedrons to all other faces
462
463 volume = 0;
464 for ( ; i<brush->numsides ; i++)
465 {
466 w = brush->sides[i].winding;
467 if (!w)
468 continue;
469 plane = &mapplanes[brush->sides[i].planenum];
470 d = -(DotProduct (corner, plane->normal) - plane->dist);
471 area = WindingArea (w);
472 volume += d*area;
473 }
474
475 volume /= 3;
476 return volume;
477 }
478
479
480
481 /*
482 WriteBSPBrushMap()
483 writes a map with the split bsp brushes
484 */
485
WriteBSPBrushMap(char * name,brush_t * list)486 void WriteBSPBrushMap( char *name, brush_t *list )
487 {
488 FILE *f;
489 side_t *s;
490 int i;
491 winding_t *w;
492
493
494 /* note it */
495 Sys_Printf( "Writing %s\n", name );
496
497 /* open the map file */
498 f = fopen( name, "wb" );
499 if( f == NULL )
500 Error( "Can't write %s\b", name );
501
502 fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
503
504 for ( ; list ; list=list->next )
505 {
506 fprintf (f, "{\n");
507 for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
508 {
509 w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
510
511 fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
512 fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
513 fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
514
515 fprintf (f, "notexture 0 0 0 1 1\n" );
516 FreeWinding (w);
517 }
518 fprintf (f, "}\n");
519 }
520 fprintf (f, "}\n");
521
522 fclose (f);
523
524 }
525
526
527
528 /*
529 FilterBrushIntoTree_r()
530 adds brush reference to any intersecting bsp leafnode
531 */
532
FilterBrushIntoTree_r(brush_t * b,node_t * node)533 int FilterBrushIntoTree_r( brush_t *b, node_t *node )
534 {
535 brush_t *front, *back;
536 int c;
537
538
539 /* dummy check */
540 if( b == NULL )
541 return 0;
542
543 /* add it to the leaf list */
544 if( node->planenum == PLANENUM_LEAF )
545 {
546 /* something somewhere is hammering brushlist */
547 b->next = node->brushlist;
548 node->brushlist = b;
549
550 /* classify the leaf by the structural brush */
551 if( !b->detail )
552 {
553 if( b->opaque )
554 {
555 node->opaque = qtrue;
556 node->areaportal = qfalse;
557 }
558 else if( b->compileFlags & C_AREAPORTAL )
559 {
560 if( !node->opaque )
561 node->areaportal = qtrue;
562 }
563 }
564
565 return 1;
566 }
567
568 /* split it by the node plane */
569 c = b->numsides;
570 SplitBrush( b, node->planenum, &front, &back );
571 FreeBrush( b );
572
573 c = 0;
574 c += FilterBrushIntoTree_r( front, node->children[ 0 ] );
575 c += FilterBrushIntoTree_r( back, node->children[ 1 ] );
576
577 return c;
578 }
579
580
581
582 /*
583 FilterDetailBrushesIntoTree
584 fragment all the detail brushes into the structural leafs
585 */
586
FilterDetailBrushesIntoTree(entity_t * e,tree_t * tree)587 void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree )
588 {
589 brush_t *b, *newb;
590 int r;
591 int c_unique, c_clusters;
592 int i;
593
594
595 /* note it */
596 Sys_FPrintf( SYS_VRB, "--- FilterDetailBrushesIntoTree ---\n" );
597
598 /* walk the list of brushes */
599 c_unique = 0;
600 c_clusters = 0;
601 for( b = e->brushes; b; b = b->next )
602 {
603 if( !b->detail )
604 continue;
605 c_unique++;
606 newb = CopyBrush( b );
607 r = FilterBrushIntoTree_r( newb, tree->headnode );
608 c_clusters += r;
609
610 /* mark all sides as visible so drawsurfs are created */
611 if( r )
612 {
613 for( i = 0; i < b->numsides; i++ )
614 {
615 if( b->sides[ i ].winding )
616 b->sides[ i ].visible = qtrue;
617 }
618 }
619 }
620
621 /* emit some statistics */
622 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_unique );
623 Sys_FPrintf( SYS_VRB, "%9d cluster references\n", c_clusters );
624 }
625
626 /*
627 =====================
628 FilterStructuralBrushesIntoTree
629
630 Mark the leafs as opaque and areaportals
631 =====================
632 */
FilterStructuralBrushesIntoTree(entity_t * e,tree_t * tree)633 void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree ) {
634 brush_t *b, *newb;
635 int r;
636 int c_unique, c_clusters;
637 int i;
638
639 Sys_FPrintf (SYS_VRB, "--- FilterStructuralBrushesIntoTree ---\n");
640
641 c_unique = 0;
642 c_clusters = 0;
643 for ( b = e->brushes ; b ; b = b->next ) {
644 if ( b->detail ) {
645 continue;
646 }
647 c_unique++;
648 newb = CopyBrush( b );
649 r = FilterBrushIntoTree_r( newb, tree->headnode );
650 c_clusters += r;
651
652 // mark all sides as visible so drawsurfs are created
653 if ( r ) {
654 for ( i = 0 ; i < b->numsides ; i++ ) {
655 if ( b->sides[i].winding ) {
656 b->sides[i].visible = qtrue;
657 }
658 }
659 }
660 }
661
662 /* emit some statistics */
663 Sys_FPrintf( SYS_VRB, "%9d structural brushes\n", c_unique );
664 Sys_FPrintf( SYS_VRB, "%9d cluster references\n", c_clusters );
665 }
666
667
668
669 /*
670 ================
671 AllocTree
672 ================
673 */
AllocTree(void)674 tree_t *AllocTree (void)
675 {
676 tree_t *tree;
677
678 tree = safe_malloc(sizeof(*tree));
679 memset (tree, 0, sizeof(*tree));
680 ClearBounds (tree->mins, tree->maxs);
681
682 return tree;
683 }
684
685 /*
686 ================
687 AllocNode
688 ================
689 */
AllocNode(void)690 node_t *AllocNode (void)
691 {
692 node_t *node;
693
694 node = safe_malloc(sizeof(*node));
695 memset (node, 0, sizeof(*node));
696
697 return node;
698 }
699
700
701 /*
702 ================
703 WindingIsTiny
704
705 Returns true if the winding would be crunched out of
706 existance by the vertex snapping.
707 ================
708 */
709 #define EDGE_LENGTH 0.2
WindingIsTiny(winding_t * w)710 qboolean WindingIsTiny (winding_t *w)
711 {
712 /*
713 if (WindingArea (w) < 1)
714 return qtrue;
715 return qfalse;
716 */
717 int i, j;
718 vec_t len;
719 vec3_t delta;
720 int edges;
721
722 edges = 0;
723 for (i=0 ; i<w->numpoints ; i++)
724 {
725 j = i == w->numpoints - 1 ? 0 : i+1;
726 VectorSubtract (w->p[j], w->p[i], delta);
727 len = VectorLength (delta);
728 if (len > EDGE_LENGTH)
729 {
730 if (++edges == 3)
731 return qfalse;
732 }
733 }
734 return qtrue;
735 }
736
737 /*
738 ================
739 WindingIsHuge
740
741 Returns true if the winding still has one of the points
742 from basewinding for plane
743 ================
744 */
WindingIsHuge(winding_t * w)745 qboolean WindingIsHuge (winding_t *w)
746 {
747 int i, j;
748
749 for (i=0 ; i<w->numpoints ; i++)
750 {
751 for (j=0 ; j<3 ; j++)
752 if (w->p[i][j] <= MIN_WORLD_COORD || w->p[i][j] >= MAX_WORLD_COORD)
753 return qtrue;
754 }
755 return qfalse;
756 }
757
758 //============================================================
759
760 /*
761 ==================
762 BrushMostlyOnSide
763
764 ==================
765 */
BrushMostlyOnSide(brush_t * brush,plane_t * plane)766 int BrushMostlyOnSide (brush_t *brush, plane_t *plane)
767 {
768 int i, j;
769 winding_t *w;
770 vec_t d, max;
771 int side;
772
773 max = 0;
774 side = PSIDE_FRONT;
775 for (i=0 ; i<brush->numsides ; i++)
776 {
777 w = brush->sides[i].winding;
778 if (!w)
779 continue;
780 for (j=0 ; j<w->numpoints ; j++)
781 {
782 d = DotProduct (w->p[j], plane->normal) - plane->dist;
783 if (d > max)
784 {
785 max = d;
786 side = PSIDE_FRONT;
787 }
788 if (-d > max)
789 {
790 max = -d;
791 side = PSIDE_BACK;
792 }
793 }
794 }
795 return side;
796 }
797
798
799
800 /*
801 SplitBrush()
802 generates two new brushes, leaving the original unchanged
803 */
804
SplitBrush(brush_t * brush,int planenum,brush_t ** front,brush_t ** back)805 void SplitBrush( brush_t *brush, int planenum, brush_t **front, brush_t **back )
806 {
807 brush_t *b[2];
808 int i, j;
809 winding_t *w, *cw[2], *midwinding;
810 plane_t *plane, *plane2;
811 side_t *s, *cs;
812 float d, d_front, d_back;
813
814
815 *front = NULL;
816 *back = NULL;
817 plane = &mapplanes[planenum];
818
819 // check all points
820 d_front = d_back = 0;
821 for (i=0 ; i<brush->numsides ; i++)
822 {
823 w = brush->sides[i].winding;
824 if (!w)
825 continue;
826 for (j=0 ; j<w->numpoints ; j++)
827 {
828 d = DotProduct (w->p[j], plane->normal) - plane->dist;
829 if (d > 0 && d > d_front)
830 d_front = d;
831 if (d < 0 && d < d_back)
832 d_back = d;
833 }
834 }
835
836 if (d_front < 0.1) // PLANESIDE_EPSILON)
837 { // only on back
838 *back = CopyBrush( brush );
839 return;
840 }
841
842 if (d_back > -0.1) // PLANESIDE_EPSILON)
843 { // only on front
844 *front = CopyBrush( brush );
845 return;
846 }
847
848 // create a new winding from the split plane
849 w = BaseWindingForPlane (plane->normal, plane->dist);
850 for (i=0 ; i<brush->numsides && w ; i++)
851 {
852 plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
853 ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
854 }
855
856 if (!w || WindingIsTiny (w) )
857 { // the brush isn't really split
858 int side;
859
860 side = BrushMostlyOnSide (brush, plane);
861 if (side == PSIDE_FRONT)
862 *front = CopyBrush (brush);
863 if (side == PSIDE_BACK)
864 *back = CopyBrush (brush);
865 return;
866 }
867
868 if( WindingIsHuge( w ) )
869 Sys_FPrintf( SYS_VRB,"WARNING: huge winding\n" );
870
871 midwinding = w;
872
873 // split it for real
874
875 for (i=0 ; i<2 ; i++)
876 {
877 b[i] = AllocBrush (brush->numsides+1);
878 memcpy( b[i], brush, sizeof( brush_t ) - sizeof( brush->sides ) );
879 b[i]->numsides = 0;
880 b[i]->next = NULL;
881 b[i]->original = brush->original;
882 }
883
884 // split all the current windings
885
886 for (i=0 ; i<brush->numsides ; i++)
887 {
888 s = &brush->sides[i];
889 w = s->winding;
890 if (!w)
891 continue;
892 ClipWindingEpsilon (w, plane->normal, plane->dist,
893 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
894 for (j=0 ; j<2 ; j++)
895 {
896 if (!cw[j])
897 continue;
898 cs = &b[j]->sides[b[j]->numsides];
899 b[j]->numsides++;
900 *cs = *s;
901 cs->winding = cw[j];
902 }
903 }
904
905
906 // see if we have valid polygons on both sides
907 for (i=0 ; i<2 ; i++)
908 {
909 BoundBrush (b[i]);
910 for (j=0 ; j<3 ; j++)
911 {
912 if (b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD)
913 {
914 Sys_FPrintf (SYS_VRB,"bogus brush after clip\n");
915 break;
916 }
917 }
918
919 if (b[i]->numsides < 3 || j < 3)
920 {
921 FreeBrush (b[i]);
922 b[i] = NULL;
923 }
924 }
925
926 if ( !(b[0] && b[1]) )
927 {
928 if (!b[0] && !b[1])
929 Sys_FPrintf (SYS_VRB,"split removed brush\n");
930 else
931 Sys_FPrintf (SYS_VRB,"split not on both sides\n");
932 if (b[0])
933 {
934 FreeBrush (b[0]);
935 *front = CopyBrush (brush);
936 }
937 if (b[1])
938 {
939 FreeBrush (b[1]);
940 *back = CopyBrush (brush);
941 }
942 return;
943 }
944
945 // add the midwinding to both sides
946 for (i=0 ; i<2 ; i++)
947 {
948 cs = &b[i]->sides[b[i]->numsides];
949 b[i]->numsides++;
950
951 cs->planenum = planenum^i^1;
952 cs->shaderInfo = NULL;
953 if (i==0)
954 cs->winding = CopyWinding (midwinding);
955 else
956 cs->winding = midwinding;
957 }
958
959 {
960 vec_t v1;
961 int i;
962
963
964 for (i=0 ; i<2 ; i++)
965 {
966 v1 = BrushVolume (b[i]);
967 if (v1 < 1.0)
968 {
969 FreeBrush (b[i]);
970 b[i] = NULL;
971 // Sys_FPrintf (SYS_VRB,"tiny volume after clip\n");
972 }
973 }
974 }
975
976 *front = b[0];
977 *back = b[1];
978 }
979