-/*\r
-BobToolz plugin for GtkRadiant\r
-Copyright (C) 2001 Gordon Biggans\r
-\r
-This library is free software; you can redistribute it and/or\r
-modify it under the terms of the GNU Lesser General Public\r
-License as published by the Free Software Foundation; either\r
-version 2.1 of the License, or (at your option) any later version.\r
-\r
-This library is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
-Lesser General Public License for more details.\r
-\r
-You should have received a copy of the GNU Lesser General Public\r
-License along with this library; if not, write to the Free Software\r
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
-*/\r
-\r
-// DBrush.cpp: implementation of the DBrush class.\r
-//\r
-//////////////////////////////////////////////////////////////////////\r
-\r
-#include "StdAfx.h"\r
-\r
-#ifdef _WIN32\r
-#pragma warning(disable : 4786)\r
-#endif\r
-\r
-#include "DBrush.h"\r
-#include "DWinding.h"\r
-#include "dialogs-gtk.h"\r
-\r
-#include "misc.h"\r
-\r
-//////////////////////////////////////////////////////////////////////\r
-// Construction/Destruction\r
-//////////////////////////////////////////////////////////////////////\r
-\r
-DBrush::DBrush(int ID)\r
-{\r
- m_nBrushID = ID;\r
- bBoundsBuilt = FALSE;\r
- QER_brush = NULL;\r
-}\r
-\r
-DBrush::~DBrush()\r
-{\r
- ClearFaces();\r
- ClearPoints();\r
-}\r
-\r
-//////////////////////////////////////////////////////////////////////\r
-// Implementation\r
-//////////////////////////////////////////////////////////////////////\r
-\r
-DPlane* DBrush::AddFace(vec3_t va, vec3_t vb, vec3_t vc, _QERFaceData* texData)\r
-{\r
-#ifdef _DEBUG\r
-// 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
-#endif\r
- bBoundsBuilt = FALSE;\r
- DPlane* newFace = new DPlane(va, vb, vc, texData);\r
- faceList.push_back(newFace);\r
- \r
- return newFace;\r
-}\r
-\r
-int DBrush::BuildPoints()\r
-{\r
- ClearPoints();\r
- \r
- if(faceList.size() <= 3) // if less than 3 faces, there can be no points\r
- return 0; // with only 3 faces u can't have a bounded soild\r
-\r
- for(list<DPlane *>::const_iterator p1=faceList.begin(); p1!=faceList.end(); p1++)\r
- {\r
- list<DPlane *>::const_iterator p2=p1;\r
- for(p2++; p2!=faceList.end(); p2++)\r
- {\r
- list<DPlane *>::const_iterator p3=p2;\r
- for(p3++; p3!=faceList.end(); p3++)\r
- {\r
- vec3_t pnt;\r
- if((*p1)->PlaneIntersection(*p2, *p3, pnt))\r
- {\r
- int pos = PointPosition(pnt);\r
-\r
- if(pos == POINT_IN_BRUSH)\r
- { // ???? shouldn't happen here\r
- Sys_Printf("ERROR:: Build Brush Points: Point IN brush!!!\n");\r
- }\r
- else if(pos == POINT_ON_BRUSH)\r
- { // normal point\r
- if(!HasPoint(pnt))\r
- AddPoint(pnt);\r
-/* else\r
- Sys_Printf("Duplicate Point Found, pyramids ahoy!!!!!\n");*/\r
- // point lies on more that 3 planes\r
- }\r
- \r
- // otherwise point is removed due to another plane..\r
-\r
- // Sys_Printf("(%f, %f, %f)\n", pnt[0], pnt[1], pnt[2]); \r
- } \r
- }\r
- }\r
- }\r
-\r
-#ifdef _DEBUG\r
-// Sys_Printf("%i points on brush\n", pointList.size());\r
-#endif\r
-\r
- return pointList.size();\r
-}\r
-\r
-void DBrush::LoadFromBrush_t(brush_t* brush, bool textured)\r
-{\r
- ClearFaces();\r
- ClearPoints();\r
-\r
- for(int i = g_FuncTable.m_pfnGetFaceCount(brush)-1; i >= 0 ; i--)\r
- { // running backwards so i dont have to use the count function each time (OPT)\r
- _QERFaceData* faceData = g_FuncTable.m_pfnGetFaceData(brush, i);\r
-\r
- if(faceData == NULL)\r
- DoMessageBox("Null pointer returned", "WARNING!", MB_OK);\r
-\r
- if(textured)\r
- AddFace(faceData->m_v1, faceData->m_v2, faceData->m_v3, faceData);\r
- else\r
- AddFace(faceData->m_v1, faceData->m_v2, faceData->m_v3, NULL);\r
- }\r
-\r
- QER_brush = brush;\r
-}\r
-\r
-int DBrush::PointPosition(vec3_t pnt)\r
-{\r
- int state = POINT_IN_BRUSH; // if nothing happens point is inside brush\r
-\r
- for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)\r
- {\r
- float dist = (*chkPlane)->DistanceToPoint(pnt);\r
-\r
- if(dist > MAX_ROUND_ERROR)\r
- return POINT_OUT_BRUSH; // if point is in front of plane, it CANT be in the brush\r
- else if(fabs(dist) < MAX_ROUND_ERROR)\r
- state = POINT_ON_BRUSH; // if point is ON plane point is either ON the brush \r
- // or outside it, it can no longer be in it\r
- }\r
-\r
- return state;\r
-}\r
-\r
-void DBrush::ClearPoints()\r
-{\r
- for(list<DPoint *>::const_iterator deadPoint=pointList.begin(); deadPoint!=pointList.end(); deadPoint++) {\r
- delete *deadPoint;\r
- }\r
- pointList.clear();\r
-}\r
-\r
-void DBrush::ClearFaces()\r
-{\r
- bBoundsBuilt = FALSE;\r
- for(list<DPlane *>::const_iterator deadPlane=faceList.begin(); deadPlane!=faceList.end(); deadPlane++)\r
- {\r
- delete *deadPlane;\r
- }\r
- faceList.clear();\r
-}\r
-\r
-void DBrush::AddPoint(vec3_t pnt)\r
-{\r
- DPoint* newPoint = new DPoint;\r
- VectorCopy(pnt, newPoint->_pnt);\r
- pointList.push_back(newPoint);\r
-}\r
-\r
-bool DBrush::HasPoint(vec3_t pnt)\r
-{\r
- for(list<DPoint *>::const_iterator chkPoint=pointList.begin(); chkPoint!=pointList.end(); chkPoint++)\r
- {\r
- if(**chkPoint == pnt)\r
- return TRUE;\r
- }\r
-\r
- return FALSE;\r
-}\r
-\r
-int DBrush::RemoveRedundantPlanes()\r
-{\r
- int cnt = 0;\r
- list<DPlane *>::iterator chkPlane;\r
-\r
- // find duplicate planes\r
- list<DPlane *>::iterator p1=faceList.begin();\r
-\r
- while( p1!=faceList.end() )\r
- {\r
- list<DPlane *>::iterator p2 = p1;\r
-\r
- for(p2++; p2!=faceList.end(); p2++)\r
- {\r
- if(**p1 == **p2)\r
- {\r
- if(!strcmp((*p1)->texInfo.m_TextureName, "textures/common/caulk"))\r
- {\r
- delete *p1;\r
- p1 = faceList.erase(p1); // duplicate plane\r
- }\r
- else\r
- {\r
- delete *p2;\r
- p2 = faceList.erase(p2); // duplicate plane\r
- }\r
-\r
- cnt++;\r
- break;\r
- }\r
- }\r
-\r
- if( p2 == faceList.end() )\r
- p1++;\r
- }\r
- \r
- //+djbob kill planes with bad normal, they are more of a nuisance than losing a brush\r
- chkPlane=faceList.begin();\r
- while( chkPlane!=faceList.end() )\r
- {\r
- if(VectorLength((*chkPlane)->normal) == 0) // plane has bad normal\r
- {\r
- delete *chkPlane;\r
- chkPlane = faceList.erase(chkPlane);\r
- cnt++;\r
- } else {\r
- chkPlane++;\r
- }\r
- }\r
- //-djbob\r
- \r
- if(pointList.size() == 0) // if points may not have been built, build them\r
-/* if(BuildPoints() == 0) // just let the planes die if they are all bad\r
- return cnt;*/\r
- BuildPoints();\r
-\r
- chkPlane=faceList.begin();\r
- while(chkPlane != faceList.end())\r
- {\r
- if((*chkPlane)->IsRedundant(pointList)) // checks that plane "0wnz" :), 3 or more points\r
- {\r
- delete *chkPlane;\r
- chkPlane = faceList.erase(chkPlane);\r
- cnt++;\r
- } \r
- else \r
- chkPlane++;\r
- }\r
-\r
- return cnt;\r
-}\r
-\r
-bool DBrush::GetBounds(vec3_t min, vec3_t max)\r
-{\r
- BuildBounds();\r
-\r
- if(!bBoundsBuilt)\r
- return FALSE;\r
-\r
- VectorCopy(bbox_min, min);\r
- VectorCopy(bbox_max, max);\r
-\r
- return TRUE;\r
-}\r
-\r
-bool DBrush::BBoxCollision(DBrush* chkBrush)\r
-{\r
- vec3_t min1, min2;\r
- vec3_t max1, max2;\r
-\r
- GetBounds(min1, max1);\r
- chkBrush->GetBounds(min2, max2);\r
-\r
- if(min1[0] >= max2[0])\r
- return FALSE;\r
- if(min1[1] >= max2[1])\r
- return FALSE;\r
- if(min1[2] >= max2[2])\r
- return FALSE;\r
-\r
- if(max1[0] <= min2[0])\r
- return FALSE;\r
- if(max1[1] <= min2[1])\r
- return FALSE;\r
- if(max1[2] <= min2[2])\r
- return FALSE;\r
-\r
- return TRUE;\r
-}\r
-\r
-DPlane* DBrush::HasPlane(DPlane* chkPlane)\r
-{\r
- for(list<DPlane *>::const_iterator brushPlane=faceList.begin(); brushPlane!=faceList.end(); brushPlane++)\r
- {\r
- if(**brushPlane == *chkPlane)\r
- return *brushPlane;\r
- }\r
- return NULL;\r
-}\r
-\r
-bool DBrush::IsCutByPlane(DPlane *cuttingPlane)\r
-{\r
- bool isInFront;\r
-\r
- if(pointList.size() == 0)\r
- if(BuildPoints() == 0)\r
- return FALSE;\r
-\r
- list<DPoint *>::const_iterator chkPnt = pointList.begin();\r
-\r
- if(chkPnt == pointList.end())\r
- return FALSE;\r
-\r
- float dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);\r
-\r
- if(dist > MAX_ROUND_ERROR)\r
- isInFront = FALSE;\r
- else if(dist < MAX_ROUND_ERROR)\r
- isInFront = TRUE;\r
- else\r
- return TRUE;\r
-\r
- for(chkPnt++=pointList.begin(); chkPnt!=pointList.end(); chkPnt++)\r
- {\r
- dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);\r
-\r
- if(dist > MAX_ROUND_ERROR)\r
- {\r
- if(isInFront)\r
- return TRUE;\r
- }\r
- else if(dist < MAX_ROUND_ERROR)\r
- {\r
- if(!isInFront)\r
- return TRUE;\r
- }\r
- else\r
- return TRUE;\r
- }\r
-\r
- return FALSE;\r
-}\r
-\r
-brush_t* DBrush::BuildInRadiant(bool allowDestruction, int* changeCnt, entity_t* entity)\r
-{\r
- if(allowDestruction)\r
- {\r
- bool kill = TRUE;\r
- \r
- for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)\r
- {\r
- if((*chkPlane)->m_bChkOk)\r
- {\r
- kill = FALSE;\r
- break;\r
- }\r
- }\r
- if(kill)\r
- return NULL;\r
- }\r
-\r
- //+djbob: fixed bug when brush had no faces "phantom brush" in radiant.\r
- if(faceList.size() < 4)\r
- {\r
- Sys_Printf("Possible Phantom Brush Found, will not rebuild\n");\r
- return NULL;\r
- }\r
- //-djbob\r
-\r
- QER_brush = (brush_t*)g_FuncTable.m_pfnCreateBrushHandle();\r
-\r
- for(list<DPlane *>::const_iterator buildPlane=faceList.begin(); buildPlane!=faceList.end(); buildPlane++) {\r
- if((*buildPlane)->AddToBrush_t(QER_brush) && changeCnt) {\r
- (*changeCnt)++;\r
- }\r
- }\r
-\r
- if(entity) {\r
- g_FuncTable.m_pfnCommitBrushHandleToEntity(QER_brush, entity);\r
- g_BrushTable.m_pfnBrush_Build(QER_brush);\r
- g_BrushTable.m_pfnBrush_AddToList(QER_brush, g_AppDataTable.m_pfnSelectedBrushes());\r
- } else {\r
- g_FuncTable.m_pfnCommitBrushHandle(QER_brush);\r
- }\r
-\r
- return QER_brush;\r
-}\r
-\r
-void DBrush::CutByPlane(DPlane *cutPlane, DBrush **newBrush1, DBrush **newBrush2)\r
-{\r
- if(!IsCutByPlane(cutPlane))\r
- {\r
- *newBrush1 = NULL;\r
- *newBrush2 = NULL;\r
- return;\r
- }\r
-\r
- DBrush* b1 = new DBrush;\r
- DBrush* b2 = new DBrush;\r
- \r
- for(list<DPlane *>::const_iterator parsePlane=faceList.begin(); parsePlane!=faceList.end(); parsePlane++)\r
- {\r
- b1->AddFace((*parsePlane)->points[0], (*parsePlane)->points[1], (*parsePlane)->points[2], NULL);\r
- b2->AddFace((*parsePlane)->points[0], (*parsePlane)->points[1], (*parsePlane)->points[2], NULL);\r
- }\r
-\r
- b1->AddFace(cutPlane->points[0], cutPlane->points[1], cutPlane->points[2], NULL);\r
- b2->AddFace(cutPlane->points[2], cutPlane->points[1], cutPlane->points[0], NULL);\r
-\r
- b1->RemoveRedundantPlanes();\r
- b2->RemoveRedundantPlanes();\r
-\r
- *newBrush1 = b1;\r
- *newBrush2 = b2;\r
-}\r
-\r
-bool DBrush::IntersectsWith(DBrush *chkBrush)\r
-{\r
- if(pointList.size() == 0)\r
- if(BuildPoints() == 0)\r
- return FALSE; // invalid brush!!!!\r
-\r
- if(chkBrush->pointList.size() == 0)\r
- if(chkBrush->BuildPoints() == 0)\r
- return FALSE; // invalid brush!!!!\r
- \r
- if(!BBoxCollision(chkBrush))\r
- return FALSE;\r
-\r
- list<DPlane *>::const_iterator iplPlane;\r
-\r
- for( iplPlane=faceList.begin(); iplPlane!=faceList.end(); iplPlane++)\r
- {\r
-\r
- bool allInFront = TRUE;\r
- for(list<DPoint *>::const_iterator iPoint=chkBrush->pointList.begin(); iPoint!=chkBrush->pointList.end(); iPoint++)\r
- {\r
- if((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR)\r
- {\r
- allInFront = FALSE;\r
- break;\r
- }\r
- }\r
- if(allInFront)\r
- return FALSE;\r
- }\r
-\r
- for( iplPlane=chkBrush->faceList.begin(); iplPlane!=chkBrush->faceList.end(); iplPlane++)\r
- {\r
- bool allInFront = TRUE;\r
- for(list<DPoint *>::const_iterator iPoint=pointList.begin(); iPoint!=pointList.end(); iPoint++)\r
- {\r
- if((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR)\r
- {\r
- allInFront = FALSE;\r
- break;\r
- }\r
- }\r
- if(allInFront)\r
- return FALSE;\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-bool DBrush::IntersectsWith(DPlane* p1, DPlane* p2, vec3_t v) {\r
- vec3_t vDown = { 0, 0, -1 };\r
-\r
- list<DPlane *>::const_iterator iplPlane;\r
- for( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++) {\r
- DPlane* p = (*iplPlane);\r
- \r
- vec_t d = DotProduct( p->normal, vDown );\r
- if( d >= 0 ) {\r
- continue;\r
- }\r
- if(p->PlaneIntersection(p1, p2, v)) {\r
- if(PointPosition( v ) != POINT_OUT_BRUSH) {\r
- return TRUE;\r
- }\r
- }\r
- }\r
-\r
- return FALSE;\r
-}\r
-\r
-void DBrush::BuildBounds()\r
-{\r
- if(!bBoundsBuilt)\r
- {\r
- if(pointList.size() == 0) // if points may not have been built, build them\r
- if(BuildPoints() == 0)\r
- return;\r
- \r
- list<DPoint *>::const_iterator first = pointList.begin();\r
- VectorCopy((*first)->_pnt, bbox_min);\r
- VectorCopy((*first)->_pnt, bbox_max);\r
-\r
- list<DPoint *>::const_iterator point=pointList.begin();\r
- for( point++; point!=pointList.end(); point++)\r
- {\r
- if((*point)->_pnt[0] > bbox_max[0])\r
- bbox_max[0] = (*point)->_pnt[0];\r
- if((*point)->_pnt[1] > bbox_max[1])\r
- bbox_max[1] = (*point)->_pnt[1];\r
- if((*point)->_pnt[2] > bbox_max[2])\r
- bbox_max[2] = (*point)->_pnt[2];\r
-\r
- if((*point)->_pnt[0] < bbox_min[0])\r
- bbox_min[0] = (*point)->_pnt[0];\r
- if((*point)->_pnt[1] < bbox_min[1])\r
- bbox_min[1] = (*point)->_pnt[1];\r
- if((*point)->_pnt[2] < bbox_min[2])\r
- bbox_min[2] = (*point)->_pnt[2];\r
- }\r
-\r
- bBoundsBuilt = TRUE;\r
- }\r
-}\r
-\r
-bool DBrush::BBoxTouch(DBrush *chkBrush)\r
-{\r
- vec3_t min1, min2;\r
- vec3_t max1, max2;\r
-\r
- GetBounds(min1, max1);\r
- chkBrush->GetBounds(min2, max2);\r
-\r
- if((min1[0] - max2[0]) > MAX_ROUND_ERROR)\r
- return FALSE;\r
- if((min1[1] - max2[1]) > MAX_ROUND_ERROR)\r
- return FALSE;\r
- if((min1[2] - max2[2]) > MAX_ROUND_ERROR)\r
- return FALSE;\r
-\r
- if((min2[0] - max1[0]) > MAX_ROUND_ERROR)\r
- return FALSE;\r
- if((min2[1] - max1[1]) > MAX_ROUND_ERROR)\r
- return FALSE;\r
- if((min2[2] - max1[2]) > MAX_ROUND_ERROR)\r
- return FALSE;\r
-\r
- int cnt = 0;\r
-\r
- if((min2[0] - max1[0]) == 0)\r
- cnt++;\r
-\r
- if((min2[1] - max1[1]) == 0)\r
- cnt++;\r
-\r
- if((min2[2] - max1[2]) == 0)\r
- cnt++;\r
-\r
- if((min1[0] - max2[0]) == 0)\r
- cnt++;\r
-\r
- if((min1[1] - max2[1]) == 0)\r
- cnt++;\r
-\r
- if((min1[2] - max2[2]) == 0)\r
- cnt++;\r
-\r
- if(cnt > 1)\r
- return FALSE;\r
-\r
- return TRUE;\r
-}\r
-\r
-void DBrush::ResetChecks(list<Str>* exclusionList)\r
-{\r
- for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)\r
- {\r
- bool set = FALSE;\r
-\r
- if(exclusionList)\r
- {\r
- for(list<Str>::iterator eTexture = exclusionList->begin(); eTexture != exclusionList->end(); eTexture++)\r
- {\r
- if(strstr((*resetPlane)->texInfo.m_TextureName, eTexture->GetBuffer()))\r
- {\r
- set = TRUE;\r
- break;\r
- }\r
- }\r
- }\r
-\r
- (*resetPlane)->m_bChkOk = set;\r
- }\r
-}\r
-\r
-DPlane* DBrush::HasPlaneInverted(DPlane *chkPlane)\r
-{\r
- for(list<DPlane *>::const_iterator brushPlane=faceList.begin(); brushPlane!=faceList.end(); brushPlane++)\r
- {\r
- if(**brushPlane != *chkPlane)\r
- {\r
- if(fabs((*brushPlane)->_d + chkPlane->_d) < 0.1)\r
- return (*brushPlane);\r
- }\r
- }\r
- return NULL;\r
-}\r
-\r
-bool DBrush::HasTexture(const char *textureName)\r
-{\r
- for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)\r
- {\r
- if(strstr((*chkPlane)->texInfo.m_TextureName, textureName))\r
- return TRUE;\r
-\r
- }\r
- return FALSE;\r
-}\r
-\r
-bool DBrush::IsDetail()\r
-{\r
- for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)\r
- {\r
- if((*chkPlane)->texInfo.m_nContents & FACE_DETAIL)\r
- return TRUE;\r
-\r
- }\r
- return FALSE;\r
-}\r
-\r
-void DBrush::BuildFromWinding(DWinding *w)\r
-{\r
- if(w->numpoints < 3)\r
- {\r
- Sys_ERROR("Winding has invalid number of points");\r
- return;\r
- }\r
-\r
- DPlane* wPlane = w->WindingPlane();\r
-\r
- DWinding* w2;\r
- w2 = w->CopyWinding();\r
- int i;\r
- for(i = 0; i < w2->numpoints; i++)\r
- VectorAdd(w2->p[i], wPlane->normal, w2->p[i]);\r
-\r
- AddFace(w2->p[0], w2->p[1], w2->p[2], NULL);\r
- AddFace(w->p[2], w->p[1], w->p[0], NULL);\r
-\r
- for(i = 0; i < w->numpoints-1; i++)\r
- AddFace(w2->p[i], w->p[i], w->p[i+1], NULL);\r
- AddFace(w2->p[w->numpoints-1], w->p[w->numpoints-1], w->p[0], NULL);\r
-\r
- delete wPlane;\r
- delete w2;\r
-}\r
-\r
-void DBrush::SaveToFile(FILE *pFile)\r
-{\r
- fprintf(pFile, "{\n");\r
-\r
- for(list<DPlane *>::const_iterator pp=faceList.begin(); pp!=faceList.end(); pp++)\r
- {\r
- char buffer[512];\r
-\r
- sprintf(buffer, "( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) %s %.0f %.0f %f %f %.0f 0 0 0\n",\r
- (*pp)->points[0][0], (*pp)->points[0][1], (*pp)->points[0][2], \r
- (*pp)->points[1][0], (*pp)->points[1][1], (*pp)->points[1][2], \r
- (*pp)->points[2][0], (*pp)->points[2][1], (*pp)->points[2][2], \r
- (*pp)->texInfo.m_TextureName,\r
- (*pp)->texInfo.m_fShift[0], (*pp)->texInfo.m_fShift[1], \r
- (*pp)->texInfo.m_fScale[0], (*pp)->texInfo.m_fScale[0], \r
- (*pp)->texInfo.m_fRotate);\r
-\r
- fprintf(pFile, buffer);\r
- }\r
-\r
- fprintf(pFile, "}\n");\r
-}\r
-\r
-void DBrush::Rotate(vec3_t vOrigin, vec3_t vRotation)\r
-{\r
- for(list<DPlane *>::const_iterator rotPlane=faceList.begin(); rotPlane!=faceList.end(); rotPlane++)\r
- {\r
- for(int i = 0; i < 3; i++)\r
- VectorRotate((*rotPlane)->points[i], vRotation, vOrigin);\r
-\r
- (*rotPlane)->Rebuild();\r
- }\r
-}\r
-\r
-void DBrush::RotateAboutCentre(vec3_t vRotation)\r
-{\r
- vec3_t min, max, centre;\r
- GetBounds(min, max);\r
- VectorAdd(min, max, centre);\r
- VectorScale(centre, 0.5f, centre);\r
-\r
- Rotate(centre, vRotation);\r
-}\r
-\r
-bool DBrush::ResetTextures(const char* textureName, float fScale[2], float fShift[2], int rotation, const char* newTextureName, \r
- int bResetTextureName, int bResetScale[2], int bResetShift[2], int bResetRotation)\r
-{\r
- if(textureName)\r
- {\r
- bool changed = FALSE;\r
- for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)\r
- {\r
- if(!strcmp((*resetPlane)->texInfo.m_TextureName, textureName))\r
- {\r
- if(bResetTextureName)\r
- strcpy((*resetPlane)->texInfo.m_TextureName, newTextureName);\r
-\r
- if(bResetScale[0])\r
- (*resetPlane)->texInfo.m_fScale[0] = fScale[0];\r
- if(bResetScale[1])\r
- (*resetPlane)->texInfo.m_fScale[1] = fScale[1];\r
-\r
- if(bResetShift[0])\r
- (*resetPlane)->texInfo.m_fShift[0] = fShift[0];\r
- if(bResetShift[1])\r
- (*resetPlane)->texInfo.m_fShift[1] = fShift[1];\r
-\r
- if(bResetRotation)\r
- (*resetPlane)->texInfo.m_fRotate = (float)rotation;\r
-\r
- changed = TRUE;\r
- }\r
- }\r
- return changed; // no point rebuilding unless we need to, only slows things down\r
- }\r
- else\r
- {\r
- for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)\r
- {\r
- if(bResetTextureName)\r
- strcpy((*resetPlane)->texInfo.m_TextureName, newTextureName);\r
-\r
- if(bResetScale[0])\r
- (*resetPlane)->texInfo.m_fScale[0] = fScale[0];\r
- if(bResetScale[1])\r
- (*resetPlane)->texInfo.m_fScale[1] = fScale[1];\r
-\r
- if(bResetShift[0])\r
- (*resetPlane)->texInfo.m_fShift[0] = fShift[0];\r
- if(bResetShift[1])\r
- (*resetPlane)->texInfo.m_fShift[1] = fShift[1];\r
-\r
- if(bResetRotation)\r
- (*resetPlane)->texInfo.m_fRotate = (float)rotation;\r
- }\r
- return TRUE;\r
- }\r
-}\r
-\r
-bool DBrush::operator ==(DBrush* other)\r
-{\r
- list<DPlane *>::const_iterator chkPlane;\r
- \r
- for(chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)\r
- {\r
- if(!other->HasPlane((*chkPlane)))\r
- return FALSE;\r
- }\r
-\r
- for(chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)\r
- {\r
- if(!HasPlane((*chkPlane)))\r
- return FALSE;\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-DPlane* DBrush::AddFace(vec3_t va, vec3_t vb, vec3_t vc, const char *textureName, bool bDetail)\r
-{\r
- bBoundsBuilt = FALSE;\r
- DPlane* newFace = new DPlane(va, vb, vc, textureName, bDetail);\r
- faceList.push_back(newFace);\r
- \r
- return newFace;\r
-}\r
-\r
-DPlane* DBrush::FindPlaneWithClosestNormal( vec_t* normal ) {\r
- vec_t bestDot = -2;\r
- DPlane* bestDotPlane = NULL;\r
- list<DPlane *>::const_iterator chkPlane;\r
- for( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ ) {\r
- DPlane* pPlane = (*chkPlane);\r
-\r
- vec_t dot = DotProduct( pPlane->normal, normal );\r
- if( dot > bestDot ) {\r
- bestDot = dot;\r
- bestDotPlane = pPlane;\r
- }\r
- }\r
-\r
- return bestDotPlane;\r
-}\r
-\r
-int DBrush::FindPointsForPlane( DPlane* plane, DPoint** pnts, int maxpnts ) {\r
- int numpnts = 0;\r
-\r
- if(!maxpnts) {\r
- return 0;\r
- }\r
-\r
- BuildPoints();\r
-\r
- for( list<DPoint *>::const_iterator points = pointList.begin(); points != pointList.end(); points++ ) {\r
- DPoint* point = (*points);\r
-\r
- if( fabs(plane->DistanceToPoint( point->_pnt )) < MAX_ROUND_ERROR ) {\r
- pnts[numpnts] = point;\r
- numpnts++;\r
-\r
- if(numpnts >= maxpnts) {\r
- return numpnts;\r
- }\r
-\r
- }\r
- }\r
-\r
- return numpnts;\r
-}\r
-\r
-void DBrush::RemovePlane( DPlane* plane ) {\r
- bBoundsBuilt = FALSE;\r
- for( list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++ ) { \r
- if(*deadPlane == plane) {\r
- delete *deadPlane;\r
- faceList.remove( plane );\r
- }\r
- }\r
-}\r
-\r
-void DBrush::RemoveFromRadiant( void ) {\r
- if(QER_brush) {\r
- g_FuncTable.m_pfnDeleteBrushHandle(QER_brush);\r
- }\r
-}\r
+/*
+ BobToolz plugin for GtkRadiant
+ Copyright (C) 2001 Gordon Biggans
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// DBrush.cpp: implementation of the DBrush class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "DBrush.h"
+#include "globaldefs.h"
+
+#if GDEF_COMPILER_MSVC
+#pragma warning(disable : 4786)
+#endif
+
+#include <list>
+#include "str.h"
+
+#include "DPoint.h"
+#include "DPlane.h"
+#include "DEPair.h"
+#include "DPatch.h"
+#include "DEntity.h"
+#include "DWinding.h"
+
+#include "dialogs/dialogs-gtk.h"
+
+#include "misc.h"
+
+#include "iundo.h"
+
+#include "generic/referencecounted.h"
+
+#include "scenelib.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+DBrush::DBrush( int ID ){
+ m_nBrushID = ID;
+ bBoundsBuilt = false;
+ QER_entity = NULL;
+ QER_brush = NULL;
+}
+
+DBrush::~DBrush(){
+ ClearFaces();
+ ClearPoints();
+}
+
+//////////////////////////////////////////////////////////////////////
+// Implementation
+//////////////////////////////////////////////////////////////////////
+
+DPlane* DBrush::AddFace( const vec3_t va, const vec3_t vb, const vec3_t vc, const _QERFaceData* texData ){
+#if GDEF_DEBUG
+// 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]);
+#endif
+ bBoundsBuilt = false;
+ DPlane* newFace = new DPlane( va, vb, vc, texData );
+ faceList.push_back( newFace );
+
+ return newFace;
+}
+
+int DBrush::BuildPoints(){
+ ClearPoints();
+
+ if ( faceList.size() <= 3 ) { // if less than 3 faces, there can be no points
+ return 0; // with only 3 faces u can't have a bounded soild
+
+ }
+ for ( std::list<DPlane *>::const_iterator p1 = faceList.begin(); p1 != faceList.end(); p1++ )
+ {
+ std::list<DPlane *>::const_iterator p2 = p1;
+ for ( p2++; p2 != faceList.end(); p2++ )
+ {
+ std::list<DPlane *>::const_iterator p3 = p2;
+ for ( p3++; p3 != faceList.end(); p3++ )
+ {
+ vec3_t pnt;
+ if ( ( *p1 )->PlaneIntersection( *p2, *p3, pnt ) ) {
+ int pos = PointPosition( pnt );
+
+ if ( pos == POINT_IN_BRUSH ) { // ???? shouldn't happen here
+ globalErrorStream() << "ERROR:: Build Brush Points: Point IN brush!!!\n";
+ }
+ else if ( pos == POINT_ON_BRUSH ) { // normal point
+ if ( !HasPoint( pnt ) ) {
+ AddPoint( pnt );
+ }
+/* else
+ Sys_Printf("Duplicate Point Found, pyramids ahoy!!!!!\n");*/
+ // point lies on more that 3 planes
+ }
+
+ // otherwise point is removed due to another plane..
+
+ // Sys_Printf("(%f, %f, %f)\n", pnt[0], pnt[1], pnt[2]);
+ }
+ }
+ }
+ }
+
+#if GDEF_DEBUG
+// Sys_Printf("%i points on brush\n", pointList.size());
+#endif
+
+ return static_cast<int>( pointList.size() );
+}
+
+void DBrush_addFace( DBrush& brush, const _QERFaceData& faceData ){
+ brush.AddFace( vector3_to_array( faceData.m_p0 ), vector3_to_array( faceData.m_p1 ), vector3_to_array( faceData.m_p2 ), 0 );
+}
+typedef ReferenceCaller1<DBrush, const _QERFaceData&, DBrush_addFace> DBrushAddFaceCaller;
+
+void DBrush_addFaceTextured( DBrush& brush, const _QERFaceData& faceData ){
+ brush.AddFace( vector3_to_array( faceData.m_p0 ), vector3_to_array( faceData.m_p1 ), vector3_to_array( faceData.m_p2 ), &faceData );
+}
+typedef ReferenceCaller1<DBrush, const _QERFaceData&, DBrush_addFaceTextured> DBrushAddFaceTexturedCaller;
+
+void DBrush::LoadFromBrush( scene::Instance& brush, bool textured ){
+ ClearFaces();
+ ClearPoints();
+
+ GlobalBrushCreator().Brush_forEachFace( brush.path().top(), textured ? BrushFaceDataCallback( DBrushAddFaceTexturedCaller( *this ) ) : BrushFaceDataCallback( DBrushAddFaceCaller( *this ) ) );
+
+ QER_entity = brush.path().parent().get_pointer();
+ QER_brush = brush.path().top().get_pointer();
+}
+
+int DBrush::PointPosition( vec3_t pnt ){
+ int state = POINT_IN_BRUSH; // if nothing happens point is inside brush
+
+ for ( std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
+ {
+ float dist = ( *chkPlane )->DistanceToPoint( pnt );
+
+ if ( dist > MAX_ROUND_ERROR ) {
+ return POINT_OUT_BRUSH; // if point is in front of plane, it CANT be in the brush
+ }
+ else if ( fabs( dist ) < MAX_ROUND_ERROR ) {
+ state = POINT_ON_BRUSH; // if point is ON plane point is either ON the brush
+ }
+ // or outside it, it can no longer be in it
+ }
+
+ return state;
+}
+
+void DBrush::ClearPoints(){
+ for ( std::list<DPoint *>::const_iterator deadPoint = pointList.begin(); deadPoint != pointList.end(); deadPoint++ ) {
+ delete *deadPoint;
+ }
+ pointList.clear();
+}
+
+void DBrush::ClearFaces(){
+ bBoundsBuilt = false;
+ for ( std::list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++ )
+ {
+ delete *deadPlane;
+ }
+ faceList.clear();
+}
+
+void DBrush::AddPoint( vec3_t pnt ){
+ DPoint* newPoint = new DPoint;
+ VectorCopy( pnt, newPoint->_pnt );
+ pointList.push_back( newPoint );
+}
+
+bool DBrush::HasPoint( vec3_t pnt ){
+ for ( std::list<DPoint *>::const_iterator chkPoint = pointList.begin(); chkPoint != pointList.end(); chkPoint++ )
+ {
+ if ( **chkPoint == pnt ) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int DBrush::RemoveRedundantPlanes(){
+ int cnt = 0;
+ std::list<DPlane *>::iterator chkPlane;
+
+ // find duplicate planes
+ std::list<DPlane *>::iterator p1 = faceList.begin();
+
+ while ( p1 != faceList.end() )
+ {
+ std::list<DPlane *>::iterator p2 = p1;
+
+ for ( p2++; p2 != faceList.end(); p2++ )
+ {
+ if ( **p1 == **p2 ) {
+ if ( !strcmp( ( *p1 )->m_shader.c_str(), "textures/common/caulk" ) ) {
+ delete *p1;
+ p1 = faceList.erase( p1 ); // duplicate plane
+ }
+ else
+ {
+ delete *p2;
+ p2 = faceList.erase( p2 ); // duplicate plane
+ }
+
+ cnt++;
+ break;
+ }
+ }
+
+ if ( p2 == faceList.end() ) {
+ p1++;
+ }
+ }
+
+ //+djbob kill planes with bad normal, they are more of a nuisance than losing a brush
+ chkPlane = faceList.begin();
+ while ( chkPlane != faceList.end() )
+ {
+ if ( VectorLength( ( *chkPlane )->normal ) == 0 ) { // plane has bad normal
+ delete *chkPlane;
+ chkPlane = faceList.erase( chkPlane );
+ cnt++;
+ }
+ else {
+ chkPlane++;
+ }
+ }
+ //-djbob
+
+ if ( pointList.size() == 0 ) { // if points may not have been built, build them
+/* if(BuildPoints() == 0) // just let the planes die if they are all bad
+ return cnt;*/
+ BuildPoints();
+ }
+
+ chkPlane = faceList.begin();
+ while ( chkPlane != faceList.end() )
+ {
+ if ( ( *chkPlane )->IsRedundant( pointList ) ) { // checks that plane "0wnz" :), 3 or more points
+ delete *chkPlane;
+ chkPlane = faceList.erase( chkPlane );
+ cnt++;
+ }
+ else{
+ chkPlane++;
+ }
+ }
+
+ return cnt;
+}
+
+bool DBrush::GetBounds( vec3_t min, vec3_t max ){
+ BuildBounds();
+
+ if ( !bBoundsBuilt ) {
+ return false;
+ }
+
+ VectorCopy( bbox_min, min );
+ VectorCopy( bbox_max, max );
+
+ return true;
+}
+
+bool DBrush::BBoxCollision( DBrush* chkBrush ){
+ vec3_t min1, min2;
+ vec3_t max1, max2;
+
+ GetBounds( min1, max1 );
+ chkBrush->GetBounds( min2, max2 );
+
+ if ( min1[0] >= max2[0] ) {
+ return false;
+ }
+ if ( min1[1] >= max2[1] ) {
+ return false;
+ }
+ if ( min1[2] >= max2[2] ) {
+ return false;
+ }
+
+ if ( max1[0] <= min2[0] ) {
+ return false;
+ }
+ if ( max1[1] <= min2[1] ) {
+ return false;
+ }
+ if ( max1[2] <= min2[2] ) {
+ return false;
+ }
+
+ return true;
+}
+
+DPlane* DBrush::HasPlane( DPlane* chkPlane ){
+ for ( std::list<DPlane *>::const_iterator brushPlane = faceList.begin(); brushPlane != faceList.end(); brushPlane++ )
+ {
+ if ( **brushPlane == *chkPlane ) {
+ return *brushPlane;
+ }
+ }
+ return NULL;
+}
+
+bool DBrush::IsCutByPlane( DPlane *cuttingPlane ){
+ bool isInFront;
+
+ if ( pointList.size() == 0 ) {
+ if ( BuildPoints() == 0 ) {
+ return false;
+ }
+ }
+
+ std::list<DPoint *>::const_iterator chkPnt = pointList.begin();
+
+ if ( chkPnt == pointList.end() ) {
+ return false;
+ }
+
+ float dist = cuttingPlane->DistanceToPoint( ( *chkPnt )->_pnt );
+
+ if ( dist > MAX_ROUND_ERROR ) {
+ isInFront = false;
+ }
+ else if ( dist < MAX_ROUND_ERROR ) {
+ isInFront = true;
+ }
+ else{
+ return true;
+ }
+
+ for ( chkPnt++ = pointList.begin(); chkPnt != pointList.end(); chkPnt++ )
+ {
+ dist = cuttingPlane->DistanceToPoint( ( *chkPnt )->_pnt );
+
+ if ( dist > MAX_ROUND_ERROR ) {
+ if ( isInFront ) {
+ return true;
+ }
+ }
+ else if ( dist < MAX_ROUND_ERROR ) {
+ if ( !isInFront ) {
+ return true;
+ }
+ }
+ else{
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+scene::Node* DBrush::BuildInRadiant( bool allowDestruction, int* changeCnt, scene::Node* entity ){
+ if ( allowDestruction ) {
+ bool kill = true;
+
+ for ( std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
+ {
+ if ( ( *chkPlane )->m_bChkOk ) {
+ kill = false;
+ break;
+ }
+ }
+ if ( kill ) {
+ return NULL;
+ }
+ }
+
+ //+djbob: fixed bug when brush had no faces "phantom brush" in radiant.
+ if ( faceList.size() < 4 ) {
+ globalErrorStream() << "Possible Phantom Brush Found, will not rebuild\n";
+ return NULL;
+ }
+ //-djbob
+
+ NodeSmartReference node( GlobalBrushCreator().createBrush() );
+
+ for ( std::list<DPlane *>::const_iterator buildPlane = faceList.begin(); buildPlane != faceList.end(); buildPlane++ ) {
+ if ( ( *buildPlane )->AddToBrush( node ) && changeCnt ) {
+ ( *changeCnt )++;
+ }
+ }
+
+ if ( entity ) {
+ Node_getTraversable( *entity )->insert( node );
+ }
+ else {
+ Node_getTraversable( GlobalRadiant().getMapWorldEntity() )->insert( node );
+ }
+
+ QER_entity = entity;
+ QER_brush = node.get_pointer();
+
+ return node.get_pointer();
+}
+
+void DBrush::CutByPlane( DPlane *cutPlane, DBrush **newBrush1, DBrush **newBrush2 ){
+ if ( !IsCutByPlane( cutPlane ) ) {
+ *newBrush1 = NULL;
+ *newBrush2 = NULL;
+ return;
+ }
+
+ DBrush* b1 = new DBrush;
+ DBrush* b2 = new DBrush;
+
+ for ( std::list<DPlane *>::const_iterator parsePlane = faceList.begin(); parsePlane != faceList.end(); parsePlane++ )
+ {
+ b1->AddFace( ( *parsePlane )->points[0], ( *parsePlane )->points[1], ( *parsePlane )->points[2], NULL );
+ b2->AddFace( ( *parsePlane )->points[0], ( *parsePlane )->points[1], ( *parsePlane )->points[2], NULL );
+ }
+
+ b1->AddFace( cutPlane->points[0], cutPlane->points[1], cutPlane->points[2], NULL );
+ b2->AddFace( cutPlane->points[2], cutPlane->points[1], cutPlane->points[0], NULL );
+
+ b1->RemoveRedundantPlanes();
+ b2->RemoveRedundantPlanes();
+
+ *newBrush1 = b1;
+ *newBrush2 = b2;
+}
+
+bool DBrush::IntersectsWith( DBrush *chkBrush ){
+ if ( pointList.size() == 0 ) {
+ if ( BuildPoints() == 0 ) {
+ return false; // invalid brush!!!!
+
+ }
+ }
+ if ( chkBrush->pointList.size() == 0 ) {
+ if ( chkBrush->BuildPoints() == 0 ) {
+ return false; // invalid brush!!!!
+
+ }
+ }
+ if ( !BBoxCollision( chkBrush ) ) {
+ return false;
+ }
+
+ std::list<DPlane *>::const_iterator iplPlane;
+
+ for ( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++ )
+ {
+
+ bool allInFront = true;
+ for ( std::list<DPoint *>::const_iterator iPoint = chkBrush->pointList.begin(); iPoint != chkBrush->pointList.end(); iPoint++ )
+ {
+ if ( ( *iplPlane )->DistanceToPoint( ( *iPoint )->_pnt ) < -MAX_ROUND_ERROR ) {
+ allInFront = false;
+ break;
+ }
+ }
+ if ( allInFront ) {
+ return false;
+ }
+ }
+
+ for ( iplPlane = chkBrush->faceList.begin(); iplPlane != chkBrush->faceList.end(); iplPlane++ )
+ {
+ bool allInFront = true;
+ for ( std::list<DPoint *>::const_iterator iPoint = pointList.begin(); iPoint != pointList.end(); iPoint++ )
+ {
+ if ( ( *iplPlane )->DistanceToPoint( ( *iPoint )->_pnt ) < -MAX_ROUND_ERROR ) {
+ allInFront = false;
+ break;
+ }
+ }
+ if ( allInFront ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool DBrush::IntersectsWith( DPlane* p1, DPlane* p2, vec3_t v ) {
+ vec3_t vDown = { 0, 0, -1 };
+
+ std::list<DPlane *>::const_iterator iplPlane;
+ for ( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++ ) {
+ DPlane* p = ( *iplPlane );
+
+ vec_t d = DotProduct( p->normal, vDown );
+ if ( d >= 0 ) {
+ continue;
+ }
+ if ( p->PlaneIntersection( p1, p2, v ) ) {
+ if ( PointPosition( v ) != POINT_OUT_BRUSH ) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void DBrush::BuildBounds(){
+ if ( !bBoundsBuilt ) {
+ if ( pointList.size() == 0 ) { // if points may not have been built, build them
+ if ( BuildPoints() == 0 ) {
+ return;
+ }
+ }
+
+ std::list<DPoint *>::const_iterator first = pointList.begin();
+ VectorCopy( ( *first )->_pnt, bbox_min );
+ VectorCopy( ( *first )->_pnt, bbox_max );
+
+ std::list<DPoint *>::const_iterator point = pointList.begin();
+ for ( point++; point != pointList.end(); point++ )
+ {
+ if ( ( *point )->_pnt[0] > bbox_max[0] ) {
+ bbox_max[0] = ( *point )->_pnt[0];
+ }
+ if ( ( *point )->_pnt[1] > bbox_max[1] ) {
+ bbox_max[1] = ( *point )->_pnt[1];
+ }
+ if ( ( *point )->_pnt[2] > bbox_max[2] ) {
+ bbox_max[2] = ( *point )->_pnt[2];
+ }
+
+ if ( ( *point )->_pnt[0] < bbox_min[0] ) {
+ bbox_min[0] = ( *point )->_pnt[0];
+ }
+ if ( ( *point )->_pnt[1] < bbox_min[1] ) {
+ bbox_min[1] = ( *point )->_pnt[1];
+ }
+ if ( ( *point )->_pnt[2] < bbox_min[2] ) {
+ bbox_min[2] = ( *point )->_pnt[2];
+ }
+ }
+
+ bBoundsBuilt = true;
+ }
+}
+
+bool DBrush::BBoxTouch( DBrush *chkBrush ){
+ vec3_t min1, min2;
+ vec3_t max1, max2;
+
+ GetBounds( min1, max1 );
+ chkBrush->GetBounds( min2, max2 );
+
+ if ( ( min1[0] - max2[0] ) > MAX_ROUND_ERROR ) {
+ return false;
+ }
+ if ( ( min1[1] - max2[1] ) > MAX_ROUND_ERROR ) {
+ return false;
+ }
+ if ( ( min1[2] - max2[2] ) > MAX_ROUND_ERROR ) {
+ return false;
+ }
+
+ if ( ( min2[0] - max1[0] ) > MAX_ROUND_ERROR ) {
+ return false;
+ }
+ if ( ( min2[1] - max1[1] ) > MAX_ROUND_ERROR ) {
+ return false;
+ }
+ if ( ( min2[2] - max1[2] ) > MAX_ROUND_ERROR ) {
+ return false;
+ }
+
+ int cnt = 0;
+
+ if ( ( min2[0] - max1[0] ) == 0 ) {
+ cnt++;
+ }
+
+ if ( ( min2[1] - max1[1] ) == 0 ) {
+ cnt++;
+ }
+
+ if ( ( min2[2] - max1[2] ) == 0 ) {
+ cnt++;
+ }
+
+ if ( ( min1[0] - max2[0] ) == 0 ) {
+ cnt++;
+ }
+
+ if ( ( min1[1] - max2[1] ) == 0 ) {
+ cnt++;
+ }
+
+ if ( ( min1[2] - max2[2] ) == 0 ) {
+ cnt++;
+ }
+
+ if ( cnt > 1 ) {
+ return false;
+ }
+
+ return true;
+}
+
+void DBrush::ResetChecks( std::list<Str>* exclusionList ){
+ for ( std::list<DPlane *>::const_iterator resetPlane = faceList.begin(); resetPlane != faceList.end(); resetPlane++ )
+ {
+ bool set = false;
+
+ if ( exclusionList ) {
+ for ( std::list<Str>::iterator eTexture = exclusionList->begin(); eTexture != exclusionList->end(); eTexture++ )
+ {
+ if ( strstr( ( *resetPlane )->m_shader.c_str(), eTexture->GetBuffer() ) ) {
+ set = true;
+ break;
+ }
+ }
+ }
+
+ ( *resetPlane )->m_bChkOk = set;
+ }
+}
+
+DPlane* DBrush::HasPlaneInverted( DPlane *chkPlane ){
+ for ( std::list<DPlane *>::const_iterator brushPlane = faceList.begin(); brushPlane != faceList.end(); brushPlane++ )
+ {
+ if ( **brushPlane != *chkPlane ) {
+ if ( fabs( ( *brushPlane )->_d + chkPlane->_d ) < 0.1 ) {
+ return ( *brushPlane );
+ }
+ }
+ }
+ return NULL;
+}
+
+bool DBrush::HasTexture( const char *textureName ){
+ for ( std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
+ {
+ if ( strstr( ( *chkPlane )->m_shader.c_str(), textureName ) ) {
+ return true;
+ }
+
+ }
+ return false;
+}
+
+bool DBrush::IsDetail(){
+ for ( std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
+ {
+ if ( ( *chkPlane )->texInfo.contents & FACE_DETAIL ) {
+ return true;
+ }
+
+ }
+ return false;
+}
+
+void DBrush::BuildFromWinding( DWinding *w ){
+ if ( w->numpoints < 3 ) {
+ globalErrorStream() << "Winding has invalid number of points";
+ return;
+ }
+
+ DPlane* wPlane = w->WindingPlane();
+
+ DWinding* w2;
+ w2 = w->CopyWinding();
+ int i;
+ for ( i = 0; i < w2->numpoints; i++ )
+ VectorAdd( w2->p[i], wPlane->normal, w2->p[i] );
+
+ AddFace( w2->p[0], w2->p[1], w2->p[2], NULL );
+ AddFace( w->p[2], w->p[1], w->p[0], NULL );
+
+ for ( i = 0; i < w->numpoints - 1; i++ )
+ AddFace( w2->p[i], w->p[i], w->p[i + 1], NULL );
+ AddFace( w2->p[w->numpoints - 1], w->p[w->numpoints - 1], w->p[0], NULL );
+
+ delete wPlane;
+ delete w2;
+}
+
+void DBrush::SaveToFile( FILE *pFile ){
+ fprintf( pFile, "{\n" );
+
+ for ( std::list<DPlane *>::const_iterator pp = faceList.begin(); pp != faceList.end(); pp++ )
+ {
+ char buffer[512];
+
+ sprintf( buffer, "( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) %s %.0f %.0f %f %f %.0f 0 0 0\n",
+ ( *pp )->points[0][0], ( *pp )->points[0][1], ( *pp )->points[0][2],
+ ( *pp )->points[1][0], ( *pp )->points[1][1], ( *pp )->points[1][2],
+ ( *pp )->points[2][0], ( *pp )->points[2][1], ( *pp )->points[2][2],
+ ( *pp )->m_shader.c_str(),
+ ( *pp )->texInfo.m_texdef.shift[0], ( *pp )->texInfo.m_texdef.shift[1],
+ ( *pp )->texInfo.m_texdef.scale[0], ( *pp )->texInfo.m_texdef.scale[0],
+ ( *pp )->texInfo.m_texdef.rotate );
+
+ fprintf( pFile, "%s", buffer );
+ }
+
+ fprintf( pFile, "}\n" );
+}
+
+void DBrush::Rotate( vec3_t vOrigin, vec3_t vRotation ){
+ for ( std::list<DPlane *>::const_iterator rotPlane = faceList.begin(); rotPlane != faceList.end(); rotPlane++ )
+ {
+ for ( int i = 0; i < 3; i++ )
+ VectorRotate( ( *rotPlane )->points[i], vRotation, vOrigin );
+
+ ( *rotPlane )->Rebuild();
+ }
+}
+
+void DBrush::RotateAboutCentre( vec3_t vRotation ){
+ vec3_t min, max, centre;
+ GetBounds( min, max );
+ VectorAdd( min, max, centre );
+ VectorScale( centre, 0.5f, centre );
+
+ Rotate( centre, vRotation );
+}
+
+bool DBrush::ResetTextures( const char* textureName, float fScale[2], float fShift[2], int rotation, const char* newTextureName,
+ int bResetTextureName, int bResetScale[2], int bResetShift[2], int bResetRotation ){
+ if ( textureName ) {
+ bool changed = false;
+ for ( std::list<DPlane *>::const_iterator resetPlane = faceList.begin(); resetPlane != faceList.end(); resetPlane++ )
+ {
+ if ( !strcmp( ( *resetPlane )->m_shader.c_str(), textureName ) ) {
+ if ( bResetTextureName ) {
+ ( *resetPlane )->m_shader = newTextureName;
+ }
+
+ if ( bResetScale[0] ) {
+ ( *resetPlane )->texInfo.m_texdef.scale[0] = fScale[0];
+ }
+ if ( bResetScale[1] ) {
+ ( *resetPlane )->texInfo.m_texdef.scale[1] = fScale[1];
+ }
+
+ if ( bResetShift[0] ) {
+ ( *resetPlane )->texInfo.m_texdef.shift[0] = fShift[0];
+ }
+ if ( bResetShift[1] ) {
+ ( *resetPlane )->texInfo.m_texdef.shift[1] = fShift[1];
+ }
+
+ if ( bResetRotation ) {
+ ( *resetPlane )->texInfo.m_texdef.rotate = (float)rotation;
+ }
+
+ changed = true;
+ }
+ }
+ return changed; // no point rebuilding unless we need to, only slows things down
+ }
+ else
+ {
+ for ( std::list<DPlane *>::const_iterator resetPlane = faceList.begin(); resetPlane != faceList.end(); resetPlane++ )
+ {
+ if ( bResetTextureName ) {
+ ( *resetPlane )->m_shader = newTextureName;
+ }
+
+ if ( bResetScale[0] ) {
+ ( *resetPlane )->texInfo.m_texdef.scale[0] = fScale[0];
+ }
+ if ( bResetScale[1] ) {
+ ( *resetPlane )->texInfo.m_texdef.scale[1] = fScale[1];
+ }
+
+ if ( bResetShift[0] ) {
+ ( *resetPlane )->texInfo.m_texdef.shift[0] = fShift[0];
+ }
+ if ( bResetShift[1] ) {
+ ( *resetPlane )->texInfo.m_texdef.shift[1] = fShift[1];
+ }
+
+ if ( bResetRotation ) {
+ ( *resetPlane )->texInfo.m_texdef.rotate = (float)rotation;
+ }
+ }
+ return true;
+ }
+}
+
+bool DBrush::operator ==( DBrush* other ){
+ std::list<DPlane *>::const_iterator chkPlane;
+
+ for ( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
+ {
+ if ( !other->HasPlane( ( *chkPlane ) ) ) {
+ return false;
+ }
+ }
+
+ for ( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
+ {
+ if ( !HasPlane( ( *chkPlane ) ) ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+DPlane* DBrush::AddFace( const vec3_t va, const vec3_t vb, const vec3_t vc, const char *textureName, bool bDetail ){
+ bBoundsBuilt = false;
+ DPlane* newFace = new DPlane( va, vb, vc, textureName, bDetail );
+ faceList.push_back( newFace );
+
+ return newFace;
+}
+
+DPlane* DBrush::FindPlaneWithClosestNormal( vec_t* normal ) {
+ vec_t bestDot = -2;
+ DPlane* bestDotPlane = NULL;
+ std::list<DPlane *>::const_iterator chkPlane;
+ for ( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ ) {
+ DPlane* pPlane = ( *chkPlane );
+
+ vec_t dot = DotProduct( pPlane->normal, normal );
+ if ( dot > bestDot ) {
+ bestDot = dot;
+ bestDotPlane = pPlane;
+ }
+ }
+
+ return bestDotPlane;
+}
+
+int DBrush::FindPointsForPlane( DPlane* plane, DPoint** pnts, int maxpnts ) {
+ int numpnts = 0;
+
+ if ( !maxpnts ) {
+ return 0;
+ }
+
+ BuildPoints();
+
+ for ( std::list<DPoint *>::const_iterator points = pointList.begin(); points != pointList.end(); points++ ) {
+ DPoint* point = ( *points );
+
+ if ( fabs( plane->DistanceToPoint( point->_pnt ) ) < MAX_ROUND_ERROR ) {
+ pnts[numpnts] = point;
+ numpnts++;
+
+ if ( numpnts >= maxpnts ) {
+ return numpnts;
+ }
+
+ }
+ }
+
+ return numpnts;
+}
+
+void DBrush::RemovePlane( DPlane* plane ) {
+ bBoundsBuilt = false;
+ for ( std::list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++ ) {
+ if ( *deadPlane == plane ) {
+ delete *deadPlane;
+ faceList.remove( plane );
+ }
+ }
+}