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 //////////////////////////////////////////////////////////////////////
25 #include "globaldefs.h"
27 #if GDEF_COMPILER_MSVC
28 #pragma warning(disable : 4786)
41 #include "dialogs/dialogs-gtk.h"
47 #include "generic/referencecounted.h"
51 //////////////////////////////////////////////////////////////////////
52 // Construction/Destruction
53 //////////////////////////////////////////////////////////////////////
55 DBrush::DBrush(int ID)
69 //////////////////////////////////////////////////////////////////////
71 //////////////////////////////////////////////////////////////////////
73 DPlane *DBrush::AddFace(const vec3_t va, const vec3_t vb, const vec3_t vc, const _QERFaceData *texData)
76 // 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]);
79 DPlane *newFace = new DPlane(va, vb, vc, texData);
80 faceList.push_back(newFace);
85 int DBrush::BuildPoints()
89 if (faceList.size() <= 3) { // if less than 3 faces, there can be no points
90 return 0; // with only 3 faces u can't have a bounded soild
93 for (std::list<DPlane *>::const_iterator p1 = faceList.begin(); p1 != faceList.end(); p1++) {
94 std::list<DPlane *>::const_iterator p2 = p1;
95 for (p2++; p2 != faceList.end(); p2++) {
96 std::list<DPlane *>::const_iterator p3 = p2;
97 for (p3++; p3 != faceList.end(); p3++) {
99 if ((*p1)->PlaneIntersection(*p2, *p3, pnt)) {
100 int pos = PointPosition(pnt);
102 if (pos == POINT_IN_BRUSH) { // ???? shouldn't happen here
103 globalErrorStream() << "ERROR:: Build Brush Points: Point IN brush!!!\n";
104 } else if (pos == POINT_ON_BRUSH) { // normal point
105 if (!HasPoint(pnt)) {
109 Sys_Printf("Duplicate Point Found, pyramids ahoy!!!!!\n");*/
110 // point lies on more that 3 planes
113 // otherwise point is removed due to another plane..
115 // Sys_Printf("(%f, %f, %f)\n", pnt[0], pnt[1], pnt[2]);
122 // Sys_Printf("%i points on brush\n", pointList.size());
125 return static_cast<int>( pointList.size());
128 void DBrush_addFace(DBrush &brush, const _QERFaceData &faceData)
130 brush.AddFace(vector3_to_array(faceData.m_p0), vector3_to_array(faceData.m_p1), vector3_to_array(faceData.m_p2), 0);
133 typedef ReferenceCaller<DBrush, void(const _QERFaceData &), DBrush_addFace> DBrushAddFaceCaller;
135 void DBrush_addFaceTextured(DBrush &brush, const _QERFaceData &faceData)
137 brush.AddFace(vector3_to_array(faceData.m_p0), vector3_to_array(faceData.m_p1), vector3_to_array(faceData.m_p2),
141 typedef ReferenceCaller<DBrush, void(const _QERFaceData &), DBrush_addFaceTextured> DBrushAddFaceTexturedCaller;
143 void DBrush::LoadFromBrush(scene::Instance &brush, bool textured)
148 GlobalBrushCreator().Brush_forEachFace(brush.path().top(),
149 textured ? BrushFaceDataCallback(DBrushAddFaceTexturedCaller(*this))
150 : BrushFaceDataCallback(DBrushAddFaceCaller(*this)));
152 QER_entity = brush.path().parent().get_pointer();
153 QER_brush = brush.path().top().get_pointer();
156 int DBrush::PointPosition(vec3_t pnt)
158 int state = POINT_IN_BRUSH; // if nothing happens point is inside brush
160 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
168 // or outside it, it can no longer be in it
174 void DBrush::ClearPoints()
176 for (std::list<DPoint *>::const_iterator deadPoint = pointList.begin(); deadPoint != pointList.end(); deadPoint++) {
182 void DBrush::ClearFaces()
184 bBoundsBuilt = false;
185 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++) {
201 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()) {
218 std::list<DPlane *>::iterator p2 = p1;
220 for (p2++; p2 != faceList.end(); p2++) {
222 if (!strcmp((*p1)->m_shader.c_str(), "textures/common/caulk")) {
224 p1 = faceList.erase(p1); // duplicate plane
227 p2 = faceList.erase(p2); // duplicate plane
235 if (p2 == faceList.end()) {
240 //+djbob kill planes with bad normal, they are more of a nuisance than losing a brush
241 chkPlane = faceList.begin();
242 while (chkPlane != faceList.end()) {
243 if (VectorLength((*chkPlane)->normal) == 0) { // plane has bad normal
245 chkPlane = faceList.erase(chkPlane);
253 if (pointList.size() == 0) { // if points may not have been built, build them
254 /* if(BuildPoints() == 0) // just let the planes die if they are all bad
259 chkPlane = faceList.begin();
260 while (chkPlane != faceList.end()) {
261 if ((*chkPlane)->IsRedundant(pointList)) { // checks that plane "0wnz" :), 3 or more points
263 chkPlane = faceList.erase(chkPlane);
273 bool DBrush::GetBounds(vec3_t min, vec3_t max)
281 VectorCopy(bbox_min, min);
282 VectorCopy(bbox_max, max);
287 bool DBrush::BBoxCollision(DBrush *chkBrush)
292 GetBounds(min1, max1);
293 chkBrush->GetBounds(min2, max2);
295 if (min1[0] >= max2[0]) {
298 if (min1[1] >= max2[1]) {
301 if (min1[2] >= max2[2]) {
305 if (max1[0] <= min2[0]) {
308 if (max1[1] <= min2[1]) {
311 if (max1[2] <= min2[2]) {
318 DPlane *DBrush::HasPlane(DPlane *chkPlane)
320 for (std::list<DPlane *>::const_iterator brushPlane = faceList.begin();
321 brushPlane != faceList.end(); brushPlane++) {
322 if (**brushPlane == *chkPlane) {
329 bool DBrush::IsCutByPlane(DPlane *cuttingPlane)
333 if (pointList.size() == 0) {
334 if (BuildPoints() == 0) {
339 std::list<DPoint *>::const_iterator chkPnt = pointList.begin();
341 if (chkPnt == pointList.end()) {
345 float dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);
347 if (dist > MAX_ROUND_ERROR) {
349 } else if (dist < MAX_ROUND_ERROR) {
355 for (chkPnt++ = pointList.begin(); chkPnt != pointList.end(); chkPnt++) {
356 dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);
358 if (dist > MAX_ROUND_ERROR) {
362 } else if (dist < MAX_ROUND_ERROR) {
375 scene::Node *DBrush::BuildInRadiant(bool allowDestruction, int *changeCnt, scene::Node *entity)
377 if (allowDestruction) {
380 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) {
393 globalErrorStream() << "Possible Phantom Brush Found, will not rebuild\n";
398 NodeSmartReference node(GlobalBrushCreator().createBrush());
400 for (std::list<DPlane *>::const_iterator buildPlane = faceList.begin();
401 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)) {
427 DBrush *b1 = new DBrush;
428 DBrush *b2 = new DBrush;
430 for (std::list<DPlane *>::const_iterator parsePlane = faceList.begin();
431 parsePlane != faceList.end(); parsePlane++) {
432 b1->AddFace((*parsePlane)->points[0], (*parsePlane)->points[1], (*parsePlane)->points[2], NULL);
433 b2->AddFace((*parsePlane)->points[0], (*parsePlane)->points[1], (*parsePlane)->points[2], NULL);
436 b1->AddFace(cutPlane->points[0], cutPlane->points[1], cutPlane->points[2], NULL);
437 b2->AddFace(cutPlane->points[2], cutPlane->points[1], cutPlane->points[0], NULL);
439 b1->RemoveRedundantPlanes();
440 b2->RemoveRedundantPlanes();
446 bool DBrush::IntersectsWith(DBrush *chkBrush)
448 if (pointList.size() == 0) {
449 if (BuildPoints() == 0) {
450 return false; // invalid brush!!!!
454 if (chkBrush->pointList.size() == 0) {
455 if (chkBrush->BuildPoints() == 0) {
456 return false; // invalid brush!!!!
460 if (!BBoxCollision(chkBrush)) {
464 std::list<DPlane *>::const_iterator iplPlane;
466 for (iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++) {
468 bool allInFront = true;
469 for (std::list<DPoint *>::const_iterator iPoint = chkBrush->pointList.begin();
470 iPoint != chkBrush->pointList.end(); iPoint++) {
471 if ((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR) {
481 for (iplPlane = chkBrush->faceList.begin(); iplPlane != chkBrush->faceList.end(); iplPlane++) {
482 bool allInFront = true;
483 for (std::list<DPoint *>::const_iterator iPoint = pointList.begin(); iPoint != pointList.end(); iPoint++) {
484 if ((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR) {
497 bool DBrush::IntersectsWith(DPlane *p1, DPlane *p2, vec3_t v)
499 vec3_t vDown = {0, 0, -1};
501 std::list<DPlane *>::const_iterator iplPlane;
502 for (iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++) {
503 DPlane *p = (*iplPlane);
505 vec_t d = DotProduct(p->normal, vDown);
509 if (p->PlaneIntersection(p1, p2, v)) {
510 if (PointPosition(v) != POINT_OUT_BRUSH) {
519 void DBrush::BuildBounds()
522 if (pointList.size() == 0) { // if points may not have been built, build them
523 if (BuildPoints() == 0) {
528 std::list<DPoint *>::const_iterator first = pointList.begin();
529 VectorCopy((*first)->_pnt, bbox_min);
530 VectorCopy((*first)->_pnt, bbox_max);
532 std::list<DPoint *>::const_iterator point = pointList.begin();
533 for (point++; point != pointList.end(); point++) {
534 if ((*point)->_pnt[0] > bbox_max[0]) {
535 bbox_max[0] = (*point)->_pnt[0];
537 if ((*point)->_pnt[1] > bbox_max[1]) {
538 bbox_max[1] = (*point)->_pnt[1];
540 if ((*point)->_pnt[2] > bbox_max[2]) {
541 bbox_max[2] = (*point)->_pnt[2];
544 if ((*point)->_pnt[0] < bbox_min[0]) {
545 bbox_min[0] = (*point)->_pnt[0];
547 if ((*point)->_pnt[1] < bbox_min[1]) {
548 bbox_min[1] = (*point)->_pnt[1];
550 if ((*point)->_pnt[2] < bbox_min[2]) {
551 bbox_min[2] = (*point)->_pnt[2];
559 bool DBrush::BBoxTouch(DBrush *chkBrush)
564 GetBounds(min1, max1);
565 chkBrush->GetBounds(min2, max2);
567 if ((min1[0] - max2[0]) > MAX_ROUND_ERROR) {
570 if ((min1[1] - max2[1]) > MAX_ROUND_ERROR) {
573 if ((min1[2] - max2[2]) > MAX_ROUND_ERROR) {
577 if ((min2[0] - max1[0]) > MAX_ROUND_ERROR) {
580 if ((min2[1] - max1[1]) > MAX_ROUND_ERROR) {
583 if ((min2[2] - max1[2]) > MAX_ROUND_ERROR) {
589 if ((min2[0] - max1[0]) == 0) {
593 if ((min2[1] - max1[1]) == 0) {
597 if ((min2[2] - max1[2]) == 0) {
601 if ((min1[0] - max2[0]) == 0) {
605 if ((min1[1] - max2[1]) == 0) {
609 if ((min1[2] - max2[2]) == 0) {
620 void DBrush::ResetChecks(std::list<Str> *exclusionList)
622 for (std::list<DPlane *>::const_iterator resetPlane = faceList.begin();
623 resetPlane != faceList.end(); resetPlane++) {
627 for (std::list<Str>::iterator eTexture = exclusionList->begin();
628 eTexture != exclusionList->end(); eTexture++) {
629 if (strstr((*resetPlane)->m_shader.c_str(), eTexture->GetBuffer())) {
636 (*resetPlane)->m_bChkOk = set;
640 DPlane *DBrush::HasPlaneInverted(DPlane *chkPlane)
642 for (std::list<DPlane *>::const_iterator brushPlane = faceList.begin();
643 brushPlane != faceList.end(); brushPlane++) {
644 if (**brushPlane != *chkPlane) {
645 if (fabs((*brushPlane)->_d + chkPlane->_d) < 0.1) {
646 return (*brushPlane);
653 bool DBrush::HasTexture(const char *textureName)
655 for (std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++) {
656 if (strstr((*chkPlane)->m_shader.c_str(), textureName)) {
664 bool DBrush::IsDetail()
666 for (std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++) {
667 if ((*chkPlane)->texInfo.contents & FACE_DETAIL) {
675 void DBrush::BuildFromWinding(DWinding *w)
677 if (w->numpoints < 3) {
678 globalErrorStream() << "Winding has invalid number of points";
682 DPlane *wPlane = w->WindingPlane();
685 w2 = w->CopyWinding();
687 for (i = 0; i < w2->numpoints; i++)
688 VectorAdd(w2->p[i], wPlane->normal, w2->p[i]);
690 AddFace(w2->p[0], w2->p[1], w2->p[2], NULL);
691 AddFace(w->p[2], w->p[1], w->p[0], NULL);
693 for (i = 0; i < w->numpoints - 1; i++) {
694 AddFace(w2->p[i], w->p[i], w->p[i + 1], NULL);
696 AddFace(w2->p[w->numpoints - 1], w->p[w->numpoints - 1], w->p[0], NULL);
702 void DBrush::SaveToFile(FILE *pFile)
704 fprintf(pFile, "{\n");
706 for (std::list<DPlane *>::const_iterator pp = faceList.begin(); pp != faceList.end(); pp++) {
709 sprintf(buffer, "( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) %s %.0f %.0f %f %f %.0f 0 0 0\n",
710 (*pp)->points[0][0], (*pp)->points[0][1], (*pp)->points[0][2],
711 (*pp)->points[1][0], (*pp)->points[1][1], (*pp)->points[1][2],
712 (*pp)->points[2][0], (*pp)->points[2][1], (*pp)->points[2][2],
713 (*pp)->m_shader.c_str(),
714 (*pp)->texInfo.m_texdef.shift[0], (*pp)->texInfo.m_texdef.shift[1],
715 (*pp)->texInfo.m_texdef.scale[0], (*pp)->texInfo.m_texdef.scale[0],
716 (*pp)->texInfo.m_texdef.rotate);
718 fprintf(pFile, "%s", buffer);
721 fprintf(pFile, "}\n");
724 void DBrush::Rotate(vec3_t vOrigin, vec3_t vRotation)
726 for (std::list<DPlane *>::const_iterator rotPlane = faceList.begin(); rotPlane != faceList.end(); rotPlane++) {
727 for (int i = 0; i < 3; i++) {
728 VectorRotate((*rotPlane)->points[i], vRotation, vOrigin);
731 (*rotPlane)->Rebuild();
735 void DBrush::RotateAboutCentre(vec3_t vRotation)
737 vec3_t min, max, centre;
739 VectorAdd(min, max, centre);
740 VectorScale(centre, 0.5f, centre);
742 Rotate(centre, vRotation);
745 bool DBrush::ResetTextures(const char *textureName, float fScale[2], float fShift[2], int rotation,
746 const char *newTextureName,
747 int bResetTextureName, int bResetScale[2], int bResetShift[2], int bResetRotation)
750 bool changed = false;
751 for (std::list<DPlane *>::const_iterator resetPlane = faceList.begin();
752 resetPlane != faceList.end(); resetPlane++) {
753 if (!strcmp((*resetPlane)->m_shader.c_str(), textureName)) {
754 if (bResetTextureName) {
755 (*resetPlane)->m_shader = newTextureName;
758 if (bResetScale[0]) {
759 (*resetPlane)->texInfo.m_texdef.scale[0] = fScale[0];
761 if (bResetScale[1]) {
762 (*resetPlane)->texInfo.m_texdef.scale[1] = fScale[1];
765 if (bResetShift[0]) {
766 (*resetPlane)->texInfo.m_texdef.shift[0] = fShift[0];
768 if (bResetShift[1]) {
769 (*resetPlane)->texInfo.m_texdef.shift[1] = fShift[1];
772 if (bResetRotation) {
773 (*resetPlane)->texInfo.m_texdef.rotate = (float) rotation;
779 return changed; // no point rebuilding unless we need to, only slows things down
781 for (std::list<DPlane *>::const_iterator resetPlane = faceList.begin();
782 resetPlane != faceList.end(); resetPlane++) {
783 if (bResetTextureName) {
784 (*resetPlane)->m_shader = newTextureName;
787 if (bResetScale[0]) {
788 (*resetPlane)->texInfo.m_texdef.scale[0] = fScale[0];
790 if (bResetScale[1]) {
791 (*resetPlane)->texInfo.m_texdef.scale[1] = fScale[1];
794 if (bResetShift[0]) {
795 (*resetPlane)->texInfo.m_texdef.shift[0] = fShift[0];
797 if (bResetShift[1]) {
798 (*resetPlane)->texInfo.m_texdef.shift[1] = fShift[1];
801 if (bResetRotation) {
802 (*resetPlane)->texInfo.m_texdef.rotate = (float) rotation;
809 bool DBrush::operator==(DBrush *other)
811 std::list<DPlane *>::const_iterator chkPlane;
813 for (chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++) {
814 if (!other->HasPlane((*chkPlane))) {
819 for (chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++) {
820 if (!HasPlane((*chkPlane))) {
828 DPlane *DBrush::AddFace(const vec3_t va, const vec3_t vb, const vec3_t vc, const char *textureName, bool bDetail)
830 bBoundsBuilt = false;
831 DPlane *newFace = new DPlane(va, vb, vc, textureName, bDetail);
832 faceList.push_back(newFace);
837 DPlane *DBrush::FindPlaneWithClosestNormal(vec_t *normal)
840 DPlane *bestDotPlane = NULL;
841 std::list<DPlane *>::const_iterator chkPlane;
842 for (chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++) {
843 DPlane *pPlane = (*chkPlane);
845 vec_t dot = DotProduct(pPlane->normal, normal);
848 bestDotPlane = pPlane;
855 int DBrush::FindPointsForPlane(DPlane *plane, DPoint **pnts, int maxpnts)
865 for (std::list<DPoint *>::const_iterator points = pointList.begin(); points != pointList.end(); points++) {
866 DPoint *point = (*points);
868 if (fabs(plane->DistanceToPoint(point->_pnt)) < MAX_ROUND_ERROR) {
869 pnts[numpnts] = point;
872 if (numpnts >= maxpnts) {
882 void DBrush::RemovePlane(DPlane *plane)
884 bBoundsBuilt = false;
885 for (std::list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++) {
886 if (*deadPlane == plane) {
888 faceList.remove(plane);