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