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