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)
40 #include "dialogs/dialogs-gtk.h"
46 #include "generic/referencecounted.h"
50 //////////////////////////////////////////////////////////////////////
51 // Construction/Destruction
52 //////////////////////////////////////////////////////////////////////
54 DBrush::DBrush(int ID)
68 //////////////////////////////////////////////////////////////////////
70 //////////////////////////////////////////////////////////////////////
72 DPlane* DBrush::AddFace(const vec3_t va, const vec3_t vb, const vec3_t vc, const _QERFaceData* texData)
75 // 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]);
78 DPlane* newFace = new DPlane(va, vb, vc, texData);
79 faceList.push_back(newFace);
84 int DBrush::BuildPoints()
88 if(faceList.size() <= 3) // if less than 3 faces, there can be no points
89 return 0; // with only 3 faces u can't have a bounded soild
91 for(std::list<DPlane *>::const_iterator p1=faceList.begin(); p1!=faceList.end(); p1++)
93 std::list<DPlane *>::const_iterator p2=p1;
94 for(p2++; p2!=faceList.end(); p2++)
96 std::list<DPlane *>::const_iterator p3=p2;
97 for(p3++; p3!=faceList.end(); p3++)
100 if((*p1)->PlaneIntersection(*p2, *p3, pnt))
102 int pos = PointPosition(pnt);
104 if(pos == POINT_IN_BRUSH)
105 { // ???? shouldn't happen here
106 globalErrorStream() << "ERROR:: Build Brush Points: Point IN brush!!!\n";
108 else if(pos == POINT_ON_BRUSH)
113 Sys_Printf("Duplicate Point Found, pyramids ahoy!!!!!\n");*/
114 // point lies on more that 3 planes
117 // otherwise point is removed due to another plane..
119 // Sys_Printf("(%f, %f, %f)\n", pnt[0], pnt[1], pnt[2]);
126 // Sys_Printf("%i points on brush\n", pointList.size());
129 return pointList.size();
132 void DBrush_addFace(DBrush& brush, const _QERFaceData& faceData)
134 brush.AddFace(vector3_to_array(faceData.m_p0), vector3_to_array(faceData.m_p1), vector3_to_array(faceData.m_p2), 0);
136 typedef ReferenceCaller1<DBrush, const _QERFaceData&, DBrush_addFace> DBrushAddFaceCaller;
138 void DBrush_addFaceTextured(DBrush& brush, const _QERFaceData& faceData)
140 brush.AddFace(vector3_to_array(faceData.m_p0), vector3_to_array(faceData.m_p1), vector3_to_array(faceData.m_p2), &faceData);
142 typedef ReferenceCaller1<DBrush, const _QERFaceData&, DBrush_addFaceTextured> DBrushAddFaceTexturedCaller;
144 void DBrush::LoadFromBrush(scene::Instance& brush, bool textured)
149 GlobalBrushCreator().Brush_forEachFace(brush.path().top(), textured ? BrushFaceDataCallback(DBrushAddFaceTexturedCaller(*this)) : BrushFaceDataCallback(DBrushAddFaceCaller(*this)));
151 QER_entity = brush.path().parent().get_pointer();
152 QER_brush = brush.path().top().get_pointer();
155 int DBrush::PointPosition(vec3_t pnt)
157 int state = POINT_IN_BRUSH; // if nothing happens point is inside brush
159 for(std::list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
161 float dist = (*chkPlane)->DistanceToPoint(pnt);
163 if(dist > MAX_ROUND_ERROR)
164 return POINT_OUT_BRUSH; // if point is in front of plane, it CANT be in the brush
165 else if(fabs(dist) < MAX_ROUND_ERROR)
166 state = POINT_ON_BRUSH; // if point is ON plane point is either ON the brush
167 // or outside it, it can no longer be in it
173 void DBrush::ClearPoints()
175 for(std::list<DPoint *>::const_iterator deadPoint=pointList.begin(); deadPoint!=pointList.end(); deadPoint++) {
181 void DBrush::ClearFaces()
183 bBoundsBuilt = false;
184 for(std::list<DPlane *>::const_iterator deadPlane=faceList.begin(); deadPlane!=faceList.end(); deadPlane++)
191 void DBrush::AddPoint(vec3_t pnt)
193 DPoint* newPoint = new DPoint;
194 VectorCopy(pnt, newPoint->_pnt);
195 pointList.push_back(newPoint);
198 bool DBrush::HasPoint(vec3_t pnt)
200 for(std::list<DPoint *>::const_iterator chkPoint=pointList.begin(); chkPoint!=pointList.end(); chkPoint++)
202 if(**chkPoint == pnt)
209 int DBrush::RemoveRedundantPlanes()
212 std::list<DPlane *>::iterator chkPlane;
214 // find duplicate planes
215 std::list<DPlane *>::iterator p1=faceList.begin();
217 while( p1!=faceList.end() )
219 std::list<DPlane *>::iterator p2 = p1;
221 for(p2++; p2!=faceList.end(); p2++)
225 if(!strcmp((*p1)->m_shader.c_str(), "textures/common/caulk"))
228 p1 = faceList.erase(p1); // duplicate plane
233 p2 = faceList.erase(p2); // duplicate plane
241 if( p2 == faceList.end() )
245 //+djbob kill planes with bad normal, they are more of a nuisance than losing a brush
246 chkPlane=faceList.begin();
247 while( chkPlane!=faceList.end() )
249 if(VectorLength((*chkPlane)->normal) == 0) // plane has bad normal
252 chkPlane = faceList.erase(chkPlane);
260 if(pointList.size() == 0) // if points may not have been built, build them
261 /* if(BuildPoints() == 0) // just let the planes die if they are all bad
265 chkPlane=faceList.begin();
266 while(chkPlane != faceList.end())
268 if((*chkPlane)->IsRedundant(pointList)) // checks that plane "0wnz" :), 3 or more points
271 chkPlane = faceList.erase(chkPlane);
281 bool DBrush::GetBounds(vec3_t min, vec3_t max)
288 VectorCopy(bbox_min, min);
289 VectorCopy(bbox_max, max);
294 bool DBrush::BBoxCollision(DBrush* chkBrush)
299 GetBounds(min1, max1);
300 chkBrush->GetBounds(min2, max2);
302 if(min1[0] >= max2[0])
304 if(min1[1] >= max2[1])
306 if(min1[2] >= max2[2])
309 if(max1[0] <= min2[0])
311 if(max1[1] <= min2[1])
313 if(max1[2] <= min2[2])
319 DPlane* DBrush::HasPlane(DPlane* chkPlane)
321 for(std::list<DPlane *>::const_iterator brushPlane=faceList.begin(); brushPlane!=faceList.end(); brushPlane++)
323 if(**brushPlane == *chkPlane)
329 bool DBrush::IsCutByPlane(DPlane *cuttingPlane)
333 if(pointList.size() == 0)
334 if(BuildPoints() == 0)
337 std::list<DPoint *>::const_iterator chkPnt = pointList.begin();
339 if(chkPnt == pointList.end())
342 float dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);
344 if(dist > MAX_ROUND_ERROR)
346 else if(dist < MAX_ROUND_ERROR)
351 for(chkPnt++=pointList.begin(); chkPnt!=pointList.end(); chkPnt++)
353 dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);
355 if(dist > MAX_ROUND_ERROR)
360 else if(dist < MAX_ROUND_ERROR)
373 scene::Node* DBrush::BuildInRadiant(bool allowDestruction, int* changeCnt, scene::Node* entity)
379 for(std::list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
381 if((*chkPlane)->m_bChkOk)
391 //+djbob: fixed bug when brush had no faces "phantom brush" in radiant.
392 if(faceList.size() < 4)
394 globalErrorStream() << "Possible Phantom Brush Found, will not rebuild\n";
399 NodeSmartReference node(GlobalBrushCreator().createBrush());
401 for(std::list<DPlane *>::const_iterator buildPlane=faceList.begin(); buildPlane!=faceList.end(); buildPlane++) {
402 if((*buildPlane)->AddToBrush(node) && changeCnt) {
408 Node_getTraversable(*entity)->insert(node);
410 Node_getTraversable(GlobalRadiant().getMapWorldEntity())->insert(node);
414 QER_brush = node.get_pointer();
416 return node.get_pointer();
419 void DBrush::CutByPlane(DPlane *cutPlane, DBrush **newBrush1, DBrush **newBrush2)
421 if(!IsCutByPlane(cutPlane))
428 DBrush* b1 = new DBrush;
429 DBrush* b2 = new DBrush;
431 for(std::list<DPlane *>::const_iterator parsePlane=faceList.begin(); parsePlane!=faceList.end(); parsePlane++)
433 b1->AddFace((*parsePlane)->points[0], (*parsePlane)->points[1], (*parsePlane)->points[2], NULL);
434 b2->AddFace((*parsePlane)->points[0], (*parsePlane)->points[1], (*parsePlane)->points[2], NULL);
437 b1->AddFace(cutPlane->points[0], cutPlane->points[1], cutPlane->points[2], NULL);
438 b2->AddFace(cutPlane->points[2], cutPlane->points[1], cutPlane->points[0], NULL);
440 b1->RemoveRedundantPlanes();
441 b2->RemoveRedundantPlanes();
447 bool DBrush::IntersectsWith(DBrush *chkBrush)
449 if(pointList.size() == 0)
450 if(BuildPoints() == 0)
451 return false; // invalid brush!!!!
453 if(chkBrush->pointList.size() == 0)
454 if(chkBrush->BuildPoints() == 0)
455 return false; // invalid brush!!!!
457 if(!BBoxCollision(chkBrush))
460 std::list<DPlane *>::const_iterator iplPlane;
462 for( iplPlane=faceList.begin(); iplPlane!=faceList.end(); iplPlane++)
465 bool allInFront = true;
466 for(std::list<DPoint *>::const_iterator iPoint=chkBrush->pointList.begin(); iPoint!=chkBrush->pointList.end(); iPoint++)
468 if((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR)
478 for( iplPlane=chkBrush->faceList.begin(); iplPlane!=chkBrush->faceList.end(); iplPlane++)
480 bool allInFront = true;
481 for(std::list<DPoint *>::const_iterator iPoint=pointList.begin(); iPoint!=pointList.end(); iPoint++)
483 if((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR)
496 bool DBrush::IntersectsWith(DPlane* p1, DPlane* p2, vec3_t v) {
497 vec3_t vDown = { 0, 0, -1 };
499 std::list<DPlane *>::const_iterator iplPlane;
500 for( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++) {
501 DPlane* p = (*iplPlane);
503 vec_t d = DotProduct( p->normal, vDown );
507 if(p->PlaneIntersection(p1, p2, v)) {
508 if(PointPosition( v ) != POINT_OUT_BRUSH) {
517 void DBrush::BuildBounds()
521 if(pointList.size() == 0) // if points may not have been built, build them
522 if(BuildPoints() == 0)
525 std::list<DPoint *>::const_iterator first = pointList.begin();
526 VectorCopy((*first)->_pnt, bbox_min);
527 VectorCopy((*first)->_pnt, bbox_max);
529 std::list<DPoint *>::const_iterator point=pointList.begin();
530 for( point++; point!=pointList.end(); point++)
532 if((*point)->_pnt[0] > bbox_max[0])
533 bbox_max[0] = (*point)->_pnt[0];
534 if((*point)->_pnt[1] > bbox_max[1])
535 bbox_max[1] = (*point)->_pnt[1];
536 if((*point)->_pnt[2] > bbox_max[2])
537 bbox_max[2] = (*point)->_pnt[2];
539 if((*point)->_pnt[0] < bbox_min[0])
540 bbox_min[0] = (*point)->_pnt[0];
541 if((*point)->_pnt[1] < bbox_min[1])
542 bbox_min[1] = (*point)->_pnt[1];
543 if((*point)->_pnt[2] < bbox_min[2])
544 bbox_min[2] = (*point)->_pnt[2];
551 bool DBrush::BBoxTouch(DBrush *chkBrush)
556 GetBounds(min1, max1);
557 chkBrush->GetBounds(min2, max2);
559 if((min1[0] - max2[0]) > MAX_ROUND_ERROR)
561 if((min1[1] - max2[1]) > MAX_ROUND_ERROR)
563 if((min1[2] - max2[2]) > MAX_ROUND_ERROR)
566 if((min2[0] - max1[0]) > MAX_ROUND_ERROR)
568 if((min2[1] - max1[1]) > MAX_ROUND_ERROR)
570 if((min2[2] - max1[2]) > MAX_ROUND_ERROR)
575 if((min2[0] - max1[0]) == 0)
578 if((min2[1] - max1[1]) == 0)
581 if((min2[2] - max1[2]) == 0)
584 if((min1[0] - max2[0]) == 0)
587 if((min1[1] - max2[1]) == 0)
590 if((min1[2] - max2[2]) == 0)
599 void DBrush::ResetChecks(std::list<Str>* exclusionList)
601 for(std::list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
607 for(std::list<Str>::iterator eTexture = exclusionList->begin(); eTexture != exclusionList->end(); eTexture++)
609 if(strstr((*resetPlane)->m_shader.c_str(), eTexture->GetBuffer()))
617 (*resetPlane)->m_bChkOk = set;
621 DPlane* DBrush::HasPlaneInverted(DPlane *chkPlane)
623 for(std::list<DPlane *>::const_iterator brushPlane=faceList.begin(); brushPlane!=faceList.end(); brushPlane++)
625 if(**brushPlane != *chkPlane)
627 if(fabs((*brushPlane)->_d + chkPlane->_d) < 0.1)
628 return (*brushPlane);
634 bool DBrush::HasTexture(const char *textureName)
636 for(std::list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
638 if(strstr((*chkPlane)->m_shader.c_str(), textureName))
645 bool DBrush::IsDetail()
647 for(std::list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
649 if((*chkPlane)->texInfo.contents & FACE_DETAIL)
656 void DBrush::BuildFromWinding(DWinding *w)
660 globalErrorStream() << "Winding has invalid number of points";
664 DPlane* wPlane = w->WindingPlane();
667 w2 = w->CopyWinding();
669 for(i = 0; i < w2->numpoints; i++)
670 VectorAdd(w2->p[i], wPlane->normal, w2->p[i]);
672 AddFace(w2->p[0], w2->p[1], w2->p[2], NULL);
673 AddFace(w->p[2], w->p[1], w->p[0], NULL);
675 for(i = 0; i < w->numpoints-1; i++)
676 AddFace(w2->p[i], w->p[i], w->p[i+1], NULL);
677 AddFace(w2->p[w->numpoints-1], w->p[w->numpoints-1], w->p[0], NULL);
683 void DBrush::SaveToFile(FILE *pFile)
685 fprintf(pFile, "{\n");
687 for(std::list<DPlane *>::const_iterator pp=faceList.begin(); pp!=faceList.end(); pp++)
691 sprintf(buffer, "( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) %s %.0f %.0f %f %f %.0f 0 0 0\n",
692 (*pp)->points[0][0], (*pp)->points[0][1], (*pp)->points[0][2],
693 (*pp)->points[1][0], (*pp)->points[1][1], (*pp)->points[1][2],
694 (*pp)->points[2][0], (*pp)->points[2][1], (*pp)->points[2][2],
695 (*pp)->m_shader.c_str(),
696 (*pp)->texInfo.m_texdef.shift[0], (*pp)->texInfo.m_texdef.shift[1],
697 (*pp)->texInfo.m_texdef.scale[0], (*pp)->texInfo.m_texdef.scale[0],
698 (*pp)->texInfo.m_texdef.rotate);
700 fprintf(pFile, buffer);
703 fprintf(pFile, "}\n");
706 void DBrush::Rotate(vec3_t vOrigin, vec3_t vRotation)
708 for(std::list<DPlane *>::const_iterator rotPlane=faceList.begin(); rotPlane!=faceList.end(); rotPlane++)
710 for(int i = 0; i < 3; i++)
711 VectorRotate((*rotPlane)->points[i], vRotation, vOrigin);
713 (*rotPlane)->Rebuild();
717 void DBrush::RotateAboutCentre(vec3_t vRotation)
719 vec3_t min, max, centre;
721 VectorAdd(min, max, centre);
722 VectorScale(centre, 0.5f, centre);
724 Rotate(centre, vRotation);
727 bool DBrush::ResetTextures(const char* textureName, float fScale[2], float fShift[2], int rotation, const char* newTextureName,
728 int bResetTextureName, int bResetScale[2], int bResetShift[2], int bResetRotation)
732 bool changed = false;
733 for(std::list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
735 if(!strcmp((*resetPlane)->m_shader.c_str(), textureName))
737 if(bResetTextureName)
738 (*resetPlane)->m_shader = newTextureName;
741 (*resetPlane)->texInfo.m_texdef.scale[0] = fScale[0];
743 (*resetPlane)->texInfo.m_texdef.scale[1] = fScale[1];
746 (*resetPlane)->texInfo.m_texdef.shift[0] = fShift[0];
748 (*resetPlane)->texInfo.m_texdef.shift[1] = fShift[1];
751 (*resetPlane)->texInfo.m_texdef.rotate = (float)rotation;
756 return changed; // no point rebuilding unless we need to, only slows things down
760 for(std::list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
762 if(bResetTextureName)
763 (*resetPlane)->m_shader = newTextureName;
766 (*resetPlane)->texInfo.m_texdef.scale[0] = fScale[0];
768 (*resetPlane)->texInfo.m_texdef.scale[1] = fScale[1];
771 (*resetPlane)->texInfo.m_texdef.shift[0] = fShift[0];
773 (*resetPlane)->texInfo.m_texdef.shift[1] = fShift[1];
776 (*resetPlane)->texInfo.m_texdef.rotate = (float)rotation;
782 bool DBrush::operator ==(DBrush* other)
784 std::list<DPlane *>::const_iterator chkPlane;
786 for(chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
788 if(!other->HasPlane((*chkPlane)))
792 for(chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
794 if(!HasPlane((*chkPlane)))
801 DPlane* DBrush::AddFace(const vec3_t va, const vec3_t vb, const vec3_t vc, const char *textureName, bool bDetail)
803 bBoundsBuilt = false;
804 DPlane* newFace = new DPlane(va, vb, vc, textureName, bDetail);
805 faceList.push_back(newFace);
810 DPlane* DBrush::FindPlaneWithClosestNormal( vec_t* normal ) {
812 DPlane* bestDotPlane = NULL;
813 std::list<DPlane *>::const_iterator chkPlane;
814 for( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ ) {
815 DPlane* pPlane = (*chkPlane);
817 vec_t dot = DotProduct( pPlane->normal, normal );
818 if( dot > bestDot ) {
820 bestDotPlane = pPlane;
827 int DBrush::FindPointsForPlane( DPlane* plane, DPoint** pnts, int maxpnts ) {
836 for( std::list<DPoint *>::const_iterator points = pointList.begin(); points != pointList.end(); points++ ) {
837 DPoint* point = (*points);
839 if( fabs(plane->DistanceToPoint( point->_pnt )) < MAX_ROUND_ERROR ) {
840 pnts[numpnts] = point;
843 if(numpnts >= maxpnts) {
853 void DBrush::RemovePlane( DPlane* plane ) {
854 bBoundsBuilt = false;
855 for( std::list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++ ) {
856 if(*deadPlane == plane) {
858 faceList.remove( plane );