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