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 ){
67 //////////////////////////////////////////////////////////////////////
69 //////////////////////////////////////////////////////////////////////
71 DPlane* DBrush::AddFace( const vec3_t va, const vec3_t vb, const vec3_t vc, const _QERFaceData* texData ){
73 // 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]);
76 DPlane* newFace = new DPlane( va, vb, vc, texData );
77 faceList.push_back( newFace );
82 int DBrush::BuildPoints(){
85 if ( faceList.size() <= 3 ) { // if less than 3 faces, there can be no points
86 return 0; // with only 3 faces u can't have a bounded soild
89 for ( std::list<DPlane *>::const_iterator p1 = faceList.begin(); p1 != faceList.end(); p1++ )
91 std::list<DPlane *>::const_iterator p2 = p1;
92 for ( p2++; p2 != faceList.end(); p2++ )
94 std::list<DPlane *>::const_iterator p3 = p2;
95 for ( p3++; p3 != faceList.end(); p3++ )
98 if ( ( *p1 )->PlaneIntersection( *p2, *p3, pnt ) ) {
99 int pos = PointPosition( pnt );
101 if ( pos == POINT_IN_BRUSH ) { // ???? shouldn't happen here
102 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 ){
129 brush.AddFace( vector3_to_array( faceData.m_p0 ), vector3_to_array( faceData.m_p1 ), vector3_to_array( faceData.m_p2 ), 0 );
131 typedef ReferenceCaller<DBrush, void(const _QERFaceData&), DBrush_addFace> DBrushAddFaceCaller;
133 void DBrush_addFaceTextured( 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 ), &faceData );
136 typedef ReferenceCaller<DBrush, void(const _QERFaceData&), DBrush_addFaceTextured> DBrushAddFaceTexturedCaller;
138 void DBrush::LoadFromBrush( scene::Instance& brush, bool textured ){
142 GlobalBrushCreator().Brush_forEachFace( brush.path().top(), textured ? BrushFaceDataCallback( DBrushAddFaceTexturedCaller( *this ) ) : BrushFaceDataCallback( DBrushAddFaceCaller( *this ) ) );
144 QER_entity = brush.path().parent().get_pointer();
145 QER_brush = brush.path().top().get_pointer();
148 int DBrush::PointPosition( vec3_t pnt ){
149 int state = POINT_IN_BRUSH; // if nothing happens point is inside brush
151 for ( std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
153 float dist = ( *chkPlane )->DistanceToPoint( pnt );
155 if ( dist > MAX_ROUND_ERROR ) {
156 return POINT_OUT_BRUSH; // if point is in front of plane, it CANT be in the brush
158 else if ( fabs( dist ) < MAX_ROUND_ERROR ) {
159 state = POINT_ON_BRUSH; // if point is ON plane point is either ON the brush
161 // or outside it, it can no longer be in it
167 void DBrush::ClearPoints(){
168 for ( std::list<DPoint *>::const_iterator deadPoint = pointList.begin(); deadPoint != pointList.end(); deadPoint++ ) {
174 void DBrush::ClearFaces(){
175 bBoundsBuilt = false;
176 for ( std::list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++ )
183 void DBrush::AddPoint( vec3_t pnt ){
184 DPoint* newPoint = new DPoint;
185 VectorCopy( pnt, newPoint->_pnt );
186 pointList.push_back( newPoint );
189 bool DBrush::HasPoint( vec3_t pnt ){
190 for ( std::list<DPoint *>::const_iterator chkPoint = pointList.begin(); chkPoint != pointList.end(); chkPoint++ )
192 if ( **chkPoint == pnt ) {
200 int DBrush::RemoveRedundantPlanes(){
202 std::list<DPlane *>::iterator chkPlane;
204 // find duplicate planes
205 std::list<DPlane *>::iterator p1 = faceList.begin();
207 while ( p1 != faceList.end() )
209 std::list<DPlane *>::iterator p2 = p1;
211 for ( p2++; p2 != faceList.end(); p2++ )
213 if ( **p1 == **p2 ) {
214 if ( !strcmp( ( *p1 )->m_shader.c_str(), "textures/common/caulk" ) ) {
216 p1 = faceList.erase( p1 ); // duplicate plane
221 p2 = faceList.erase( p2 ); // duplicate plane
229 if ( p2 == faceList.end() ) {
234 //+djbob kill planes with bad normal, they are more of a nuisance than losing a brush
235 chkPlane = faceList.begin();
236 while ( chkPlane != faceList.end() )
238 if ( VectorLength( ( *chkPlane )->normal ) == 0 ) { // plane has bad normal
240 chkPlane = faceList.erase( chkPlane );
249 if ( pointList.size() == 0 ) { // if points may not have been built, build them
250 /* if(BuildPoints() == 0) // just let the planes die if they are all bad
255 chkPlane = faceList.begin();
256 while ( chkPlane != faceList.end() )
258 if ( ( *chkPlane )->IsRedundant( pointList ) ) { // checks that plane "0wnz" :), 3 or more points
260 chkPlane = faceList.erase( chkPlane );
271 bool DBrush::GetBounds( vec3_t min, vec3_t max ){
274 if ( !bBoundsBuilt ) {
278 VectorCopy( bbox_min, min );
279 VectorCopy( bbox_max, max );
284 bool DBrush::BBoxCollision( DBrush* chkBrush ){
288 GetBounds( min1, max1 );
289 chkBrush->GetBounds( min2, max2 );
291 if ( min1[0] >= max2[0] ) {
294 if ( min1[1] >= max2[1] ) {
297 if ( min1[2] >= max2[2] ) {
301 if ( max1[0] <= min2[0] ) {
304 if ( max1[1] <= min2[1] ) {
307 if ( max1[2] <= min2[2] ) {
314 DPlane* DBrush::HasPlane( DPlane* chkPlane ){
315 for ( std::list<DPlane *>::const_iterator brushPlane = faceList.begin(); brushPlane != faceList.end(); brushPlane++ )
317 if ( **brushPlane == *chkPlane ) {
324 bool DBrush::IsCutByPlane( DPlane *cuttingPlane ){
327 if ( pointList.size() == 0 ) {
328 if ( BuildPoints() == 0 ) {
333 std::list<DPoint *>::const_iterator chkPnt = pointList.begin();
335 if ( chkPnt == pointList.end() ) {
339 float dist = cuttingPlane->DistanceToPoint( ( *chkPnt )->_pnt );
341 if ( dist > MAX_ROUND_ERROR ) {
344 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 ) {
374 scene::Node* DBrush::BuildInRadiant( bool allowDestruction, int* changeCnt, scene::Node* entity ){
375 if ( allowDestruction ) {
378 for ( std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
380 if ( ( *chkPlane )->m_bChkOk ) {
390 //+djbob: fixed bug when brush had no faces "phantom brush" in radiant.
391 if ( faceList.size() < 4 ) {
392 globalErrorStream() << "Possible Phantom Brush Found, will not rebuild\n";
397 NodeSmartReference node( GlobalBrushCreator().createBrush() );
399 for ( std::list<DPlane *>::const_iterator buildPlane = faceList.begin(); buildPlane != faceList.end(); buildPlane++ ) {
400 if ( ( *buildPlane )->AddToBrush( node ) && changeCnt ) {
406 Node_getTraversable( *entity )->insert( node );
409 Node_getTraversable( GlobalRadiant().getMapWorldEntity() )->insert( node );
413 QER_brush = node.get_pointer();
415 return node.get_pointer();
418 void DBrush::CutByPlane( DPlane *cutPlane, DBrush **newBrush1, DBrush **newBrush2 ){
419 if ( !IsCutByPlane( cutPlane ) ) {
425 DBrush* b1 = new DBrush;
426 DBrush* b2 = new DBrush;
428 for ( std::list<DPlane *>::const_iterator parsePlane = faceList.begin(); parsePlane != faceList.end(); parsePlane++ )
430 b1->AddFace( ( *parsePlane )->points[0], ( *parsePlane )->points[1], ( *parsePlane )->points[2], NULL );
431 b2->AddFace( ( *parsePlane )->points[0], ( *parsePlane )->points[1], ( *parsePlane )->points[2], NULL );
434 b1->AddFace( cutPlane->points[0], cutPlane->points[1], cutPlane->points[2], NULL );
435 b2->AddFace( cutPlane->points[2], cutPlane->points[1], cutPlane->points[0], NULL );
437 b1->RemoveRedundantPlanes();
438 b2->RemoveRedundantPlanes();
444 bool DBrush::IntersectsWith( DBrush *chkBrush ){
445 if ( pointList.size() == 0 ) {
446 if ( BuildPoints() == 0 ) {
447 return false; // invalid brush!!!!
451 if ( chkBrush->pointList.size() == 0 ) {
452 if ( chkBrush->BuildPoints() == 0 ) {
453 return false; // invalid brush!!!!
457 if ( !BBoxCollision( chkBrush ) ) {
461 std::list<DPlane *>::const_iterator iplPlane;
463 for ( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++ )
466 bool allInFront = true;
467 for ( std::list<DPoint *>::const_iterator iPoint = chkBrush->pointList.begin(); iPoint != chkBrush->pointList.end(); iPoint++ )
469 if ( ( *iplPlane )->DistanceToPoint( ( *iPoint )->_pnt ) < -MAX_ROUND_ERROR ) {
479 for ( iplPlane = chkBrush->faceList.begin(); iplPlane != chkBrush->faceList.end(); iplPlane++ )
481 bool allInFront = true;
482 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 ) {
498 vec3_t vDown = { 0, 0, -1 };
500 std::list<DPlane *>::const_iterator iplPlane;
501 for ( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++ ) {
502 DPlane* p = ( *iplPlane );
504 vec_t d = DotProduct( p->normal, vDown );
508 if ( p->PlaneIntersection( p1, p2, v ) ) {
509 if ( PointPosition( v ) != POINT_OUT_BRUSH ) {
518 void DBrush::BuildBounds(){
519 if ( !bBoundsBuilt ) {
520 if ( pointList.size() == 0 ) { // if points may not have been built, build them
521 if ( BuildPoints() == 0 ) {
526 std::list<DPoint *>::const_iterator first = pointList.begin();
527 VectorCopy( ( *first )->_pnt, bbox_min );
528 VectorCopy( ( *first )->_pnt, bbox_max );
530 std::list<DPoint *>::const_iterator point = pointList.begin();
531 for ( point++; point != pointList.end(); point++ )
533 if ( ( *point )->_pnt[0] > bbox_max[0] ) {
534 bbox_max[0] = ( *point )->_pnt[0];
536 if ( ( *point )->_pnt[1] > bbox_max[1] ) {
537 bbox_max[1] = ( *point )->_pnt[1];
539 if ( ( *point )->_pnt[2] > bbox_max[2] ) {
540 bbox_max[2] = ( *point )->_pnt[2];
543 if ( ( *point )->_pnt[0] < bbox_min[0] ) {
544 bbox_min[0] = ( *point )->_pnt[0];
546 if ( ( *point )->_pnt[1] < bbox_min[1] ) {
547 bbox_min[1] = ( *point )->_pnt[1];
549 if ( ( *point )->_pnt[2] < bbox_min[2] ) {
550 bbox_min[2] = ( *point )->_pnt[2];
558 bool DBrush::BBoxTouch( DBrush *chkBrush ){
562 GetBounds( min1, max1 );
563 chkBrush->GetBounds( min2, max2 );
565 if ( ( min1[0] - max2[0] ) > MAX_ROUND_ERROR ) {
568 if ( ( min1[1] - max2[1] ) > MAX_ROUND_ERROR ) {
571 if ( ( min1[2] - max2[2] ) > MAX_ROUND_ERROR ) {
575 if ( ( min2[0] - max1[0] ) > MAX_ROUND_ERROR ) {
578 if ( ( min2[1] - max1[1] ) > MAX_ROUND_ERROR ) {
581 if ( ( min2[2] - max1[2] ) > MAX_ROUND_ERROR ) {
587 if ( ( min2[0] - max1[0] ) == 0 ) {
591 if ( ( min2[1] - max1[1] ) == 0 ) {
595 if ( ( min2[2] - max1[2] ) == 0 ) {
599 if ( ( min1[0] - max2[0] ) == 0 ) {
603 if ( ( min1[1] - max2[1] ) == 0 ) {
607 if ( ( min1[2] - max2[2] ) == 0 ) {
618 void DBrush::ResetChecks( std::list<Str>* exclusionList ){
619 for ( std::list<DPlane *>::const_iterator resetPlane = faceList.begin(); resetPlane != faceList.end(); resetPlane++ )
623 if ( exclusionList ) {
624 for ( std::list<Str>::iterator eTexture = exclusionList->begin(); eTexture != exclusionList->end(); eTexture++ )
626 if ( strstr( ( *resetPlane )->m_shader.c_str(), eTexture->GetBuffer() ) ) {
633 ( *resetPlane )->m_bChkOk = set;
637 DPlane* DBrush::HasPlaneInverted( DPlane *chkPlane ){
638 for ( std::list<DPlane *>::const_iterator brushPlane = faceList.begin(); brushPlane != faceList.end(); brushPlane++ )
640 if ( **brushPlane != *chkPlane ) {
641 if ( fabs( ( *brushPlane )->_d + chkPlane->_d ) < 0.1 ) {
642 return ( *brushPlane );
649 bool DBrush::HasTexture( const char *textureName ){
650 for ( std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
652 if ( strstr( ( *chkPlane )->m_shader.c_str(), textureName ) ) {
660 bool DBrush::IsDetail(){
661 for ( std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
663 if ( ( *chkPlane )->texInfo.contents & FACE_DETAIL ) {
671 void DBrush::BuildFromWinding( DWinding *w ){
672 if ( w->numpoints < 3 ) {
673 globalErrorStream() << "Winding has invalid number of points";
677 DPlane* wPlane = w->WindingPlane();
680 w2 = w->CopyWinding();
682 for ( i = 0; i < w2->numpoints; i++ )
683 VectorAdd( w2->p[i], wPlane->normal, w2->p[i] );
685 AddFace( w2->p[0], w2->p[1], w2->p[2], NULL );
686 AddFace( w->p[2], w->p[1], w->p[0], NULL );
688 for ( i = 0; i < w->numpoints - 1; i++ )
689 AddFace( w2->p[i], w->p[i], w->p[i + 1], NULL );
690 AddFace( w2->p[w->numpoints - 1], w->p[w->numpoints - 1], w->p[0], NULL );
696 void DBrush::SaveToFile( FILE *pFile ){
697 fprintf( pFile, "{\n" );
699 for ( std::list<DPlane *>::const_iterator pp = faceList.begin(); pp != faceList.end(); pp++ )
703 sprintf( buffer, "( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) %s %.0f %.0f %f %f %.0f 0 0 0\n",
704 ( *pp )->points[0][0], ( *pp )->points[0][1], ( *pp )->points[0][2],
705 ( *pp )->points[1][0], ( *pp )->points[1][1], ( *pp )->points[1][2],
706 ( *pp )->points[2][0], ( *pp )->points[2][1], ( *pp )->points[2][2],
707 ( *pp )->m_shader.c_str(),
708 ( *pp )->texInfo.m_texdef.shift[0], ( *pp )->texInfo.m_texdef.shift[1],
709 ( *pp )->texInfo.m_texdef.scale[0], ( *pp )->texInfo.m_texdef.scale[0],
710 ( *pp )->texInfo.m_texdef.rotate );
712 fprintf( pFile, "%s", buffer );
715 fprintf( pFile, "}\n" );
718 void DBrush::Rotate( vec3_t vOrigin, vec3_t vRotation ){
719 for ( std::list<DPlane *>::const_iterator rotPlane = faceList.begin(); rotPlane != faceList.end(); rotPlane++ )
721 for ( int i = 0; i < 3; i++ )
722 VectorRotate( ( *rotPlane )->points[i], vRotation, vOrigin );
724 ( *rotPlane )->Rebuild();
728 void DBrush::RotateAboutCentre( vec3_t vRotation ){
729 vec3_t min, max, centre;
730 GetBounds( min, max );
731 VectorAdd( min, max, centre );
732 VectorScale( centre, 0.5f, centre );
734 Rotate( centre, vRotation );
737 bool DBrush::ResetTextures( const char* textureName, float fScale[2], float fShift[2], int rotation, const char* newTextureName,
738 int bResetTextureName, int bResetScale[2], int bResetShift[2], int bResetRotation ){
740 bool changed = false;
741 for ( std::list<DPlane *>::const_iterator resetPlane = faceList.begin(); resetPlane != faceList.end(); resetPlane++ )
743 if ( !strcmp( ( *resetPlane )->m_shader.c_str(), textureName ) ) {
744 if ( bResetTextureName ) {
745 ( *resetPlane )->m_shader = newTextureName;
748 if ( bResetScale[0] ) {
749 ( *resetPlane )->texInfo.m_texdef.scale[0] = fScale[0];
751 if ( bResetScale[1] ) {
752 ( *resetPlane )->texInfo.m_texdef.scale[1] = fScale[1];
755 if ( bResetShift[0] ) {
756 ( *resetPlane )->texInfo.m_texdef.shift[0] = fShift[0];
758 if ( bResetShift[1] ) {
759 ( *resetPlane )->texInfo.m_texdef.shift[1] = fShift[1];
762 if ( bResetRotation ) {
763 ( *resetPlane )->texInfo.m_texdef.rotate = (float)rotation;
769 return changed; // no point rebuilding unless we need to, only slows things down
773 for ( std::list<DPlane *>::const_iterator resetPlane = faceList.begin(); resetPlane != faceList.end(); resetPlane++ )
775 if ( bResetTextureName ) {
776 ( *resetPlane )->m_shader = newTextureName;
779 if ( bResetScale[0] ) {
780 ( *resetPlane )->texInfo.m_texdef.scale[0] = fScale[0];
782 if ( bResetScale[1] ) {
783 ( *resetPlane )->texInfo.m_texdef.scale[1] = fScale[1];
786 if ( bResetShift[0] ) {
787 ( *resetPlane )->texInfo.m_texdef.shift[0] = fShift[0];
789 if ( bResetShift[1] ) {
790 ( *resetPlane )->texInfo.m_texdef.shift[1] = fShift[1];
793 if ( bResetRotation ) {
794 ( *resetPlane )->texInfo.m_texdef.rotate = (float)rotation;
801 bool DBrush::operator ==( DBrush* other ){
802 std::list<DPlane *>::const_iterator chkPlane;
804 for ( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
806 if ( !other->HasPlane( ( *chkPlane ) ) ) {
811 for ( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
813 if ( !HasPlane( ( *chkPlane ) ) ) {
821 DPlane* DBrush::AddFace( const vec3_t va, const vec3_t vb, const vec3_t vc, const char *textureName, bool bDetail ){
822 bBoundsBuilt = false;
823 DPlane* newFace = new DPlane( va, vb, vc, textureName, bDetail );
824 faceList.push_back( newFace );
829 DPlane* DBrush::FindPlaneWithClosestNormal( vec_t* normal ) {
831 DPlane* bestDotPlane = NULL;
832 std::list<DPlane *>::const_iterator chkPlane;
833 for ( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ ) {
834 DPlane* pPlane = ( *chkPlane );
836 vec_t dot = DotProduct( pPlane->normal, normal );
837 if ( dot > bestDot ) {
839 bestDotPlane = pPlane;
846 int DBrush::FindPointsForPlane( DPlane* plane, DPoint** pnts, int maxpnts ) {
855 for ( std::list<DPoint *>::const_iterator points = pointList.begin(); points != pointList.end(); points++ ) {
856 DPoint* point = ( *points );
858 if ( fabs( plane->DistanceToPoint( point->_pnt ) ) < MAX_ROUND_ERROR ) {
859 pnts[numpnts] = point;
862 if ( numpnts >= maxpnts ) {
872 void DBrush::RemovePlane( DPlane* plane ) {
873 bBoundsBuilt = false;
874 for ( std::list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++ ) {
875 if ( *deadPlane == plane ) {
877 faceList.remove( plane );