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