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