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