2 BobToolz plugin for GtkRadiant
3 Copyright (C) 2001 Gordon Biggans
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.
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.
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
20 // DBrush.cpp: implementation of the DBrush class.
22 //////////////////////////////////////////////////////////////////////
27 #pragma warning(disable : 4786)
32 #include "dialogs/dialogs-gtk.h"
36 //////////////////////////////////////////////////////////////////////
37 // Construction/Destruction
38 //////////////////////////////////////////////////////////////////////
40 DBrush::DBrush(int ID)
53 //////////////////////////////////////////////////////////////////////
55 //////////////////////////////////////////////////////////////////////
57 DPlane* DBrush::AddFace(vec3_t va, vec3_t vb, vec3_t vc, _QERFaceData* texData)
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]);
63 DPlane* newFace = new DPlane(va, vb, vc, texData);
64 faceList.push_back(newFace);
69 int DBrush::BuildPoints()
73 if(faceList.size() <= 3) // if less than 3 faces, there can be no points
74 return 0; // with only 3 faces u can't have a bounded soild
76 for(list<DPlane *>::const_iterator p1=faceList.begin(); p1!=faceList.end(); p1++)
78 list<DPlane *>::const_iterator p2=p1;
79 for(p2++; p2!=faceList.end(); p2++)
81 list<DPlane *>::const_iterator p3=p2;
82 for(p3++; p3!=faceList.end(); p3++)
85 if((*p1)->PlaneIntersection(*p2, *p3, pnt))
87 int pos = PointPosition(pnt);
89 if(pos == POINT_IN_BRUSH)
90 { // ???? shouldn't happen here
91 Sys_Printf("ERROR:: Build Brush Points: Point IN brush!!!\n");
93 else if(pos == POINT_ON_BRUSH)
98 Sys_Printf("Duplicate Point Found, pyramids ahoy!!!!!\n");*/
99 // point lies on more that 3 planes
102 // otherwise point is removed due to another plane..
104 // Sys_Printf("(%f, %f, %f)\n", pnt[0], pnt[1], pnt[2]);
111 // Sys_Printf("%i points on brush\n", pointList.size());
114 return pointList.size();
117 void DBrush::LoadFromBrush_t(brush_t* brush, bool textured)
122 for(int i = g_FuncTable.m_pfnGetFaceCount(brush)-1; i >= 0 ; i--)
123 { // running backwards so i dont have to use the count function each time (OPT)
124 _QERFaceData* faceData = g_FuncTable.m_pfnGetFaceData(brush, i);
127 DoMessageBox("Null pointer returned", "WARNING!", MB_OK);
130 AddFace(faceData->m_v1, faceData->m_v2, faceData->m_v3, faceData);
132 AddFace(faceData->m_v1, faceData->m_v2, faceData->m_v3, NULL);
138 int DBrush::PointPosition(vec3_t pnt)
140 int state = POINT_IN_BRUSH; // if nothing happens point is inside brush
142 for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
144 float dist = (*chkPlane)->DistanceToPoint(pnt);
146 if(dist > MAX_ROUND_ERROR)
147 return POINT_OUT_BRUSH; // if point is in front of plane, it CANT be in the brush
148 else if(fabs(dist) < MAX_ROUND_ERROR)
149 state = POINT_ON_BRUSH; // if point is ON plane point is either ON the brush
150 // or outside it, it can no longer be in it
156 void DBrush::ClearPoints()
158 for(list<DPoint *>::const_iterator deadPoint=pointList.begin(); deadPoint!=pointList.end(); deadPoint++) {
164 void DBrush::ClearFaces()
166 bBoundsBuilt = FALSE;
167 for(list<DPlane *>::const_iterator deadPlane=faceList.begin(); deadPlane!=faceList.end(); deadPlane++)
174 void DBrush::AddPoint(vec3_t pnt)
176 DPoint* newPoint = new DPoint;
177 VectorCopy(pnt, newPoint->_pnt);
178 pointList.push_back(newPoint);
181 bool DBrush::HasPoint(vec3_t pnt)
183 for(list<DPoint *>::const_iterator chkPoint=pointList.begin(); chkPoint!=pointList.end(); chkPoint++)
185 if(**chkPoint == pnt)
192 int DBrush::RemoveRedundantPlanes()
195 list<DPlane *>::iterator chkPlane;
197 // find duplicate planes
198 list<DPlane *>::iterator p1=faceList.begin();
200 while( p1!=faceList.end() )
202 list<DPlane *>::iterator p2 = p1;
204 for(p2++; p2!=faceList.end(); p2++)
208 if(!strcmp((*p1)->texInfo.m_TextureName, "textures/common/caulk"))
211 p1 = faceList.erase(p1); // duplicate plane
216 p2 = faceList.erase(p2); // duplicate plane
224 if( p2 == faceList.end() )
228 //+djbob kill planes with bad normal, they are more of a nuisance than losing a brush
229 chkPlane=faceList.begin();
230 while( chkPlane!=faceList.end() )
232 if(VectorLength((*chkPlane)->normal) == 0) // plane has bad normal
235 chkPlane = faceList.erase(chkPlane);
243 if(pointList.size() == 0) // if points may not have been built, build them
244 /* if(BuildPoints() == 0) // just let the planes die if they are all bad
248 chkPlane=faceList.begin();
249 while(chkPlane != faceList.end())
251 if((*chkPlane)->IsRedundant(pointList)) // checks that plane "0wnz" :), 3 or more points
254 chkPlane = faceList.erase(chkPlane);
264 bool DBrush::GetBounds(vec3_t min, vec3_t max)
271 VectorCopy(bbox_min, min);
272 VectorCopy(bbox_max, max);
277 bool DBrush::BBoxCollision(DBrush* chkBrush)
282 GetBounds(min1, max1);
283 chkBrush->GetBounds(min2, max2);
285 if(min1[0] >= max2[0])
287 if(min1[1] >= max2[1])
289 if(min1[2] >= max2[2])
292 if(max1[0] <= min2[0])
294 if(max1[1] <= min2[1])
296 if(max1[2] <= min2[2])
302 DPlane* DBrush::HasPlane(DPlane* chkPlane)
304 for(list<DPlane *>::const_iterator brushPlane=faceList.begin(); brushPlane!=faceList.end(); brushPlane++)
306 if(**brushPlane == *chkPlane)
312 bool DBrush::IsCutByPlane(DPlane *cuttingPlane)
316 if(pointList.size() == 0)
317 if(BuildPoints() == 0)
320 list<DPoint *>::const_iterator chkPnt = pointList.begin();
322 if(chkPnt == pointList.end())
325 float dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);
327 if(dist > MAX_ROUND_ERROR)
329 else if(dist < MAX_ROUND_ERROR)
334 for(chkPnt++=pointList.begin(); chkPnt!=pointList.end(); chkPnt++)
336 dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);
338 if(dist > MAX_ROUND_ERROR)
343 else if(dist < MAX_ROUND_ERROR)
355 brush_t* DBrush::BuildInRadiant(bool allowDestruction, int* changeCnt, entity_t* entity)
361 for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
363 if((*chkPlane)->m_bChkOk)
373 //+djbob: fixed bug when brush had no faces "phantom brush" in radiant.
374 if(faceList.size() < 4)
376 Sys_Printf("Possible Phantom Brush Found, will not rebuild\n");
381 QER_brush = (brush_t*)g_FuncTable.m_pfnCreateBrushHandle();
383 for(list<DPlane *>::const_iterator buildPlane=faceList.begin(); buildPlane!=faceList.end(); buildPlane++) {
384 if((*buildPlane)->AddToBrush_t(QER_brush) && changeCnt) {
390 g_FuncTable.m_pfnCommitBrushHandleToEntity(QER_brush, entity);
391 g_BrushTable.m_pfnBrush_Build(QER_brush, false, false, false, false);
392 g_BrushTable.m_pfnBrush_AddToList(QER_brush, g_AppDataTable.m_pfnSelectedBrushes());
394 g_FuncTable.m_pfnCommitBrushHandle(QER_brush);
400 void DBrush::CutByPlane(DPlane *cutPlane, DBrush **newBrush1, DBrush **newBrush2)
402 if(!IsCutByPlane(cutPlane))
409 DBrush* b1 = new DBrush;
410 DBrush* b2 = new DBrush;
412 for(list<DPlane *>::const_iterator parsePlane=faceList.begin(); parsePlane!=faceList.end(); parsePlane++)
414 b1->AddFace((*parsePlane)->points[0], (*parsePlane)->points[1], (*parsePlane)->points[2], NULL);
415 b2->AddFace((*parsePlane)->points[0], (*parsePlane)->points[1], (*parsePlane)->points[2], NULL);
418 b1->AddFace(cutPlane->points[0], cutPlane->points[1], cutPlane->points[2], NULL);
419 b2->AddFace(cutPlane->points[2], cutPlane->points[1], cutPlane->points[0], NULL);
421 b1->RemoveRedundantPlanes();
422 b2->RemoveRedundantPlanes();
428 bool DBrush::IntersectsWith(DBrush *chkBrush)
430 if(pointList.size() == 0)
431 if(BuildPoints() == 0)
432 return FALSE; // invalid brush!!!!
434 if(chkBrush->pointList.size() == 0)
435 if(chkBrush->BuildPoints() == 0)
436 return FALSE; // invalid brush!!!!
438 if(!BBoxCollision(chkBrush))
441 list<DPlane *>::const_iterator iplPlane;
443 for( iplPlane=faceList.begin(); iplPlane!=faceList.end(); iplPlane++)
446 bool allInFront = TRUE;
447 for(list<DPoint *>::const_iterator iPoint=chkBrush->pointList.begin(); iPoint!=chkBrush->pointList.end(); iPoint++)
449 if((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR)
459 for( iplPlane=chkBrush->faceList.begin(); iplPlane!=chkBrush->faceList.end(); iplPlane++)
461 bool allInFront = TRUE;
462 for(list<DPoint *>::const_iterator iPoint=pointList.begin(); iPoint!=pointList.end(); iPoint++)
464 if((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR)
477 bool DBrush::IntersectsWith(DPlane* p1, DPlane* p2, vec3_t v) {
478 vec3_t vDown = { 0, 0, -1 };
480 list<DPlane *>::const_iterator iplPlane;
481 for( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++) {
482 DPlane* p = (*iplPlane);
484 vec_t d = DotProduct( p->normal, vDown );
488 if(p->PlaneIntersection(p1, p2, v)) {
489 if(PointPosition( v ) != POINT_OUT_BRUSH) {
498 void DBrush::BuildBounds()
502 if(pointList.size() == 0) // if points may not have been built, build them
503 if(BuildPoints() == 0)
506 list<DPoint *>::const_iterator first = pointList.begin();
507 VectorCopy((*first)->_pnt, bbox_min);
508 VectorCopy((*first)->_pnt, bbox_max);
510 list<DPoint *>::const_iterator point=pointList.begin();
511 for( point++; point!=pointList.end(); point++)
513 if((*point)->_pnt[0] > bbox_max[0])
514 bbox_max[0] = (*point)->_pnt[0];
515 if((*point)->_pnt[1] > bbox_max[1])
516 bbox_max[1] = (*point)->_pnt[1];
517 if((*point)->_pnt[2] > bbox_max[2])
518 bbox_max[2] = (*point)->_pnt[2];
520 if((*point)->_pnt[0] < bbox_min[0])
521 bbox_min[0] = (*point)->_pnt[0];
522 if((*point)->_pnt[1] < bbox_min[1])
523 bbox_min[1] = (*point)->_pnt[1];
524 if((*point)->_pnt[2] < bbox_min[2])
525 bbox_min[2] = (*point)->_pnt[2];
532 bool DBrush::BBoxTouch(DBrush *chkBrush)
537 GetBounds(min1, max1);
538 chkBrush->GetBounds(min2, max2);
540 if((min1[0] - max2[0]) > MAX_ROUND_ERROR)
542 if((min1[1] - max2[1]) > MAX_ROUND_ERROR)
544 if((min1[2] - max2[2]) > MAX_ROUND_ERROR)
547 if((min2[0] - max1[0]) > MAX_ROUND_ERROR)
549 if((min2[1] - max1[1]) > MAX_ROUND_ERROR)
551 if((min2[2] - max1[2]) > MAX_ROUND_ERROR)
556 if((min2[0] - max1[0]) == 0)
559 if((min2[1] - max1[1]) == 0)
562 if((min2[2] - max1[2]) == 0)
565 if((min1[0] - max2[0]) == 0)
568 if((min1[1] - max2[1]) == 0)
571 if((min1[2] - max2[2]) == 0)
580 void DBrush::ResetChecks(list<Str>* exclusionList)
582 for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
588 for(list<Str>::iterator eTexture = exclusionList->begin(); eTexture != exclusionList->end(); eTexture++)
590 if(strstr((*resetPlane)->texInfo.m_TextureName, eTexture->GetBuffer()))
598 (*resetPlane)->m_bChkOk = set;
602 DPlane* DBrush::HasPlaneInverted(DPlane *chkPlane)
604 for(list<DPlane *>::const_iterator brushPlane=faceList.begin(); brushPlane!=faceList.end(); brushPlane++)
606 if(**brushPlane != *chkPlane)
608 if(fabs((*brushPlane)->_d + chkPlane->_d) < 0.1)
609 return (*brushPlane);
615 bool DBrush::HasTexture(const char *textureName)
617 for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
619 if(strstr((*chkPlane)->texInfo.m_TextureName, textureName))
626 bool DBrush::IsDetail()
628 for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
630 if((*chkPlane)->texInfo.m_nContents & FACE_DETAIL)
637 void DBrush::BuildFromWinding(DWinding *w)
641 Sys_ERROR("Winding has invalid number of points");
645 DPlane* wPlane = w->WindingPlane();
648 w2 = w->CopyWinding();
650 for(i = 0; i < w2->numpoints; i++)
651 VectorAdd(w2->p[i], wPlane->normal, w2->p[i]);
653 AddFace(w2->p[0], w2->p[1], w2->p[2], NULL);
654 AddFace(w->p[2], w->p[1], w->p[0], NULL);
656 for(i = 0; i < w->numpoints-1; i++)
657 AddFace(w2->p[i], w->p[i], w->p[i+1], NULL);
658 AddFace(w2->p[w->numpoints-1], w->p[w->numpoints-1], w->p[0], NULL);
664 void DBrush::SaveToFile(FILE *pFile)
666 fprintf(pFile, "{\n");
668 for(list<DPlane *>::const_iterator pp=faceList.begin(); pp!=faceList.end(); pp++)
672 sprintf(buffer, "( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) %s %.0f %.0f %f %f %.0f 0 0 0\n",
673 (*pp)->points[0][0], (*pp)->points[0][1], (*pp)->points[0][2],
674 (*pp)->points[1][0], (*pp)->points[1][1], (*pp)->points[1][2],
675 (*pp)->points[2][0], (*pp)->points[2][1], (*pp)->points[2][2],
676 (*pp)->texInfo.m_TextureName,
677 (*pp)->texInfo.m_fShift[0], (*pp)->texInfo.m_fShift[1],
678 (*pp)->texInfo.m_fScale[0], (*pp)->texInfo.m_fScale[0],
679 (*pp)->texInfo.m_fRotate);
681 fprintf(pFile, buffer);
684 fprintf(pFile, "}\n");
687 void DBrush::Rotate(vec3_t vOrigin, vec3_t vRotation)
689 for(list<DPlane *>::const_iterator rotPlane=faceList.begin(); rotPlane!=faceList.end(); rotPlane++)
691 for(int i = 0; i < 3; i++)
692 VectorRotate((*rotPlane)->points[i], vRotation, vOrigin);
694 (*rotPlane)->Rebuild();
698 void DBrush::RotateAboutCentre(vec3_t vRotation)
700 vec3_t min, max, centre;
702 VectorAdd(min, max, centre);
703 VectorScale(centre, 0.5f, centre);
705 Rotate(centre, vRotation);
708 bool DBrush::ResetTextures(const char* textureName, float fScale[2], float fShift[2], int rotation, const char* newTextureName,
709 int bResetTextureName, int bResetScale[2], int bResetShift[2], int bResetRotation)
713 bool changed = FALSE;
714 for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
716 if(!strcmp((*resetPlane)->texInfo.m_TextureName, textureName))
718 if(bResetTextureName)
719 strcpy((*resetPlane)->texInfo.m_TextureName, newTextureName);
722 (*resetPlane)->texInfo.m_fScale[0] = fScale[0];
724 (*resetPlane)->texInfo.m_fScale[1] = fScale[1];
727 (*resetPlane)->texInfo.m_fShift[0] = fShift[0];
729 (*resetPlane)->texInfo.m_fShift[1] = fShift[1];
732 (*resetPlane)->texInfo.m_fRotate = (float)rotation;
737 return changed; // no point rebuilding unless we need to, only slows things down
741 for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
743 if(bResetTextureName)
744 strcpy((*resetPlane)->texInfo.m_TextureName, newTextureName);
747 (*resetPlane)->texInfo.m_fScale[0] = fScale[0];
749 (*resetPlane)->texInfo.m_fScale[1] = fScale[1];
752 (*resetPlane)->texInfo.m_fShift[0] = fShift[0];
754 (*resetPlane)->texInfo.m_fShift[1] = fShift[1];
757 (*resetPlane)->texInfo.m_fRotate = (float)rotation;
763 bool DBrush::operator ==(DBrush* other)
765 list<DPlane *>::const_iterator chkPlane;
767 for(chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
769 if(!other->HasPlane((*chkPlane)))
773 for(chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
775 if(!HasPlane((*chkPlane)))
782 DPlane* DBrush::AddFace(vec3_t va, vec3_t vb, vec3_t vc, const char *textureName, bool bDetail)
784 bBoundsBuilt = FALSE;
785 DPlane* newFace = new DPlane(va, vb, vc, textureName, bDetail);
786 faceList.push_back(newFace);
791 DPlane* DBrush::FindPlaneWithClosestNormal( vec_t* normal ) {
793 DPlane* bestDotPlane = NULL;
794 list<DPlane *>::const_iterator chkPlane;
795 for( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ ) {
796 DPlane* pPlane = (*chkPlane);
798 vec_t dot = DotProduct( pPlane->normal, normal );
799 if( dot > bestDot ) {
801 bestDotPlane = pPlane;
808 int DBrush::FindPointsForPlane( DPlane* plane, DPoint** pnts, int maxpnts ) {
817 for( list<DPoint *>::const_iterator points = pointList.begin(); points != pointList.end(); points++ ) {
818 DPoint* point = (*points);
820 if( fabs(plane->DistanceToPoint( point->_pnt )) < MAX_ROUND_ERROR ) {
821 pnts[numpnts] = point;
824 if(numpnts >= maxpnts) {
834 void DBrush::RemovePlane( DPlane* plane ) {
835 bBoundsBuilt = FALSE;
836 for( list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++ ) {
837 if(*deadPlane == plane) {
839 faceList.remove( plane );
844 void DBrush::RemoveFromRadiant( void ) {
846 g_FuncTable.m_pfnDeleteBrushHandle(QER_brush);