1 /*
2 BobToolz plugin for GtkRadiant
3 Copyright (C) 2001 Gordon Biggans
4 
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.
9 
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.
14 
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
18 */
19 
20 // DBrush.cpp: implementation of the DBrush class.
21 //
22 //////////////////////////////////////////////////////////////////////
23 
24 #include "StdAfx.h"
25 
26 #ifdef WIN32
27 #pragma warning(disable : 4786)
28 #endif
29 
30 #include "StdAfx.h"
31 
32 #include "gtkr_list.h"
33 #include "str.h"
34 
35 #include "DPoint.h"
36 #include "DPlane.h"
37 #include "DBrush.h"
38 #include "DEPair.h"
39 #include "DPatch.h"
40 #include "DEntity.h"
41 #include "DWinding.h"
42 
43 #include "dialogs-gtk.h"
44 
45 #include "misc.h"
46 
47 #include "iundo.h"
48 
49 #include "refcounted_ptr.h"
50 
51 #include "scenelib.h"
52 
53 //////////////////////////////////////////////////////////////////////
54 // Construction/Destruction
55 //////////////////////////////////////////////////////////////////////
56 
DBrush(int ID)57 DBrush::DBrush(int ID)
58 {
59 	m_nBrushID = ID;
60 	bBoundsBuilt = FALSE;
61 	QER_brush = NULL;
62 }
63 
~DBrush()64 DBrush::~DBrush()
65 {
66 	ClearFaces();
67 	ClearPoints();
68 }
69 
70 //////////////////////////////////////////////////////////////////////
71 // Implementation
72 //////////////////////////////////////////////////////////////////////
73 
AddFace(vec3_t va,vec3_t vb,vec3_t vc,_QERFaceData * texData)74 DPlane* DBrush::AddFace(vec3_t va, vec3_t vb, vec3_t vc, _QERFaceData* texData)
75 {
76 #ifdef _DEBUG
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]);
78 #endif
79 	bBoundsBuilt = FALSE;
80 	DPlane*	newFace = new DPlane(va, vb, vc, texData);
81 	faceList.push_back(newFace);
82 
83 	return newFace;
84 }
85 
BuildPoints()86 int DBrush::BuildPoints()
87 {
88 	ClearPoints();
89 
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
92 
93 	for(list<DPlane *>::const_iterator p1=faceList.begin(); p1!=faceList.end(); p1++)
94 	{
95 		list<DPlane *>::const_iterator p2=p1;
96 		for(p2++; p2!=faceList.end(); p2++)
97 		{
98 			list<DPlane *>::const_iterator p3=p2;
99 			for(p3++; p3!=faceList.end(); p3++)
100 			{
101 				vec3_t pnt;
102 				if((*p1)->PlaneIntersection(*p2, *p3, pnt))
103 				{
104 					int pos = PointPosition(pnt);
105 
106 					if(pos == POINT_IN_BRUSH)
107 					{	// ???? shouldn't happen here
108 						Sys_Printf("ERROR:: Build Brush Points: Point IN brush!!!\n");
109 					}
110 					else if(pos == POINT_ON_BRUSH)
111 					{	// normal point
112 						if(!HasPoint(pnt))
113 							AddPoint(pnt);
114 /*						else
115 							Sys_Printf("Duplicate Point Found, pyramids ahoy!!!!!\n");*/
116 						// point lies on more that 3 planes
117 					}
118 
119 					// otherwise point is removed due to another plane..
120 
121 					// Sys_Printf("(%f, %f, %f)\n", pnt[0], pnt[1], pnt[2]);
122 				}
123 			}
124 		}
125 	}
126 
127 #ifdef _DEBUG
128 //	Sys_Printf("%i points on brush\n", pointList.size());
129 #endif
130 
131 	return pointList.size();
132 }
133 
LoadFromBrush(scene::Node * brush,bool textured)134 void DBrush::LoadFromBrush(scene::Node* brush, bool textured)
135 {
136 	ClearFaces();
137 	ClearPoints();
138 
139 #if 0
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);
143 
144 		if(faceData == NULL)
145 			DoMessageBox("Null pointer returned", "WARNING!", MB_OK);
146 
147 		if(textured)
148 			AddFace(faceData->m_v1, faceData->m_v2, faceData->m_v3, faceData);
149 		else
150 			AddFace(faceData->m_v1, faceData->m_v2, faceData->m_v3, NULL);
151 	}
152 #endif
153 
154 	QER_brush = brush;
155 }
156 
PointPosition(vec3_t pnt)157 int DBrush::PointPosition(vec3_t pnt)
158 {
159 	int state = POINT_IN_BRUSH;	// if nothing happens point is inside brush
160 
161 	for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
162 	{
163 		float dist = (*chkPlane)->DistanceToPoint(pnt);
164 
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
170 	}
171 
172 	return state;
173 }
174 
ClearPoints()175 void DBrush::ClearPoints()
176 {
177 	for(list<DPoint *>::const_iterator deadPoint=pointList.begin(); deadPoint!=pointList.end(); deadPoint++) {
178 		delete *deadPoint;
179 	}
180 	pointList.clear();
181 }
182 
ClearFaces()183 void DBrush::ClearFaces()
184 {
185 	bBoundsBuilt = FALSE;
186 	for(list<DPlane *>::const_iterator deadPlane=faceList.begin(); deadPlane!=faceList.end(); deadPlane++)
187 	{
188 		delete *deadPlane;
189 	}
190 	faceList.clear();
191 }
192 
AddPoint(vec3_t pnt)193 void DBrush::AddPoint(vec3_t pnt)
194 {
195 	DPoint* newPoint = new DPoint;
196 	VectorCopy(pnt, newPoint->_pnt);
197 	pointList.push_back(newPoint);
198 }
199 
HasPoint(vec3_t pnt)200 bool DBrush::HasPoint(vec3_t pnt)
201 {
202 	for(list<DPoint *>::const_iterator chkPoint=pointList.begin(); chkPoint!=pointList.end(); chkPoint++)
203 	{
204 		if(**chkPoint == pnt)
205 			return TRUE;
206 	}
207 
208 	return FALSE;
209 }
210 
RemoveRedundantPlanes()211 int DBrush::RemoveRedundantPlanes()
212 {
213 	int cnt = 0;
214 	list<DPlane *>::iterator chkPlane;
215 
216 	// find duplicate planes
217 	list<DPlane *>::iterator p1=faceList.begin();
218 
219 	while( p1!=faceList.end() )
220 	{
221 		list<DPlane *>::iterator p2 = p1;
222 
223 		for(p2++; p2!=faceList.end(); p2++)
224 		{
225 			if(**p1 == **p2)
226 			{
227 				if(!strcmp((*p1)->texInfo.m_texdef.GetName(), "textures/common/caulk"))
228 				{
229 					delete *p1;
230 					p1 = faceList.erase(p1);	// duplicate plane
231 				}
232 				else
233 				{
234 					delete *p2;
235 					p2 = faceList.erase(p2);	// duplicate plane
236 				}
237 
238 				cnt++;
239 				break;
240 			}
241 		}
242 
243 		if( p2 == faceList.end() )
244 			p1++;
245 	}
246 
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() )
250 	{
251 		if(VectorLength((*chkPlane)->normal) == 0) // plane has bad normal
252 		{
253 			delete *chkPlane;
254 			chkPlane = faceList.erase(chkPlane);
255 			cnt++;
256 		} else {
257 			chkPlane++;
258 		}
259 	}
260 	//-djbob
261 
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
264 			return cnt;*/
265 			BuildPoints();
266 
267 	chkPlane=faceList.begin();
268 	while(chkPlane != faceList.end())
269 	{
270 		if((*chkPlane)->IsRedundant(pointList)) // checks that plane "0wnz" :), 3 or more points
271 		{
272 			delete *chkPlane;
273 			chkPlane = faceList.erase(chkPlane);
274 			cnt++;
275 		}
276 		else
277 			chkPlane++;
278 	}
279 
280 	return cnt;
281 }
282 
GetBounds(vec3_t min,vec3_t max)283 bool DBrush::GetBounds(vec3_t min, vec3_t max)
284 {
285 	BuildBounds();
286 
287 	if(!bBoundsBuilt)
288 		return FALSE;
289 
290 	VectorCopy(bbox_min, min);
291 	VectorCopy(bbox_max, max);
292 
293 	return TRUE;
294 }
295 
BBoxCollision(DBrush * chkBrush)296 bool DBrush::BBoxCollision(DBrush* chkBrush)
297 {
298 	vec3_t min1, min2;
299 	vec3_t max1, max2;
300 
301 	GetBounds(min1, max1);
302 	chkBrush->GetBounds(min2, max2);
303 
304 	if(min1[0] >= max2[0])
305 		return FALSE;
306 	if(min1[1] >= max2[1])
307 		return FALSE;
308 	if(min1[2] >= max2[2])
309 		return FALSE;
310 
311 	if(max1[0] <= min2[0])
312 		return FALSE;
313 	if(max1[1] <= min2[1])
314 		return FALSE;
315 	if(max1[2] <= min2[2])
316 		return FALSE;
317 
318 	return TRUE;
319 }
320 
HasPlane(DPlane * chkPlane)321 DPlane* DBrush::HasPlane(DPlane* chkPlane)
322 {
323 	for(list<DPlane *>::const_iterator brushPlane=faceList.begin(); brushPlane!=faceList.end(); brushPlane++)
324 	{
325 		if(**brushPlane == *chkPlane)
326 			return *brushPlane;
327 	}
328 	return NULL;
329 }
330 
IsCutByPlane(DPlane * cuttingPlane)331 bool DBrush::IsCutByPlane(DPlane *cuttingPlane)
332 {
333 	bool isInFront;
334 
335 	if(pointList.size() == 0)
336 		if(BuildPoints() == 0)
337 			return FALSE;
338 
339 	list<DPoint *>::const_iterator chkPnt = pointList.begin();
340 
341 	if(chkPnt == pointList.end())
342 		return FALSE;
343 
344 	float dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);
345 
346 	if(dist > MAX_ROUND_ERROR)
347 		isInFront = FALSE;
348 	else if(dist < MAX_ROUND_ERROR)
349 		isInFront = TRUE;
350 	else
351 		return TRUE;
352 
353 	for(chkPnt++=pointList.begin(); chkPnt!=pointList.end(); chkPnt++)
354 	{
355 		dist = cuttingPlane->DistanceToPoint((*chkPnt)->_pnt);
356 
357 		if(dist > MAX_ROUND_ERROR)
358 		{
359 			if(isInFront)
360 				return TRUE;
361 		}
362 		else if(dist < MAX_ROUND_ERROR)
363 		{
364 			if(!isInFront)
365 				return TRUE;
366 		}
367 		else
368 			return TRUE;
369 	}
370 
371 	return FALSE;
372 }
373 
BuildInRadiant(bool allowDestruction,int * changeCnt,scene::Node * entity)374 scene::Node* DBrush::BuildInRadiant(bool allowDestruction, int* changeCnt, scene::Node* entity)
375 {
376 	if(allowDestruction)
377 	{
378 		bool kill = TRUE;
379 
380 		for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
381 		{
382 			if((*chkPlane)->m_bChkOk)
383 			{
384 				kill = FALSE;
385 				break;
386 			}
387 		}
388 		if(kill)
389 			return NULL;
390 	}
391 
392 	//+djbob: fixed bug when brush had no faces "phantom brush" in radiant.
393 	if(faceList.size() < 4)
394 	{
395 		Sys_Printf("Possible Phantom Brush Found, will not rebuild\n");
396 		return NULL;
397 	}
398 	//-djbob
399 
400 	NodePtr node(Brush_AllocNode());
401 
402 	for(list<DPlane *>::const_iterator buildPlane=faceList.begin(); buildPlane!=faceList.end(); buildPlane++) {
403 		if((*buildPlane)->AddToBrush(node->m_brush) && changeCnt) {
404 			(*changeCnt)++;
405 		}
406 	}
407 
408 	if(entity) {
409 		entity->m_traverse->insert(node);
410 	} else {
411 		GetWorldspawn()->m_traverse->insert(node);
412 	}
413 
414 	return node;
415 }
416 
CutByPlane(DPlane * cutPlane,DBrush ** newBrush1,DBrush ** newBrush2)417 void DBrush::CutByPlane(DPlane *cutPlane, DBrush **newBrush1, DBrush **newBrush2)
418 {
419 	if(!IsCutByPlane(cutPlane))
420 	{
421 		*newBrush1 = NULL;
422 		*newBrush2 = NULL;
423 		return;
424 	}
425 
426 	DBrush* b1 = new DBrush;
427 	DBrush* b2 = new DBrush;
428 
429 	for(list<DPlane *>::const_iterator parsePlane=faceList.begin(); parsePlane!=faceList.end(); parsePlane++)
430 	{
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);
433 	}
434 
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);
437 
438 	b1->RemoveRedundantPlanes();
439 	b2->RemoveRedundantPlanes();
440 
441 	*newBrush1 = b1;
442 	*newBrush2 = b2;
443 }
444 
IntersectsWith(DBrush * chkBrush)445 bool DBrush::IntersectsWith(DBrush *chkBrush)
446 {
447 	if(pointList.size() == 0)
448 		if(BuildPoints() == 0)
449 			return FALSE;	// invalid brush!!!!
450 
451 	if(chkBrush->pointList.size() == 0)
452 		if(chkBrush->BuildPoints() == 0)
453 			return FALSE;	// invalid brush!!!!
454 
455 	if(!BBoxCollision(chkBrush))
456 		return FALSE;
457 
458 	list<DPlane *>::const_iterator iplPlane;
459 
460 	for( iplPlane=faceList.begin(); iplPlane!=faceList.end(); iplPlane++)
461 	{
462 
463 		bool allInFront = TRUE;
464 		for(list<DPoint *>::const_iterator iPoint=chkBrush->pointList.begin(); iPoint!=chkBrush->pointList.end(); iPoint++)
465 		{
466 			if((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR)
467 			{
468 				allInFront = FALSE;
469 				break;
470 			}
471 		}
472 		if(allInFront)
473 			return FALSE;
474 	}
475 
476 	for( iplPlane=chkBrush->faceList.begin(); iplPlane!=chkBrush->faceList.end(); iplPlane++)
477 	{
478 		bool allInFront = TRUE;
479 		for(list<DPoint *>::const_iterator iPoint=pointList.begin(); iPoint!=pointList.end(); iPoint++)
480 		{
481 			if((*iplPlane)->DistanceToPoint((*iPoint)->_pnt) < -MAX_ROUND_ERROR)
482 			{
483 				allInFront = FALSE;
484 				break;
485 			}
486 		}
487 		if(allInFront)
488 			return FALSE;
489 	}
490 
491 	return TRUE;
492 }
493 
IntersectsWith(DPlane * p1,DPlane * p2,vec3_t v)494 bool DBrush::IntersectsWith(DPlane* p1, DPlane* p2, vec3_t v) {
495 	vec3_t vDown = { 0, 0, -1 };
496 
497 	list<DPlane *>::const_iterator iplPlane;
498 	for( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++) {
499 		DPlane* p = (*iplPlane);
500 
501 		vec_t d = DotProduct( p->normal, vDown );
502 		if( d >= 0 ) {
503 			continue;
504 		}
505 		if(p->PlaneIntersection(p1, p2, v)) {
506 			if(PointPosition( v ) != POINT_OUT_BRUSH) {
507 				return TRUE;
508 			}
509 		}
510 	}
511 
512 	return FALSE;
513 }
514 
BuildBounds()515 void DBrush::BuildBounds()
516 {
517 	if(!bBoundsBuilt)
518 	{
519 		if(pointList.size() == 0) // if points may not have been built, build them
520 			if(BuildPoints() == 0)
521 				return;
522 
523 		list<DPoint *>::const_iterator first = pointList.begin();
524 		VectorCopy((*first)->_pnt, bbox_min);
525 		VectorCopy((*first)->_pnt, bbox_max);
526 
527 		list<DPoint *>::const_iterator point=pointList.begin();
528 		for( point++; point!=pointList.end(); point++)
529 		{
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];
536 
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];
543 		}
544 
545 		bBoundsBuilt = TRUE;
546 	}
547 }
548 
BBoxTouch(DBrush * chkBrush)549 bool DBrush::BBoxTouch(DBrush *chkBrush)
550 {
551 	vec3_t min1, min2;
552 	vec3_t max1, max2;
553 
554 	GetBounds(min1, max1);
555 	chkBrush->GetBounds(min2, max2);
556 
557 	if((min1[0] - max2[0]) > MAX_ROUND_ERROR)
558 		return FALSE;
559 	if((min1[1] - max2[1]) > MAX_ROUND_ERROR)
560 		return FALSE;
561 	if((min1[2] - max2[2]) > MAX_ROUND_ERROR)
562 		return FALSE;
563 
564 	if((min2[0] - max1[0]) > MAX_ROUND_ERROR)
565 		return FALSE;
566 	if((min2[1] - max1[1]) > MAX_ROUND_ERROR)
567 		return FALSE;
568 	if((min2[2] - max1[2]) > MAX_ROUND_ERROR)
569 		return FALSE;
570 
571 	int cnt = 0;
572 
573 	if((min2[0] - max1[0]) == 0)
574 		cnt++;
575 
576 	if((min2[1] - max1[1]) == 0)
577 		cnt++;
578 
579 	if((min2[2] - max1[2]) == 0)
580 		cnt++;
581 
582 	if((min1[0] - max2[0]) == 0)
583 		cnt++;
584 
585 	if((min1[1] - max2[1]) == 0)
586 		cnt++;
587 
588 	if((min1[2] - max2[2]) == 0)
589 		cnt++;
590 
591 	if(cnt > 1)
592 		return FALSE;
593 
594 	return TRUE;
595 }
596 
ResetChecks(list<Str> * exclusionList)597 void DBrush::ResetChecks(list<Str>* exclusionList)
598 {
599 	for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
600 	{
601 		bool set = FALSE;
602 
603 		if(exclusionList)
604 		{
605 			for(list<Str>::iterator eTexture = exclusionList->begin(); eTexture != exclusionList->end(); eTexture++)
606 			{
607 				if(strstr((*resetPlane)->texInfo.m_texdef.GetName(), eTexture->GetBuffer()))
608 				{
609 					set = TRUE;
610 					break;
611 				}
612 			}
613 		}
614 
615 		(*resetPlane)->m_bChkOk = set;
616 	}
617 }
618 
HasPlaneInverted(DPlane * chkPlane)619 DPlane* DBrush::HasPlaneInverted(DPlane *chkPlane)
620 {
621 	for(list<DPlane *>::const_iterator brushPlane=faceList.begin(); brushPlane!=faceList.end(); brushPlane++)
622 	{
623 		if(**brushPlane != *chkPlane)
624 		{
625 			if(fabs((*brushPlane)->_d + chkPlane->_d) < 0.1)
626 				return (*brushPlane);
627 		}
628 	}
629 	return NULL;
630 }
631 
HasTexture(const char * textureName)632 bool DBrush::HasTexture(const char *textureName)
633 {
634 	for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
635 	{
636 		if(strstr((*chkPlane)->texInfo.m_texdef.GetName(), textureName))
637 			return TRUE;
638 
639 	}
640 	return FALSE;
641 }
642 
IsDetail()643 bool DBrush::IsDetail()
644 {
645 	for(list<DPlane *>::const_iterator chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
646 	{
647 		if((*chkPlane)->texInfo.m_texdef.contents & FACE_DETAIL)
648 			return TRUE;
649 
650 	}
651 	return FALSE;
652 }
653 
BuildFromWinding(DWinding * w)654 void DBrush::BuildFromWinding(DWinding *w)
655 {
656 	if(w->numpoints < 3)
657 	{
658 		Sys_ERROR("Winding has invalid number of points");
659 		return;
660 	}
661 
662 	DPlane* wPlane = w->WindingPlane();
663 
664 	DWinding* w2;
665 	w2 = w->CopyWinding();
666 	int i;
667 	for(i = 0; i < w2->numpoints; i++)
668 		VectorAdd(w2->p[i], wPlane->normal, w2->p[i]);
669 
670 	AddFace(w2->p[0], w2->p[1], w2->p[2], NULL);
671 	AddFace(w->p[2], w->p[1], w->p[0], NULL);
672 
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);
676 
677 	delete wPlane;
678 	delete w2;
679 }
680 
SaveToFile(FILE * pFile)681 void DBrush::SaveToFile(FILE *pFile)
682 {
683 	fprintf(pFile, "{\n");
684 
685 	for(list<DPlane *>::const_iterator pp=faceList.begin(); pp!=faceList.end(); pp++)
686 	{
687 		char buffer[512];
688 
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);
697 
698 		fprintf(pFile, buffer);
699 	}
700 
701 	fprintf(pFile, "}\n");
702 }
703 
Rotate(vec3_t vOrigin,vec3_t vRotation)704 void DBrush::Rotate(vec3_t vOrigin, vec3_t vRotation)
705 {
706 	for(list<DPlane *>::const_iterator rotPlane=faceList.begin(); rotPlane!=faceList.end(); rotPlane++)
707 	{
708 		for(int i = 0; i < 3; i++)
709 			VectorRotate((*rotPlane)->points[i], vRotation, vOrigin);
710 
711 		(*rotPlane)->Rebuild();
712 	}
713 }
714 
RotateAboutCentre(vec3_t vRotation)715 void DBrush::RotateAboutCentre(vec3_t vRotation)
716 {
717 	vec3_t min, max, centre;
718 	GetBounds(min, max);
719 	VectorAdd(min, max, centre);
720 	VectorScale(centre, 0.5f, centre);
721 
722 	Rotate(centre, vRotation);
723 }
724 
ResetTextures(const char * textureName,float fScale[2],float fShift[2],int rotation,const char * newTextureName,int bResetTextureName,int bResetScale[2],int bResetShift[2],int bResetRotation)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)
727 {
728 	if(textureName)
729 	{
730 		bool changed = FALSE;
731 		for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
732 		{
733 			if(!strcmp((*resetPlane)->texInfo.m_texdef.GetName(), textureName))
734 			{
735         if(bResetTextureName)
736 				  (*resetPlane)->texInfo.m_texdef.SetName(newTextureName);
737 
738 				if(bResetScale[0])
739 					(*resetPlane)->texInfo.m_texdef.scale[0] = fScale[0];
740 				if(bResetScale[1])
741 					(*resetPlane)->texInfo.m_texdef.scale[1] = fScale[1];
742 
743 				if(bResetShift[0])
744 					(*resetPlane)->texInfo.m_texdef.shift[0] = fShift[0];
745 				if(bResetShift[1])
746 					(*resetPlane)->texInfo.m_texdef.shift[1] = fShift[1];
747 
748 				if(bResetRotation)
749 					(*resetPlane)->texInfo.m_texdef.rotate = (float)rotation;
750 
751 				changed = TRUE;
752 			}
753 		}
754 		return changed; // no point rebuilding unless we need to, only slows things down
755 	}
756 	else
757 	{
758 		for(list<DPlane *>::const_iterator resetPlane=faceList.begin(); resetPlane!=faceList.end(); resetPlane++)
759 		{
760         if(bResetTextureName)
761 				  (*resetPlane)->texInfo.m_texdef.SetName(newTextureName);
762 
763 				if(bResetScale[0])
764 					(*resetPlane)->texInfo.m_texdef.scale[0] = fScale[0];
765 				if(bResetScale[1])
766 					(*resetPlane)->texInfo.m_texdef.scale[1] = fScale[1];
767 
768 				if(bResetShift[0])
769 					(*resetPlane)->texInfo.m_texdef.shift[0] = fShift[0];
770 				if(bResetShift[1])
771 					(*resetPlane)->texInfo.m_texdef.shift[1] = fShift[1];
772 
773 				if(bResetRotation)
774 					(*resetPlane)->texInfo.m_texdef.rotate = (float)rotation;
775 		}
776 		return TRUE;
777 	}
778 }
779 
operator ==(DBrush * other)780 bool DBrush::operator ==(DBrush* other)
781 {
782 	list<DPlane *>::const_iterator chkPlane;
783 
784 	for(chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
785 	{
786 		if(!other->HasPlane((*chkPlane)))
787 			return FALSE;
788 	}
789 
790 	for(chkPlane=faceList.begin(); chkPlane!=faceList.end(); chkPlane++)
791 	{
792 		if(!HasPlane((*chkPlane)))
793 			return FALSE;
794 	}
795 
796 	return TRUE;
797 }
798 
AddFace(vec3_t va,vec3_t vb,vec3_t vc,const char * textureName,bool bDetail)799 DPlane* DBrush::AddFace(vec3_t va, vec3_t vb, vec3_t vc, const char *textureName, bool bDetail)
800 {
801 	bBoundsBuilt = FALSE;
802 	DPlane*	newFace = new DPlane(va, vb, vc, textureName, bDetail);
803 	faceList.push_back(newFace);
804 
805 	return newFace;
806 }
807 
FindPlaneWithClosestNormal(vec_t * normal)808 DPlane* DBrush::FindPlaneWithClosestNormal( vec_t* normal ) {
809 	vec_t bestDot = -2;
810 	DPlane* bestDotPlane = NULL;
811 	list<DPlane *>::const_iterator chkPlane;
812 	for( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ ) {
813 		DPlane* pPlane = (*chkPlane);
814 
815 		vec_t dot = DotProduct( pPlane->normal, normal );
816 		if( dot > bestDot ) {
817 			bestDot = dot;
818 			bestDotPlane = pPlane;
819 		}
820 	}
821 
822 	return bestDotPlane;
823 }
824 
FindPointsForPlane(DPlane * plane,DPoint ** pnts,int maxpnts)825 int DBrush::FindPointsForPlane( DPlane* plane, DPoint** pnts, int maxpnts ) {
826 	int numpnts = 0;
827 
828 	if(!maxpnts) {
829 		return 0;
830 	}
831 
832 	BuildPoints();
833 
834 	for( list<DPoint *>::const_iterator points = pointList.begin(); points != pointList.end(); points++ ) {
835 		DPoint* point = (*points);
836 
837 		if( fabs(plane->DistanceToPoint( point->_pnt )) < MAX_ROUND_ERROR ) {
838 			pnts[numpnts] = point;
839 			numpnts++;
840 
841 			if(numpnts >= maxpnts) {
842 				return numpnts;
843 			}
844 
845 		}
846 	}
847 
848 	return numpnts;
849 }
850 
RemovePlane(DPlane * plane)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) {
855 			delete *deadPlane;
856 			faceList.remove( plane );
857 		}
858 	}
859 }
860 
RemoveFromRadiant(void)861 void DBrush::RemoveFromRadiant( void ) {
862 	if(QER_brush) {
863 #if 0
864 		g_FuncTable.m_pfnDeleteBrushHandle(QER_brush);
865 #endif
866 	}
867 }
868