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 "gtkr_list.h"
43 #include "dialogs-gtk.h"
49 #include "refcounted_ptr.h"
53 //////////////////////////////////////////////////////////////////////
54 // Construction/Destruction
55 //////////////////////////////////////////////////////////////////////
57 DBrush::DBrush(int ID)
70 //////////////////////////////////////////////////////////////////////
72 //////////////////////////////////////////////////////////////////////
74 DPlane* DBrush::AddFace(vec3_t va, vec3_t vb, vec3_t vc, _QERFaceData* texData)
77 // Sys_Printf("(%f %f %f) (%f %f %f) (%f %f %f)\n", va[0], va[1], va[2], vb[0], vb[1], vb[2], vc[0], vc[1], vc[2]);
80 DPlane* newFace = new DPlane(va, vb, vc, texData);
81 faceList.push_back(newFace);
86 int DBrush::BuildPoints()
90 if(faceList.size() <= 3) // if less than 3 faces, there can be no points
91 return 0; // with only 3 faces u can't have a bounded soild
93 for(list<DPlane *>::const_iterator p1=faceList.begin(); p1!=faceList.end(); p1++)
95 list<DPlane *>::const_iterator p2=p1;
96 for(p2++; p2!=faceList.end(); p2++)
98 list<DPlane *>::const_iterator p3=p2;
99 for(p3++; p3!=faceList.end(); p3++)
102 if((*p1)->PlaneIntersection(*p2, *p3, pnt))
104 int pos = PointPosition(pnt);
106 if(pos == POINT_IN_BRUSH)
107 { // ???? shouldn't happen here
108 Sys_Printf("ERROR:: Build Brush Points: Point IN brush!!!\n");
110 else if(pos == POINT_ON_BRUSH)
115 Sys_Printf("Duplicate Point Found, pyramids ahoy!!!!!\n");*/
116 // point lies on more that 3 planes
119 // otherwise point is removed due to another plane..
121 // Sys_Printf("(%f, %f, %f)\n", pnt[0], pnt[1], pnt[2]);
128 // Sys_Printf("%i points on brush\n", pointList.size());
131 return pointList.size();
134 void DBrush::LoadFromBrush(scene::Node* brush, bool textured)
140 for(int i = g_FuncTable.m_pfnGetFaceCount(brush)-1; i >= 0 ; i--)
141 { // running backwards so i dont have to use the count function each time (OPT)
142 _QERFaceData* faceData = g_FuncTable.m_pfnGetFaceData(brush, i);
145 DoMessageBox("Null pointer returned", "WARNING!", MB_OK);
148 AddFace(faceData->m_v1, faceData->m_v2, faceData->m_v3, faceData);
150 AddFace(faceData->m_v1, faceData->m_v2, faceData->m_v3, NULL);
157 int DBrush::PointPosition(vec3_t pnt)
159 int state = POINT_IN_BRUSH; // if nothing happens point is inside brush
161 for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
163 float dist = (*chkPlane)->DistanceToPoint(pnt);
165 if(dist > MAX_ROUND_ERROR)
166 return POINT_OUT_BRUSH; // if point is in front of plane, it CANT be in the brush
167 else if(fabs(dist) < MAX_ROUND_ERROR)
168 state = POINT_ON_BRUSH; // if point is ON plane point is either ON the brush
169 // or outside it, it can no longer be in it
175 void DBrush::ClearPoints()
177 for(list<DPoint *>::const_iterator deadPoint=pointList.begin(); deadPoint!=pointList.end(); deadPoint++) {
183 void DBrush::ClearFaces()
185 bBoundsBuilt = FALSE;
186 for(list<DPlane *>::const_iterator deadPlane=faceList.begin(); deadPlane!=faceList.end(); deadPlane++)
193 void DBrush::AddPoint(vec3_t pnt)
195 DPoint* newPoint = new DPoint;
196 VectorCopy(pnt, newPoint->_pnt);
197 pointList.push_back(newPoint);
200 bool DBrush::HasPoint(vec3_t pnt)
202 for(list<DPoint *>::const_iterator chkPoint=pointList.begin(); chkPoint!=pointList.end(); chkPoint++)
204 if(**chkPoint == pnt)
211 int DBrush::RemoveRedundantPlanes()
214 list<DPlane *>::iterator chkPlane;
216 // find duplicate planes
217 list<DPlane *>::iterator p1=faceList.begin();
219 while( p1!=faceList.end() )
221 list<DPlane *>::iterator p2 = p1;
223 for(p2++; p2!=faceList.end(); p2++)
227 if(!strcmp((*p1)->texInfo.m_texdef.GetName(), "textures/common/caulk"))
230 p1 = faceList.erase(p1); // duplicate plane
235 p2 = faceList.erase(p2); // duplicate plane
243 if( p2 == faceList.end() )
247 //+djbob kill planes with bad normal, they are more of a nuisance than losing a brush
248 chkPlane=faceList.begin();
249 while( chkPlane!=faceList.end() )
251 if(VectorLength((*chkPlane)->normal) == 0) // plane has bad normal
254 chkPlane = faceList.erase(chkPlane);
262 if(pointList.size() == 0) // if points may not have been built, build them
263 /* if(BuildPoints() == 0) // just let the planes die if they are all bad
267 chkPlane=faceList.begin();
268 while(chkPlane != faceList.end())
270 if((*chkPlane)->IsRedundant(pointList)) // checks that plane "0wnz" :), 3 or more points
273 chkPlane = faceList.erase(chkPlane);
283 bool DBrush::GetBounds(vec3_t min, vec3_t max)
290 VectorCopy(bbox_min, min);
291 VectorCopy(bbox_max, max);
296 bool DBrush::BBoxCollision(DBrush* chkBrush)
301 GetBounds(min1, max1);
302 chkBrush->GetBounds(min2, max2);
304 if(min1[0] >= max2[0])
306 if(min1[1] >= max2[1])
308 if(min1[2] >= max2[2])
311 if(max1[0] <= min2[0])
313 if(max1[1] <= min2[1])
315 if(max1[2] <= min2[2])
321 DPlane* DBrush::HasPlane(DPlane* chkPlane)
323 for(list<DPlane *>::const_iterator brushPlane=faceList.begin(); brushPlane!=faceList.end(); brushPlane++)
325 if(**brushPlane == *chkPlane)
331 bool DBrush::IsCutByPlane(DPlane *cuttingPlane)
335 if(pointList.size() == 0)
336 if(BuildPoints() == 0)
339 list<DPoint *>::const_iterator chkPnt = pointList.begin();
341 if(chkPnt == pointList.end())
344 float dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);
346 if(dist > MAX_ROUND_ERROR)
348 else if(dist < MAX_ROUND_ERROR)
353 for(chkPnt++=pointList.begin(); chkPnt!=pointList.end(); chkPnt++)
355 dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);
357 if(dist > MAX_ROUND_ERROR)
362 else if(dist < MAX_ROUND_ERROR)
374 scene::Node* DBrush::BuildInRadiant(bool allowDestruction, int* changeCnt, scene::Node* entity)
380 for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
382 if((*chkPlane)->m_bChkOk)
392 //+djbob: fixed bug when brush had no faces "phantom brush" in radiant.
393 if(faceList.size() < 4)
395 Sys_Printf("Possible Phantom Brush Found, will not rebuild\n");
400 NodePtr node(Brush_AllocNode());
402 for(list<DPlane *>::const_iterator buildPlane=faceList.begin(); buildPlane!=faceList.end(); buildPlane++) {
403 if((*buildPlane)->AddToBrush(node->m_brush) && changeCnt) {
409 entity->m_traverse->insert(node);
411 GetWorldspawn()->m_traverse->insert(node);
417 void DBrush::CutByPlane(DPlane *cutPlane, DBrush **newBrush1, DBrush **newBrush2)
419 if(!IsCutByPlane(cutPlane))
426 DBrush* b1 = new DBrush;
427 DBrush* b2 = new DBrush;
429 for(list<DPlane *>::const_iterator parsePlane=faceList.begin(); parsePlane!=faceList.end(); parsePlane++)
431 b1->AddFace((*parsePlane)->points[0], (*parsePlane)->points[1], (*parsePlane)->points[2], NULL);
432 b2->AddFace((*parsePlane)->points[0], (*parsePlane)->points[1], (*parsePlane)->points[2], NULL);
435 b1->AddFace(cutPlane->points[0], cutPlane->points[1], cutPlane->points[2], NULL);
436 b2->AddFace(cutPlane->points[2], cutPlane->points[1], cutPlane->points[0], NULL);
438 b1->RemoveRedundantPlanes();
439 b2->RemoveRedundantPlanes();
445 bool DBrush::IntersectsWith(DBrush *chkBrush)
447 if(pointList.size() == 0)
448 if(BuildPoints() == 0)
449 return FALSE; // invalid brush!!!!
451 if(chkBrush->pointList.size() == 0)
452 if(chkBrush->BuildPoints() == 0)
453 return FALSE; // invalid brush!!!!
455 if(!BBoxCollision(chkBrush))
458 list<DPlane *>::const_iterator iplPlane;
460 for( iplPlane=faceList.begin(); iplPlane!=faceList.end(); iplPlane++)
463 bool allInFront = TRUE;
464 for(list<DPoint *>::const_iterator iPoint=chkBrush->pointList.begin(); iPoint!=chkBrush->pointList.end(); iPoint++)
466 if((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR)
476 for( iplPlane=chkBrush->faceList.begin(); iplPlane!=chkBrush->faceList.end(); iplPlane++)
478 bool allInFront = TRUE;
479 for(list<DPoint *>::const_iterator iPoint=pointList.begin(); iPoint!=pointList.end(); iPoint++)
481 if((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR)
494 bool DBrush::IntersectsWith(DPlane* p1, DPlane* p2, vec3_t v) {
495 vec3_t vDown = { 0, 0, -1 };
497 list<DPlane *>::const_iterator iplPlane;
498 for( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++) {
499 DPlane* p = (*iplPlane);
501 vec_t d = DotProduct( p->normal, vDown );
505 if(p->PlaneIntersection(p1, p2, v)) {
506 if(PointPosition( v ) != POINT_OUT_BRUSH) {
515 void DBrush::BuildBounds()
519 if(pointList.size() == 0) // if points may not have been built, build them
520 if(BuildPoints() == 0)
523 list<DPoint *>::const_iterator first = pointList.begin();
524 VectorCopy((*first)->_pnt, bbox_min);
525 VectorCopy((*first)->_pnt, bbox_max);
527 list<DPoint *>::const_iterator point=pointList.begin();
528 for( point++; point!=pointList.end(); point++)
530 if((*point)->_pnt[0] > bbox_max[0])
531 bbox_max[0] = (*point)->_pnt[0];
532 if((*point)->_pnt[1] > bbox_max[1])
533 bbox_max[1] = (*point)->_pnt[1];
534 if((*point)->_pnt[2] > bbox_max[2])
535 bbox_max[2] = (*point)->_pnt[2];
537 if((*point)->_pnt[0] < bbox_min[0])
538 bbox_min[0] = (*point)->_pnt[0];
539 if((*point)->_pnt[1] < bbox_min[1])
540 bbox_min[1] = (*point)->_pnt[1];
541 if((*point)->_pnt[2] < bbox_min[2])
542 bbox_min[2] = (*point)->_pnt[2];
549 bool DBrush::BBoxTouch(DBrush *chkBrush)
554 GetBounds(min1, max1);
555 chkBrush->GetBounds(min2, max2);
557 if((min1[0] - max2[0]) > MAX_ROUND_ERROR)
559 if((min1[1] - max2[1]) > MAX_ROUND_ERROR)
561 if((min1[2] - max2[2]) > MAX_ROUND_ERROR)
564 if((min2[0] - max1[0]) > MAX_ROUND_ERROR)
566 if((min2[1] - max1[1]) > MAX_ROUND_ERROR)
568 if((min2[2] - max1[2]) > MAX_ROUND_ERROR)
573 if((min2[0] - max1[0]) == 0)
576 if((min2[1] - max1[1]) == 0)
579 if((min2[2] - max1[2]) == 0)
582 if((min1[0] - max2[0]) == 0)
585 if((min1[1] - max2[1]) == 0)
588 if((min1[2] - max2[2]) == 0)
597 void DBrush::ResetChecks(list<Str>* exclusionList)
599 for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
605 for(list<Str>::iterator eTexture = exclusionList->begin(); eTexture != exclusionList->end(); eTexture++)
607 if(strstr((*resetPlane)->texInfo.m_texdef.GetName(), eTexture->GetBuffer()))
615 (*resetPlane)->m_bChkOk = set;
619 DPlane* DBrush::HasPlaneInverted(DPlane *chkPlane)
621 for(list<DPlane *>::const_iterator brushPlane=faceList.begin(); brushPlane!=faceList.end(); brushPlane++)
623 if(**brushPlane != *chkPlane)
625 if(fabs((*brushPlane)->_d + chkPlane->_d) < 0.1)
626 return (*brushPlane);
632 bool DBrush::HasTexture(const char *textureName)
634 for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
636 if(strstr((*chkPlane)->texInfo.m_texdef.GetName(), textureName))
643 bool DBrush::IsDetail()
645 for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
647 if((*chkPlane)->texInfo.m_texdef.contents & FACE_DETAIL)
654 void DBrush::BuildFromWinding(DWinding *w)
658 Sys_ERROR("Winding has invalid number of points");
662 DPlane* wPlane = w->WindingPlane();
665 w2 = w->CopyWinding();
667 for(i = 0; i < w2->numpoints; i++)
668 VectorAdd(w2->p[i], wPlane->normal, w2->p[i]);
670 AddFace(w2->p[0], w2->p[1], w2->p[2], NULL);
671 AddFace(w->p[2], w->p[1], w->p[0], NULL);
673 for(i = 0; i < w->numpoints-1; i++)
674 AddFace(w2->p[i], w->p[i], w->p[i+1], NULL);
675 AddFace(w2->p[w->numpoints-1], w->p[w->numpoints-1], w->p[0], NULL);
681 void DBrush::SaveToFile(FILE *pFile)
683 fprintf(pFile, "{\n");
685 for(list<DPlane *>::const_iterator pp=faceList.begin(); pp!=faceList.end(); pp++)
689 sprintf(buffer, "( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) %s %.0f %.0f %f %f %.0f 0 0 0\n",
690 (*pp)->points[0][0], (*pp)->points[0][1], (*pp)->points[0][2],
691 (*pp)->points[1][0], (*pp)->points[1][1], (*pp)->points[1][2],
692 (*pp)->points[2][0], (*pp)->points[2][1], (*pp)->points[2][2],
693 (*pp)->texInfo.m_texdef.GetName(),
694 (*pp)->texInfo.m_texdef.shift[0], (*pp)->texInfo.m_texdef.shift[1],
695 (*pp)->texInfo.m_texdef.scale[0], (*pp)->texInfo.m_texdef.scale[0],
696 (*pp)->texInfo.m_texdef.rotate);
698 fprintf(pFile, buffer);
701 fprintf(pFile, "}\n");
704 void DBrush::Rotate(vec3_t vOrigin, vec3_t vRotation)
706 for(list<DPlane *>::const_iterator rotPlane=faceList.begin(); rotPlane!=faceList.end(); rotPlane++)
708 for(int i = 0; i < 3; i++)
709 VectorRotate((*rotPlane)->points[i], vRotation, vOrigin);
711 (*rotPlane)->Rebuild();
715 void DBrush::RotateAboutCentre(vec3_t vRotation)
717 vec3_t min, max, centre;
719 VectorAdd(min, max, centre);
720 VectorScale(centre, 0.5f, centre);
722 Rotate(centre, vRotation);
725 bool DBrush::ResetTextures(const char* textureName, float fScale[2], float fShift[2], int rotation, const char* newTextureName,
726 int bResetTextureName, int bResetScale[2], int bResetShift[2], int bResetRotation)
730 bool changed = FALSE;
731 for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
733 if(!strcmp((*resetPlane)->texInfo.m_texdef.GetName(), textureName))
735 if(bResetTextureName)
736 (*resetPlane)->texInfo.m_texdef.SetName(newTextureName);
739 (*resetPlane)->texInfo.m_texdef.scale[0] = fScale[0];
741 (*resetPlane)->texInfo.m_texdef.scale[1] = fScale[1];
744 (*resetPlane)->texInfo.m_texdef.shift[0] = fShift[0];
746 (*resetPlane)->texInfo.m_texdef.shift[1] = fShift[1];
749 (*resetPlane)->texInfo.m_texdef.rotate = (float)rotation;
754 return changed; // no point rebuilding unless we need to, only slows things down
758 for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
760 if(bResetTextureName)
761 (*resetPlane)->texInfo.m_texdef.SetName(newTextureName);
764 (*resetPlane)->texInfo.m_texdef.scale[0] = fScale[0];
766 (*resetPlane)->texInfo.m_texdef.scale[1] = fScale[1];
769 (*resetPlane)->texInfo.m_texdef.shift[0] = fShift[0];
771 (*resetPlane)->texInfo.m_texdef.shift[1] = fShift[1];
774 (*resetPlane)->texInfo.m_texdef.rotate = (float)rotation;
780 bool DBrush::operator ==(DBrush* other)
782 list<DPlane *>::const_iterator chkPlane;
784 for(chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
786 if(!other->HasPlane((*chkPlane)))
790 for(chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
792 if(!HasPlane((*chkPlane)))
799 DPlane* DBrush::AddFace(vec3_t va, vec3_t vb, vec3_t vc, const char *textureName, bool bDetail)
801 bBoundsBuilt = FALSE;
802 DPlane* newFace = new DPlane(va, vb, vc, textureName, bDetail);
803 faceList.push_back(newFace);
808 DPlane* DBrush::FindPlaneWithClosestNormal( vec_t* normal ) {
810 DPlane* bestDotPlane = NULL;
811 list<DPlane *>::const_iterator chkPlane;
812 for( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ ) {
813 DPlane* pPlane = (*chkPlane);
815 vec_t dot = DotProduct( pPlane->normal, normal );
816 if( dot > bestDot ) {
818 bestDotPlane = pPlane;
825 int DBrush::FindPointsForPlane( DPlane* plane, DPoint** pnts, int maxpnts ) {
834 for( list<DPoint *>::const_iterator points = pointList.begin(); points != pointList.end(); points++ ) {
835 DPoint* point = (*points);
837 if( fabs(plane->DistanceToPoint( point->_pnt )) < MAX_ROUND_ERROR ) {
838 pnts[numpnts] = point;
841 if(numpnts >= maxpnts) {
851 void DBrush::RemovePlane( DPlane* plane ) {
852 bBoundsBuilt = FALSE;
853 for( list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++ ) {
854 if(*deadPlane == plane) {
856 faceList.remove( plane );
861 void DBrush::RemoveFromRadiant( void ) {
864 g_FuncTable.m_pfnDeleteBrushHandle(QER_brush);