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