2 BobToolz plugin for GtkRadiant
\r
3 Copyright (C) 2001 Gordon Biggans
\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
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
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
20 // DBrush.cpp: implementation of the DBrush class.
\r
22 //////////////////////////////////////////////////////////////////////
\r
27 #pragma warning(disable : 4786)
\r
31 #include "DWinding.h"
\r
32 #include "dialogs-gtk.h"
\r
36 //////////////////////////////////////////////////////////////////////
\r
37 // Construction/Destruction
\r
38 //////////////////////////////////////////////////////////////////////
\r
40 DBrush::DBrush(int ID)
\r
43 bBoundsBuilt = FALSE;
\r
53 //////////////////////////////////////////////////////////////////////
\r
55 //////////////////////////////////////////////////////////////////////
\r
57 DPlane* DBrush::AddFace(vec3_t va, vec3_t vb, vec3_t vc, _QERFaceData* texData)
\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
62 bBoundsBuilt = FALSE;
\r
63 DPlane* newFace = new DPlane(va, vb, vc, texData);
\r
64 faceList.push_back(newFace);
\r
69 int DBrush::BuildPoints()
\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
76 for(list<DPlane *>::const_iterator p1=faceList.begin(); p1!=faceList.end(); p1++)
\r
78 list<DPlane *>::const_iterator p2=p1;
\r
79 for(p2++; p2!=faceList.end(); p2++)
\r
81 list<DPlane *>::const_iterator p3=p2;
\r
82 for(p3++; p3!=faceList.end(); p3++)
\r
85 if((*p1)->PlaneIntersection(*p2, *p3, pnt))
\r
87 int pos = PointPosition(pnt);
\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
93 else if(pos == POINT_ON_BRUSH)
\r
98 Sys_Printf("Duplicate Point Found, pyramids ahoy!!!!!\n");*/
\r
99 // point lies on more that 3 planes
\r
102 // otherwise point is removed due to another plane..
\r
104 // Sys_Printf("(%f, %f, %f)\n", pnt[0], pnt[1], pnt[2]);
\r
111 // Sys_Printf("%i points on brush\n", pointList.size());
\r
114 return pointList.size();
\r
117 void DBrush::LoadFromBrush_t(brush_t* brush, bool textured)
\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
126 if(faceData == NULL)
\r
127 DoMessageBox("Null pointer returned", "WARNING!", MB_OK);
\r
130 AddFace(faceData->m_v1, faceData->m_v2, faceData->m_v3, faceData);
\r
132 AddFace(faceData->m_v1, faceData->m_v2, faceData->m_v3, NULL);
\r
138 int DBrush::PointPosition(vec3_t pnt)
\r
140 int state = POINT_IN_BRUSH; // if nothing happens point is inside brush
\r
142 for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
\r
144 float dist = (*chkPlane)->DistanceToPoint(pnt);
\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
156 void DBrush::ClearPoints()
\r
158 for(list<DPoint *>::const_iterator deadPoint=pointList.begin(); deadPoint!=pointList.end(); deadPoint++) {
\r
164 void DBrush::ClearFaces()
\r
166 bBoundsBuilt = FALSE;
\r
167 for(list<DPlane *>::const_iterator deadPlane=faceList.begin(); deadPlane!=faceList.end(); deadPlane++)
\r
174 void DBrush::AddPoint(vec3_t pnt)
\r
176 DPoint* newPoint = new DPoint;
\r
177 VectorCopy(pnt, newPoint->_pnt);
\r
178 pointList.push_back(newPoint);
\r
181 bool DBrush::HasPoint(vec3_t pnt)
\r
183 for(list<DPoint *>::const_iterator chkPoint=pointList.begin(); chkPoint!=pointList.end(); chkPoint++)
\r
185 if(**chkPoint == pnt)
\r
192 int DBrush::RemoveRedundantPlanes()
\r
195 list<DPlane *>::iterator chkPlane;
\r
197 // find duplicate planes
\r
198 list<DPlane *>::iterator p1=faceList.begin();
\r
200 while( p1!=faceList.end() )
\r
202 list<DPlane *>::iterator p2 = p1;
\r
204 for(p2++; p2!=faceList.end(); p2++)
\r
208 if(!strcmp((*p1)->texInfo.m_TextureName, "textures/common/caulk"))
\r
211 p1 = faceList.erase(p1); // duplicate plane
\r
216 p2 = faceList.erase(p2); // duplicate plane
\r
224 if( p2 == faceList.end() )
\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
232 if(VectorLength((*chkPlane)->normal) == 0) // plane has bad normal
\r
235 chkPlane = faceList.erase(chkPlane);
\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
248 chkPlane=faceList.begin();
\r
249 while(chkPlane != faceList.end())
\r
251 if((*chkPlane)->IsRedundant(pointList)) // checks that plane "0wnz" :), 3 or more points
\r
254 chkPlane = faceList.erase(chkPlane);
\r
264 bool DBrush::GetBounds(vec3_t min, vec3_t max)
\r
271 VectorCopy(bbox_min, min);
\r
272 VectorCopy(bbox_max, max);
\r
277 bool DBrush::BBoxCollision(DBrush* chkBrush)
\r
282 GetBounds(min1, max1);
\r
283 chkBrush->GetBounds(min2, max2);
\r
285 if(min1[0] >= max2[0])
\r
287 if(min1[1] >= max2[1])
\r
289 if(min1[2] >= max2[2])
\r
292 if(max1[0] <= min2[0])
\r
294 if(max1[1] <= min2[1])
\r
296 if(max1[2] <= min2[2])
\r
302 DPlane* DBrush::HasPlane(DPlane* chkPlane)
\r
304 for(list<DPlane *>::const_iterator brushPlane=faceList.begin(); brushPlane!=faceList.end(); brushPlane++)
\r
306 if(**brushPlane == *chkPlane)
\r
307 return *brushPlane;
\r
312 bool DBrush::IsCutByPlane(DPlane *cuttingPlane)
\r
316 if(pointList.size() == 0)
\r
317 if(BuildPoints() == 0)
\r
320 list<DPoint *>::const_iterator chkPnt = pointList.begin();
\r
322 if(chkPnt == pointList.end())
\r
325 float dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);
\r
327 if(dist > MAX_ROUND_ERROR)
\r
329 else if(dist < MAX_ROUND_ERROR)
\r
334 for(chkPnt++=pointList.begin(); chkPnt!=pointList.end(); chkPnt++)
\r
336 dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);
\r
338 if(dist > MAX_ROUND_ERROR)
\r
343 else if(dist < MAX_ROUND_ERROR)
\r
355 brush_t* DBrush::BuildInRadiant(bool allowDestruction, int* changeCnt, entity_t* entity)
\r
357 if(allowDestruction)
\r
361 for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
\r
363 if((*chkPlane)->m_bChkOk)
\r
373 //+djbob: fixed bug when brush had no faces "phantom brush" in radiant.
\r
374 if(faceList.size() < 4)
\r
376 Sys_Printf("Possible Phantom Brush Found, will not rebuild\n");
\r
381 QER_brush = (brush_t*)g_FuncTable.m_pfnCreateBrushHandle();
\r
383 for(list<DPlane *>::const_iterator buildPlane=faceList.begin(); buildPlane!=faceList.end(); buildPlane++) {
\r
384 if((*buildPlane)->AddToBrush_t(QER_brush) && changeCnt) {
\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
394 g_FuncTable.m_pfnCommitBrushHandle(QER_brush);
\r
400 void DBrush::CutByPlane(DPlane *cutPlane, DBrush **newBrush1, DBrush **newBrush2)
\r
402 if(!IsCutByPlane(cutPlane))
\r
409 DBrush* b1 = new DBrush;
\r
410 DBrush* b2 = new DBrush;
\r
412 for(list<DPlane *>::const_iterator parsePlane=faceList.begin(); parsePlane!=faceList.end(); parsePlane++)
\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
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
421 b1->RemoveRedundantPlanes();
\r
422 b2->RemoveRedundantPlanes();
\r
428 bool DBrush::IntersectsWith(DBrush *chkBrush)
\r
430 if(pointList.size() == 0)
\r
431 if(BuildPoints() == 0)
\r
432 return FALSE; // invalid brush!!!!
\r
434 if(chkBrush->pointList.size() == 0)
\r
435 if(chkBrush->BuildPoints() == 0)
\r
436 return FALSE; // invalid brush!!!!
\r
438 if(!BBoxCollision(chkBrush))
\r
441 list<DPlane *>::const_iterator iplPlane;
\r
443 for( iplPlane=faceList.begin(); iplPlane!=faceList.end(); iplPlane++)
\r
446 bool allInFront = TRUE;
\r
447 for(list<DPoint *>::const_iterator iPoint=chkBrush->pointList.begin(); iPoint!=chkBrush->pointList.end(); iPoint++)
\r
449 if((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR)
\r
451 allInFront = FALSE;
\r
459 for( iplPlane=chkBrush->faceList.begin(); iplPlane!=chkBrush->faceList.end(); iplPlane++)
\r
461 bool allInFront = TRUE;
\r
462 for(list<DPoint *>::const_iterator iPoint=pointList.begin(); iPoint!=pointList.end(); iPoint++)
\r
464 if((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR)
\r
466 allInFront = FALSE;
\r
477 bool DBrush::IntersectsWith(DPlane* p1, DPlane* p2, vec3_t v) {
\r
478 vec3_t vDown = { 0, 0, -1 };
\r
480 list<DPlane *>::const_iterator iplPlane;
\r
481 for( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++) {
\r
482 DPlane* p = (*iplPlane);
\r
484 vec_t d = DotProduct( p->normal, vDown );
\r
488 if(p->PlaneIntersection(p1, p2, v)) {
\r
489 if(PointPosition( v ) != POINT_OUT_BRUSH) {
\r
498 void DBrush::BuildBounds()
\r
502 if(pointList.size() == 0) // if points may not have been built, build them
\r
503 if(BuildPoints() == 0)
\r
506 list<DPoint *>::const_iterator first = pointList.begin();
\r
507 VectorCopy((*first)->_pnt, bbox_min);
\r
508 VectorCopy((*first)->_pnt, bbox_max);
\r
510 list<DPoint *>::const_iterator point=pointList.begin();
\r
511 for( point++; point!=pointList.end(); point++)
\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
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
528 bBoundsBuilt = TRUE;
\r
532 bool DBrush::BBoxTouch(DBrush *chkBrush)
\r
537 GetBounds(min1, max1);
\r
538 chkBrush->GetBounds(min2, max2);
\r
540 if((min1[0] - max2[0]) > MAX_ROUND_ERROR)
\r
542 if((min1[1] - max2[1]) > MAX_ROUND_ERROR)
\r
544 if((min1[2] - max2[2]) > MAX_ROUND_ERROR)
\r
547 if((min2[0] - max1[0]) > MAX_ROUND_ERROR)
\r
549 if((min2[1] - max1[1]) > MAX_ROUND_ERROR)
\r
551 if((min2[2] - max1[2]) > MAX_ROUND_ERROR)
\r
556 if((min2[0] - max1[0]) == 0)
\r
559 if((min2[1] - max1[1]) == 0)
\r
562 if((min2[2] - max1[2]) == 0)
\r
565 if((min1[0] - max2[0]) == 0)
\r
568 if((min1[1] - max2[1]) == 0)
\r
571 if((min1[2] - max2[2]) == 0)
\r
580 void DBrush::ResetChecks(list<Str>* exclusionList)
\r
582 for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
\r
588 for(list<Str>::iterator eTexture = exclusionList->begin(); eTexture != exclusionList->end(); eTexture++)
\r
590 if(strstr((*resetPlane)->texInfo.m_TextureName, eTexture->GetBuffer()))
\r
598 (*resetPlane)->m_bChkOk = set;
\r
602 DPlane* DBrush::HasPlaneInverted(DPlane *chkPlane)
\r
604 for(list<DPlane *>::const_iterator brushPlane=faceList.begin(); brushPlane!=faceList.end(); brushPlane++)
\r
606 if(**brushPlane != *chkPlane)
\r
608 if(fabs((*brushPlane)->_d + chkPlane->_d) < 0.1)
\r
609 return (*brushPlane);
\r
615 bool DBrush::HasTexture(const char *textureName)
\r
617 for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
\r
619 if(strstr((*chkPlane)->texInfo.m_TextureName, textureName))
\r
626 bool DBrush::IsDetail()
\r
628 for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
\r
630 if((*chkPlane)->texInfo.m_nContents & FACE_DETAIL)
\r
637 void DBrush::BuildFromWinding(DWinding *w)
\r
639 if(w->numpoints < 3)
\r
641 Sys_ERROR("Winding has invalid number of points");
\r
645 DPlane* wPlane = w->WindingPlane();
\r
648 w2 = w->CopyWinding();
\r
650 for(i = 0; i < w2->numpoints; i++)
\r
651 VectorAdd(w2->p[i], wPlane->normal, w2->p[i]);
\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
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
664 void DBrush::SaveToFile(FILE *pFile)
\r
666 fprintf(pFile, "{\n");
\r
668 for(list<DPlane *>::const_iterator pp=faceList.begin(); pp!=faceList.end(); pp++)
\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
681 fprintf(pFile, buffer);
\r
684 fprintf(pFile, "}\n");
\r
687 void DBrush::Rotate(vec3_t vOrigin, vec3_t vRotation)
\r
689 for(list<DPlane *>::const_iterator rotPlane=faceList.begin(); rotPlane!=faceList.end(); rotPlane++)
\r
691 for(int i = 0; i < 3; i++)
\r
692 VectorRotate((*rotPlane)->points[i], vRotation, vOrigin);
\r
694 (*rotPlane)->Rebuild();
\r
698 void DBrush::RotateAboutCentre(vec3_t vRotation)
\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
705 Rotate(centre, vRotation);
\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
713 bool changed = FALSE;
\r
714 for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
\r
716 if(!strcmp((*resetPlane)->texInfo.m_TextureName, textureName))
\r
718 if(bResetTextureName)
\r
719 strcpy((*resetPlane)->texInfo.m_TextureName, newTextureName);
\r
722 (*resetPlane)->texInfo.m_fScale[0] = fScale[0];
\r
724 (*resetPlane)->texInfo.m_fScale[1] = fScale[1];
\r
727 (*resetPlane)->texInfo.m_fShift[0] = fShift[0];
\r
729 (*resetPlane)->texInfo.m_fShift[1] = fShift[1];
\r
732 (*resetPlane)->texInfo.m_fRotate = (float)rotation;
\r
737 return changed; // no point rebuilding unless we need to, only slows things down
\r
741 for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
\r
743 if(bResetTextureName)
\r
744 strcpy((*resetPlane)->texInfo.m_TextureName, newTextureName);
\r
747 (*resetPlane)->texInfo.m_fScale[0] = fScale[0];
\r
749 (*resetPlane)->texInfo.m_fScale[1] = fScale[1];
\r
752 (*resetPlane)->texInfo.m_fShift[0] = fShift[0];
\r
754 (*resetPlane)->texInfo.m_fShift[1] = fShift[1];
\r
757 (*resetPlane)->texInfo.m_fRotate = (float)rotation;
\r
763 bool DBrush::operator ==(DBrush* other)
\r
765 list<DPlane *>::const_iterator chkPlane;
\r
767 for(chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
\r
769 if(!other->HasPlane((*chkPlane)))
\r
773 for(chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
\r
775 if(!HasPlane((*chkPlane)))
\r
782 DPlane* DBrush::AddFace(vec3_t va, vec3_t vb, vec3_t vc, const char *textureName, bool bDetail)
\r
784 bBoundsBuilt = FALSE;
\r
785 DPlane* newFace = new DPlane(va, vb, vc, textureName, bDetail);
\r
786 faceList.push_back(newFace);
\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
798 vec_t dot = DotProduct( pPlane->normal, normal );
\r
799 if( dot > bestDot ) {
\r
801 bestDotPlane = pPlane;
\r
805 return bestDotPlane;
\r
808 int DBrush::FindPointsForPlane( DPlane* plane, DPoint** pnts, int maxpnts ) {
\r
817 for( list<DPoint *>::const_iterator points = pointList.begin(); points != pointList.end(); points++ ) {
\r
818 DPoint* point = (*points);
\r
820 if( fabs(plane->DistanceToPoint( point->_pnt )) < MAX_ROUND_ERROR ) {
\r
821 pnts[numpnts] = point;
\r
824 if(numpnts >= maxpnts) {
\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
839 faceList.remove( plane );
\r
844 void DBrush::RemoveFromRadiant( void ) {
\r
846 g_FuncTable.m_pfnDeleteBrushHandle(QER_brush);
\r