3e54e79717b554c6857a43bf87c721fb48dedc85
[xonotic/netradiant.git] / contrib / bobtoolz / DBrush.cpp
1 /*
2    BobToolz plugin for GtkRadiant
3    Copyright (C) 2001 Gordon Biggans
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 // DBrush.cpp: implementation of the DBrush class.
21 //
22 //////////////////////////////////////////////////////////////////////
23
24 #include "StdAfx.h"
25
26 #ifdef _WIN32
27 #pragma warning(disable : 4786)
28 #endif
29
30 #include "DBrush.h"
31 #include "DWinding.h"
32 #include "dialogs/dialogs-gtk.h"
33
34 #include "misc.h"
35
36 //////////////////////////////////////////////////////////////////////
37 // Construction/Destruction
38 //////////////////////////////////////////////////////////////////////
39
40 DBrush::DBrush( int ID ){
41         m_nBrushID = ID;
42         bBoundsBuilt = FALSE;
43         QER_brush = NULL;
44 }
45
46 DBrush::~DBrush(){
47         ClearFaces();
48         ClearPoints();
49 }
50
51 //////////////////////////////////////////////////////////////////////
52 // Implementation
53 //////////////////////////////////////////////////////////////////////
54
55 DPlane* DBrush::AddFace( vec3_t va, vec3_t vb, vec3_t vc, _QERFaceData* texData ){
56 #ifdef _DEBUG
57 //      Sys_Printf("(%f %f %f) (%f %f %f) (%f %f %f)\n", va[0], va[1], va[2], vb[0], vb[1], vb[2], vc[0], vc[1], vc[2]);
58 #endif
59         bBoundsBuilt = FALSE;
60         DPlane* newFace = new DPlane( va, vb, vc, texData );
61         faceList.push_back( newFace );
62
63         return newFace;
64 }
65
66 int DBrush::BuildPoints(){
67         ClearPoints();
68
69         if ( faceList.size() <= 3 ) {  // if less than 3 faces, there can be no points
70                 return 0;                   // with only 3 faces u can't have a bounded soild
71
72         }
73         for ( list<DPlane *>::const_iterator p1 = faceList.begin(); p1 != faceList.end(); p1++ )
74         {
75                 list<DPlane *>::const_iterator p2 = p1;
76                 for ( p2++; p2 != faceList.end(); p2++ )
77                 {
78                         list<DPlane *>::const_iterator p3 = p2;
79                         for ( p3++; p3 != faceList.end(); p3++ )
80                         {
81                                 vec3_t pnt;
82                                 if ( ( *p1 )->PlaneIntersection( *p2, *p3, pnt ) ) {
83                                         int pos = PointPosition( pnt );
84
85                                         if ( pos == POINT_IN_BRUSH ) { // ???? shouldn't happen here
86                                                 Sys_Printf( "ERROR:: Build Brush Points: Point IN brush!!!\n" );
87                                         }
88                                         else if ( pos == POINT_ON_BRUSH ) { // normal point
89                                                 if ( !HasPoint( pnt ) ) {
90                                                         AddPoint( pnt );
91                                                 }
92 /*                                              else
93                             Sys_Printf("Duplicate Point Found, pyramids ahoy!!!!!\n");*/
94                                                 // point lies on more that 3 planes
95                                         }
96
97                                         // otherwise point is removed due to another plane..
98
99                                         // Sys_Printf("(%f, %f, %f)\n", pnt[0], pnt[1], pnt[2]);
100                                 }
101                         }
102                 }
103         }
104
105 #ifdef _DEBUG
106 //      Sys_Printf("%i points on brush\n", pointList.size());
107 #endif
108
109         return pointList.size();
110 }
111
112 void DBrush::LoadFromBrush_t( brush_t* brush, bool textured ){
113         ClearFaces();
114         ClearPoints();
115
116         for ( int i = g_FuncTable.m_pfnGetFaceCount( brush ) - 1; i >= 0 ; i-- )
117         {   // running backwards so i dont have to use the count function each time (OPT)
118                 _QERFaceData* faceData = g_FuncTable.m_pfnGetFaceData( brush, i );
119
120                 if ( faceData == NULL ) {
121                         DoMessageBox( "Null pointer returned", "WARNING!", MB_OK );
122                 }
123
124                 if ( textured ) {
125                         AddFace( faceData->m_v1, faceData->m_v2, faceData->m_v3, faceData );
126                 }
127                 else{
128                         AddFace( faceData->m_v1, faceData->m_v2, faceData->m_v3, NULL );
129                 }
130         }
131
132         QER_brush = brush;
133 }
134
135 int DBrush::PointPosition( vec3_t pnt ){
136         int state = POINT_IN_BRUSH; // if nothing happens point is inside brush
137
138         for ( list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
139         {
140                 float dist = ( *chkPlane )->DistanceToPoint( pnt );
141
142                 if ( dist > MAX_ROUND_ERROR ) {
143                         return POINT_OUT_BRUSH;     // if point is in front of plane, it CANT be in the brush
144                 }
145                 else if ( fabs( dist ) < MAX_ROUND_ERROR ) {
146                         state = POINT_ON_BRUSH;     // if point is ON plane point is either ON the brush
147                 }
148                 // or outside it, it can no longer be in it
149         }
150
151         return state;
152 }
153
154 void DBrush::ClearPoints(){
155         for ( list<DPoint *>::const_iterator deadPoint = pointList.begin(); deadPoint != pointList.end(); deadPoint++ ) {
156                 delete *deadPoint;
157         }
158         pointList.clear();
159 }
160
161 void DBrush::ClearFaces(){
162         bBoundsBuilt = FALSE;
163         for ( list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++ )
164         {
165                 delete *deadPlane;
166         }
167         faceList.clear();
168 }
169
170 void DBrush::AddPoint( vec3_t pnt ){
171         DPoint* newPoint = new DPoint;
172         VectorCopy( pnt, newPoint->_pnt );
173         pointList.push_back( newPoint );
174 }
175
176 bool DBrush::HasPoint( vec3_t pnt ){
177         for ( list<DPoint *>::const_iterator chkPoint = pointList.begin(); chkPoint != pointList.end(); chkPoint++ )
178         {
179                 if ( **chkPoint == pnt ) {
180                         return TRUE;
181                 }
182         }
183
184         return FALSE;
185 }
186
187 int DBrush::RemoveRedundantPlanes(){
188         int cnt = 0;
189         list<DPlane *>::iterator chkPlane;
190
191         // find duplicate planes
192         list<DPlane *>::iterator p1 = faceList.begin();
193
194         while ( p1 != faceList.end() )
195         {
196                 list<DPlane *>::iterator p2 = p1;
197
198                 for ( p2++; p2 != faceList.end(); p2++ )
199                 {
200                         if ( **p1 == **p2 ) {
201                                 if ( !strcmp( ( *p1 )->texInfo.m_TextureName, "textures/common/caulk" ) ) {
202                                         delete *p1;
203                                         p1 = faceList.erase( p1 );    // duplicate plane
204                                 }
205                                 else
206                                 {
207                                         delete *p2;
208                                         p2 = faceList.erase( p2 );    // duplicate plane
209                                 }
210
211                                 cnt++;
212                                 break;
213                         }
214                 }
215
216                 if ( p2 == faceList.end() ) {
217                         p1++;
218                 }
219         }
220
221         //+djbob kill planes with bad normal, they are more of a nuisance than losing a brush
222         chkPlane = faceList.begin();
223         while ( chkPlane != faceList.end() )
224         {
225                 if ( VectorLength( ( *chkPlane )->normal ) == 0 ) { // plane has bad normal
226                         delete *chkPlane;
227                         chkPlane = faceList.erase( chkPlane );
228                         cnt++;
229                 }
230                 else {
231                         chkPlane++;
232                 }
233         }
234         //-djbob
235
236         if ( pointList.size() == 0 ) { // if points may not have been built, build them
237 /*              if(BuildPoints() == 0)  // just let the planes die if they are all bad
238             return cnt;*/
239                 BuildPoints();
240         }
241
242         chkPlane = faceList.begin();
243         while ( chkPlane != faceList.end() )
244         {
245                 if ( ( *chkPlane )->IsRedundant( pointList ) ) { // checks that plane "0wnz" :), 3 or more points
246                         delete *chkPlane;
247                         chkPlane = faceList.erase( chkPlane );
248                         cnt++;
249                 }
250                 else{
251                         chkPlane++;
252                 }
253         }
254
255         return cnt;
256 }
257
258 bool DBrush::GetBounds( vec3_t min, vec3_t max ){
259         BuildBounds();
260
261         if ( !bBoundsBuilt ) {
262                 return FALSE;
263         }
264
265         VectorCopy( bbox_min, min );
266         VectorCopy( bbox_max, max );
267
268         return TRUE;
269 }
270
271 bool DBrush::BBoxCollision( DBrush* chkBrush ){
272         vec3_t min1, min2;
273         vec3_t max1, max2;
274
275         GetBounds( min1, max1 );
276         chkBrush->GetBounds( min2, max2 );
277
278         if ( min1[0] >= max2[0] ) {
279                 return FALSE;
280         }
281         if ( min1[1] >= max2[1] ) {
282                 return FALSE;
283         }
284         if ( min1[2] >= max2[2] ) {
285                 return FALSE;
286         }
287
288         if ( max1[0] <= min2[0] ) {
289                 return FALSE;
290         }
291         if ( max1[1] <= min2[1] ) {
292                 return FALSE;
293         }
294         if ( max1[2] <= min2[2] ) {
295                 return FALSE;
296         }
297
298         return TRUE;
299 }
300
301 DPlane* DBrush::HasPlane( DPlane* chkPlane ){
302         for ( list<DPlane *>::const_iterator brushPlane = faceList.begin(); brushPlane != faceList.end(); brushPlane++ )
303         {
304                 if ( **brushPlane == *chkPlane ) {
305                         return *brushPlane;
306                 }
307         }
308         return NULL;
309 }
310
311 bool DBrush::IsCutByPlane( DPlane *cuttingPlane ){
312         bool isInFront;
313
314         if ( pointList.size() == 0 ) {
315                 if ( BuildPoints() == 0 ) {
316                         return FALSE;
317                 }
318         }
319
320         list<DPoint *>::const_iterator chkPnt = pointList.begin();
321
322         if ( chkPnt == pointList.end() ) {
323                 return FALSE;
324         }
325
326         float dist = cuttingPlane->DistanceToPoint( ( *chkPnt )->_pnt );
327
328         if ( dist > MAX_ROUND_ERROR ) {
329                 isInFront = FALSE;
330         }
331         else if ( dist < MAX_ROUND_ERROR ) {
332                 isInFront = TRUE;
333         }
334         else{
335                 return TRUE;
336         }
337
338         for ( chkPnt++ = pointList.begin(); chkPnt != pointList.end(); chkPnt++ )
339         {
340                 dist = cuttingPlane->DistanceToPoint( ( *chkPnt )->_pnt );
341
342                 if ( dist > MAX_ROUND_ERROR ) {
343                         if ( isInFront ) {
344                                 return TRUE;
345                         }
346                 }
347                 else if ( dist < MAX_ROUND_ERROR ) {
348                         if ( !isInFront ) {
349                                 return TRUE;
350                         }
351                 }
352                 else{
353                         return TRUE;
354                 }
355         }
356
357         return FALSE;
358 }
359
360 brush_t* DBrush::BuildInRadiant( bool allowDestruction, int* changeCnt, entity_t* entity ){
361         if ( allowDestruction ) {
362                 bool kill = TRUE;
363
364                 for ( list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
365                 {
366                         if ( ( *chkPlane )->m_bChkOk ) {
367                                 kill = FALSE;
368                                 break;
369                         }
370                 }
371                 if ( kill ) {
372                         return NULL;
373                 }
374         }
375
376         //+djbob: fixed bug when brush had no faces "phantom brush" in radiant.
377         if ( faceList.size() < 4 ) {
378                 Sys_Printf( "Possible Phantom Brush Found, will not rebuild\n" );
379                 return NULL;
380         }
381         //-djbob
382
383         QER_brush = (brush_t*)g_FuncTable.m_pfnCreateBrushHandle();
384
385         for ( list<DPlane *>::const_iterator buildPlane = faceList.begin(); buildPlane != faceList.end(); buildPlane++ ) {
386                 if ( ( *buildPlane )->AddToBrush_t( QER_brush ) && changeCnt ) {
387                         ( *changeCnt )++;
388                 }
389         }
390
391         if ( entity ) {
392                 g_FuncTable.m_pfnCommitBrushHandleToEntity( QER_brush, entity );
393                 g_BrushTable.m_pfnBrush_Build( QER_brush, false, false, false, false );
394                 g_BrushTable.m_pfnBrush_AddToList( QER_brush, g_AppDataTable.m_pfnSelectedBrushes() );
395         }
396         else {
397                 g_FuncTable.m_pfnCommitBrushHandle( QER_brush );
398         }
399
400         return QER_brush;
401 }
402
403 void DBrush::CutByPlane( DPlane *cutPlane, DBrush **newBrush1, DBrush **newBrush2 ){
404         if ( !IsCutByPlane( cutPlane ) ) {
405                 *newBrush1 = NULL;
406                 *newBrush2 = NULL;
407                 return;
408         }
409
410         DBrush* b1 = new DBrush;
411         DBrush* b2 = new DBrush;
412
413         for ( list<DPlane *>::const_iterator parsePlane = faceList.begin(); parsePlane != faceList.end(); parsePlane++ )
414         {
415                 b1->AddFace( ( *parsePlane )->points[0], ( *parsePlane )->points[1], ( *parsePlane )->points[2], NULL );
416                 b2->AddFace( ( *parsePlane )->points[0], ( *parsePlane )->points[1], ( *parsePlane )->points[2], NULL );
417         }
418
419         b1->AddFace( cutPlane->points[0], cutPlane->points[1], cutPlane->points[2], NULL );
420         b2->AddFace( cutPlane->points[2], cutPlane->points[1], cutPlane->points[0], NULL );
421
422         b1->RemoveRedundantPlanes();
423         b2->RemoveRedundantPlanes();
424
425         *newBrush1 = b1;
426         *newBrush2 = b2;
427 }
428
429 bool DBrush::IntersectsWith( DBrush *chkBrush ){
430         if ( pointList.size() == 0 ) {
431                 if ( BuildPoints() == 0 ) {
432                         return FALSE;   // invalid brush!!!!
433
434                 }
435         }
436         if ( chkBrush->pointList.size() == 0 ) {
437                 if ( chkBrush->BuildPoints() == 0 ) {
438                         return FALSE;   // invalid brush!!!!
439
440                 }
441         }
442         if ( !BBoxCollision( chkBrush ) ) {
443                 return FALSE;
444         }
445
446         list<DPlane *>::const_iterator iplPlane;
447
448         for ( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++ )
449         {
450
451                 bool allInFront = TRUE;
452                 for ( list<DPoint *>::const_iterator iPoint = chkBrush->pointList.begin(); iPoint != chkBrush->pointList.end(); iPoint++ )
453                 {
454                         if ( ( *iplPlane )->DistanceToPoint( ( *iPoint )->_pnt ) < -MAX_ROUND_ERROR ) {
455                                 allInFront = FALSE;
456                                 break;
457                         }
458                 }
459                 if ( allInFront ) {
460                         return FALSE;
461                 }
462         }
463
464         for ( iplPlane = chkBrush->faceList.begin(); iplPlane != chkBrush->faceList.end(); iplPlane++ )
465         {
466                 bool allInFront = TRUE;
467                 for ( list<DPoint *>::const_iterator iPoint = pointList.begin(); iPoint != pointList.end(); iPoint++ )
468                 {
469                         if ( ( *iplPlane )->DistanceToPoint( ( *iPoint )->_pnt ) < -MAX_ROUND_ERROR ) {
470                                 allInFront = FALSE;
471                                 break;
472                         }
473                 }
474                 if ( allInFront ) {
475                         return FALSE;
476                 }
477         }
478
479         return TRUE;
480 }
481
482 bool DBrush::IntersectsWith( DPlane* p1, DPlane* p2, vec3_t v ) {
483         vec3_t vDown = { 0, 0, -1 };
484
485         list<DPlane *>::const_iterator iplPlane;
486         for ( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++ ) {
487                 DPlane* p = ( *iplPlane );
488
489                 vec_t d = DotProduct( p->normal, vDown );
490                 if ( d >= 0 ) {
491                         continue;
492                 }
493                 if ( p->PlaneIntersection( p1, p2, v ) ) {
494                         if ( PointPosition( v ) != POINT_OUT_BRUSH ) {
495                                 return TRUE;
496                         }
497                 }
498         }
499
500         return FALSE;
501 }
502
503 void DBrush::BuildBounds(){
504         if ( !bBoundsBuilt ) {
505                 if ( pointList.size() == 0 ) { // if points may not have been built, build them
506                         if ( BuildPoints() == 0 ) {
507                                 return;
508                         }
509                 }
510
511                 list<DPoint *>::const_iterator first = pointList.begin();
512                 VectorCopy( ( *first )->_pnt, bbox_min );
513                 VectorCopy( ( *first )->_pnt, bbox_max );
514
515                 list<DPoint *>::const_iterator point = pointList.begin();
516                 for ( point++; point != pointList.end(); point++ )
517                 {
518                         if ( ( *point )->_pnt[0] > bbox_max[0] ) {
519                                 bbox_max[0] = ( *point )->_pnt[0];
520                         }
521                         if ( ( *point )->_pnt[1] > bbox_max[1] ) {
522                                 bbox_max[1] = ( *point )->_pnt[1];
523                         }
524                         if ( ( *point )->_pnt[2] > bbox_max[2] ) {
525                                 bbox_max[2] = ( *point )->_pnt[2];
526                         }
527
528                         if ( ( *point )->_pnt[0] < bbox_min[0] ) {
529                                 bbox_min[0] = ( *point )->_pnt[0];
530                         }
531                         if ( ( *point )->_pnt[1] < bbox_min[1] ) {
532                                 bbox_min[1] = ( *point )->_pnt[1];
533                         }
534                         if ( ( *point )->_pnt[2] < bbox_min[2] ) {
535                                 bbox_min[2] = ( *point )->_pnt[2];
536                         }
537                 }
538
539                 bBoundsBuilt = TRUE;
540         }
541 }
542
543 bool DBrush::BBoxTouch( DBrush *chkBrush ){
544         vec3_t min1, min2;
545         vec3_t max1, max2;
546
547         GetBounds( min1, max1 );
548         chkBrush->GetBounds( min2, max2 );
549
550         if ( ( min1[0] - max2[0] ) > MAX_ROUND_ERROR ) {
551                 return FALSE;
552         }
553         if ( ( min1[1] - max2[1] ) > MAX_ROUND_ERROR ) {
554                 return FALSE;
555         }
556         if ( ( min1[2] - max2[2] ) > MAX_ROUND_ERROR ) {
557                 return FALSE;
558         }
559
560         if ( ( min2[0] - max1[0] ) > MAX_ROUND_ERROR ) {
561                 return FALSE;
562         }
563         if ( ( min2[1] - max1[1] ) > MAX_ROUND_ERROR ) {
564                 return FALSE;
565         }
566         if ( ( min2[2] - max1[2] ) > MAX_ROUND_ERROR ) {
567                 return FALSE;
568         }
569
570         int cnt = 0;
571
572         if ( ( min2[0] - max1[0] ) == 0 ) {
573                 cnt++;
574         }
575
576         if ( ( min2[1] - max1[1] ) == 0 ) {
577                 cnt++;
578         }
579
580         if ( ( min2[2] - max1[2] ) == 0 ) {
581                 cnt++;
582         }
583
584         if ( ( min1[0] - max2[0] ) == 0 ) {
585                 cnt++;
586         }
587
588         if ( ( min1[1] - max2[1] ) == 0 ) {
589                 cnt++;
590         }
591
592         if ( ( min1[2] - max2[2] ) == 0 ) {
593                 cnt++;
594         }
595
596         if ( cnt > 1 ) {
597                 return FALSE;
598         }
599
600         return TRUE;
601 }
602
603 void DBrush::ResetChecks( list<Str>* exclusionList ){
604         for ( list<DPlane *>::const_iterator resetPlane = faceList.begin(); resetPlane != faceList.end(); resetPlane++ )
605         {
606                 bool set = FALSE;
607
608                 if ( exclusionList ) {
609                         for ( list<Str>::iterator eTexture = exclusionList->begin(); eTexture != exclusionList->end(); eTexture++ )
610                         {
611                                 if ( strstr( ( *resetPlane )->texInfo.m_TextureName, eTexture->GetBuffer() ) ) {
612                                         set = TRUE;
613                                         break;
614                                 }
615                         }
616                 }
617
618                 ( *resetPlane )->m_bChkOk = set;
619         }
620 }
621
622 DPlane* DBrush::HasPlaneInverted( DPlane *chkPlane ){
623         for ( list<DPlane *>::const_iterator brushPlane = faceList.begin(); brushPlane != faceList.end(); brushPlane++ )
624         {
625                 if ( **brushPlane != *chkPlane ) {
626                         if ( fabs( ( *brushPlane )->_d + chkPlane->_d ) < 0.1 ) {
627                                 return ( *brushPlane );
628                         }
629                 }
630         }
631         return NULL;
632 }
633
634 bool DBrush::HasTexture( const char *textureName ){
635         for ( list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
636         {
637                 if ( strstr( ( *chkPlane )->texInfo.m_TextureName, textureName ) ) {
638                         return TRUE;
639                 }
640
641         }
642         return FALSE;
643 }
644
645 bool DBrush::IsDetail(){
646         for ( list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
647         {
648                 if ( ( *chkPlane )->texInfo.m_nContents & FACE_DETAIL ) {
649                         return TRUE;
650                 }
651
652         }
653         return FALSE;
654 }
655
656 void DBrush::BuildFromWinding( DWinding *w ){
657         if ( w->numpoints < 3 ) {
658                 Sys_ERROR( "Winding has invalid number of points" );
659                 return;
660         }
661
662         DPlane* wPlane = w->WindingPlane();
663
664         DWinding* w2;
665         w2 = w->CopyWinding();
666         int i;
667         for ( i = 0; i < w2->numpoints; i++ )
668                 VectorAdd( w2->p[i], wPlane->normal, w2->p[i] );
669
670         AddFace( w2->p[0], w2->p[1], w2->p[2], NULL );
671         AddFace( w->p[2], w->p[1], w->p[0], NULL );
672
673         for ( i = 0; i < w->numpoints - 1; i++ )
674                 AddFace( w2->p[i], w->p[i], w->p[i + 1], NULL );
675         AddFace( w2->p[w->numpoints - 1], w->p[w->numpoints - 1], w->p[0], NULL );
676
677         delete wPlane;
678         delete w2;
679 }
680
681 void DBrush::SaveToFile( FILE *pFile ){
682         fprintf( pFile, "{\n" );
683
684         for ( list<DPlane *>::const_iterator pp = faceList.begin(); pp != faceList.end(); pp++ )
685         {
686                 char buffer[512];
687
688                 sprintf( buffer, "( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) %s %.0f %.0f %f %f %.0f 0 0 0\n",
689                                  ( *pp )->points[0][0], ( *pp )->points[0][1], ( *pp )->points[0][2],
690                                  ( *pp )->points[1][0], ( *pp )->points[1][1], ( *pp )->points[1][2],
691                                  ( *pp )->points[2][0], ( *pp )->points[2][1], ( *pp )->points[2][2],
692                                  ( *pp )->texInfo.m_TextureName,
693                                  ( *pp )->texInfo.m_fShift[0], ( *pp )->texInfo.m_fShift[1],
694                                  ( *pp )->texInfo.m_fScale[0], ( *pp )->texInfo.m_fScale[0],
695                                  ( *pp )->texInfo.m_fRotate );
696
697                 fprintf( pFile, "%s", buffer );
698         }
699
700         fprintf( pFile, "}\n" );
701 }
702
703 void DBrush::Rotate( vec3_t vOrigin, vec3_t vRotation ){
704         for ( list<DPlane *>::const_iterator rotPlane = faceList.begin(); rotPlane != faceList.end(); rotPlane++ )
705         {
706                 for ( int i = 0; i < 3; i++ )
707                         VectorRotate( ( *rotPlane )->points[i], vRotation, vOrigin );
708
709                 ( *rotPlane )->Rebuild();
710         }
711 }
712
713 void DBrush::RotateAboutCentre( vec3_t vRotation ){
714         vec3_t min, max, centre;
715         GetBounds( min, max );
716         VectorAdd( min, max, centre );
717         VectorScale( centre, 0.5f, centre );
718
719         Rotate( centre, vRotation );
720 }
721
722 bool DBrush::ResetTextures( const char* textureName, float fScale[2],    float fShift[2],    int rotation, const char* newTextureName,
723                                                         int bResetTextureName,   int bResetScale[2], int bResetShift[2], int bResetRotation ){
724         if ( textureName ) {
725                 bool changed = FALSE;
726                 for ( list<DPlane *>::const_iterator resetPlane = faceList.begin(); resetPlane != faceList.end(); resetPlane++ )
727                 {
728                         if ( !strcmp( ( *resetPlane )->texInfo.m_TextureName, textureName ) ) {
729                                 if ( bResetTextureName ) {
730                                         strcpy( ( *resetPlane )->texInfo.m_TextureName, newTextureName );
731                                 }
732
733                                 if ( bResetScale[0] ) {
734                                         ( *resetPlane )->texInfo.m_fScale[0] = fScale[0];
735                                 }
736                                 if ( bResetScale[1] ) {
737                                         ( *resetPlane )->texInfo.m_fScale[1] = fScale[1];
738                                 }
739
740                                 if ( bResetShift[0] ) {
741                                         ( *resetPlane )->texInfo.m_fShift[0] = fShift[0];
742                                 }
743                                 if ( bResetShift[1] ) {
744                                         ( *resetPlane )->texInfo.m_fShift[1] = fShift[1];
745                                 }
746
747                                 if ( bResetRotation ) {
748                                         ( *resetPlane )->texInfo.m_fRotate = (float)rotation;
749                                 }
750
751                                 changed = TRUE;
752                         }
753                 }
754                 return changed; // no point rebuilding unless we need to, only slows things down
755         }
756         else
757         {
758                 for ( list<DPlane *>::const_iterator resetPlane = faceList.begin(); resetPlane != faceList.end(); resetPlane++ )
759                 {
760                         if ( bResetTextureName ) {
761                                 strcpy( ( *resetPlane )->texInfo.m_TextureName, newTextureName );
762                         }
763
764                         if ( bResetScale[0] ) {
765                                 ( *resetPlane )->texInfo.m_fScale[0] = fScale[0];
766                         }
767                         if ( bResetScale[1] ) {
768                                 ( *resetPlane )->texInfo.m_fScale[1] = fScale[1];
769                         }
770
771                         if ( bResetShift[0] ) {
772                                 ( *resetPlane )->texInfo.m_fShift[0] = fShift[0];
773                         }
774                         if ( bResetShift[1] ) {
775                                 ( *resetPlane )->texInfo.m_fShift[1] = fShift[1];
776                         }
777
778                         if ( bResetRotation ) {
779                                 ( *resetPlane )->texInfo.m_fRotate = (float)rotation;
780                         }
781                 }
782                 return TRUE;
783         }
784 }
785
786 bool DBrush::operator ==( DBrush* other ){
787         list<DPlane *>::const_iterator chkPlane;
788
789         for ( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
790         {
791                 if ( !other->HasPlane( ( *chkPlane ) ) ) {
792                         return FALSE;
793                 }
794         }
795
796         for ( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
797         {
798                 if ( !HasPlane( ( *chkPlane ) ) ) {
799                         return FALSE;
800                 }
801         }
802
803         return TRUE;
804 }
805
806 DPlane* DBrush::AddFace( vec3_t va, vec3_t vb, vec3_t vc, const char *textureName, bool bDetail ){
807         bBoundsBuilt = FALSE;
808         DPlane* newFace = new DPlane( va, vb, vc, textureName, bDetail );
809         faceList.push_back( newFace );
810
811         return newFace;
812 }
813
814 DPlane* DBrush::FindPlaneWithClosestNormal( vec_t* normal ) {
815         vec_t bestDot = -2;
816         DPlane* bestDotPlane = NULL;
817         list<DPlane *>::const_iterator chkPlane;
818         for ( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ ) {
819                 DPlane* pPlane = ( *chkPlane );
820
821                 vec_t dot = DotProduct( pPlane->normal, normal );
822                 if ( dot > bestDot ) {
823                         bestDot = dot;
824                         bestDotPlane = pPlane;
825                 }
826         }
827
828         return bestDotPlane;
829 }
830
831 int DBrush::FindPointsForPlane( DPlane* plane, DPoint** pnts, int maxpnts ) {
832         int numpnts = 0;
833
834         if ( !maxpnts ) {
835                 return 0;
836         }
837
838         BuildPoints();
839
840         for ( list<DPoint *>::const_iterator points = pointList.begin(); points != pointList.end(); points++ ) {
841                 DPoint* point = ( *points );
842
843                 if ( fabs( plane->DistanceToPoint( point->_pnt ) ) < MAX_ROUND_ERROR ) {
844                         pnts[numpnts] = point;
845                         numpnts++;
846
847                         if ( numpnts >= maxpnts ) {
848                                 return numpnts;
849                         }
850
851                 }
852         }
853
854         return numpnts;
855 }
856
857 void DBrush::RemovePlane( DPlane* plane ) {
858         bBoundsBuilt = FALSE;
859         for ( list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++ ) {
860                 if ( *deadPlane == plane ) {
861                         delete *deadPlane;
862                         faceList.remove( plane );
863                 }
864         }
865 }
866
867 void DBrush::RemoveFromRadiant( void ) {
868         if ( QER_brush ) {
869                 g_FuncTable.m_pfnDeleteBrushHandle( QER_brush );
870         }
871 }