

#include "stdafx.h"
#include <assert.h>
#include "qe3.h"
#include "winding.h"

// globals

int g_nBrushId = 0;

const char* Brush_Name(brush_t *b)
{
  static char cBuff[1024];
	b->numberId = g_nBrushId++;
	if (g_qeglobals.m_bBrushPrimitMode)
  {
    sprintf(cBuff, "Brush %i", b->numberId);
    Brush_SetEpair(b, "Name", cBuff);
  }
  return cBuff;
}

brush_t *Brush_Alloc()
{
  brush_t *b = (brush_t*)qmalloc(sizeof(brush_t));
  return b;
}

void PrintWinding (winding_t *w)
{
	int		i;
	
	printf ("-------------\n");
	for (i=0 ; i<w->numpoints ; i++)
		printf ("(%5.2f, %5.2f, %5.2f)\n", w->points[i][0]
		, w->points[i][1], w->points[i][2]);
}

void PrintPlane (plane_t *p)
{
  printf ("(%5.2f, %5.2f, %5.2f) : %5.2f\n",  p->normal[0],  p->normal[1], 
  p->normal[2],  p->dist);
}

void PrintVector (vec3_t v)
{
  printf ("(%5.2f, %5.2f, %5.2f)\n",  v[0],  v[1], v[2]);
}


/*
=============================================================================

			TEXTURE COORDINATES

=============================================================================
*/


/*
==================
textureAxisFromPlane
==================
*/
vec3_t	baseaxis[18] =
{
{0,0,1}, {1,0,0}, {0,-1,0},			// floor
{0,0,-1}, {1,0,0}, {0,-1,0},		// ceiling
{1,0,0}, {0,1,0}, {0,0,-1},			// west wall
{-1,0,0}, {0,1,0}, {0,0,-1},		// east wall
{0,1,0}, {1,0,0}, {0,0,-1},			// south wall
{0,-1,0}, {1,0,0}, {0,0,-1}			// north wall
};

void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)
{
	int		bestaxis;
	float	dot,best;
	int		i;
	
	best = 0;
	bestaxis = 0;
	
	for (i=0 ; i<6 ; i++)
	{
		dot = DotProduct (pln->normal, baseaxis[i*3]);
		if (dot > best)
		{
			best = dot;
			bestaxis = i;
		}
	}
	
	VectorCopy (baseaxis[bestaxis*3+1], xv);
	VectorCopy (baseaxis[bestaxis*3+2], yv);
}



float	lightaxis[3] = {0.6, 0.8, 1.0};
/*
================
SetShadeForPlane

Light different planes differently to
improve recognition
================
*/
float SetShadeForPlane (plane_t *p)
{
	int		i;
	float	f;

	// axial plane
	for (i=0 ; i<3 ; i++)
		if (fabs(p->normal[i]) > 0.9)
		{
			f = lightaxis[i];
			return f;
		}

	// between two axial planes
	for (i=0 ; i<3 ; i++)
		if (fabs(p->normal[i]) < 0.1)
		{
			f = (lightaxis[(i+1)%3] + lightaxis[(i+2)%3])/2;
			return f;
		}

	// other
	f= (lightaxis[0] + lightaxis[1] + lightaxis[2]) / 3;
	return f;
}

vec3_t  vecs[2];
float	shift[2];

/*
================
Face_Alloc
================
*/
face_t *Face_Alloc( void )
{
	face_t *f = (face_t*)qmalloc( sizeof( *f ) );
	
	if (g_qeglobals.bSurfacePropertiesPlugin)
		f->pData = static_cast<void *>( g_SurfaceTable.m_pfnTexdefAlloc( f ) );

	return f;
}

/*
================
Face_Free
================
*/
void Face_Free( face_t *f )
{
	assert( f != 0 );

	if ( f->face_winding )
	{
		free( f->face_winding );
		f->face_winding = 0;
	}

	if (g_qeglobals.bSurfacePropertiesPlugin)
	{
#ifdef _DEBUG
		if ( !f->pData )
		{
			Sys_Printf("WARNING: unexpected IPluginTexdef is NULL in Face_Free\n");
		}
		else
#endif
		GETPLUGINTEXDEF(f)->DecRef();
	}

	f->texdef.~texdef_t();;

	free( f );
}

/*
================
Face_Clone
================
*/
face_t	*Face_Clone (face_t *f)
{
	face_t	*n;

	n = Face_Alloc();
	n->texdef = f->texdef;

	memcpy (n->planepts, f->planepts, sizeof(n->planepts));

	// all other fields are derived, and will be set by Brush_Build
	return n;
}

/*
================
Face_FullClone

makes an exact copy of the face
================
*/
face_t	*Face_FullClone (face_t *f)
{
	face_t	*n;

	n = Face_Alloc();
	n->texdef = f->texdef;
	memcpy(n->planepts, f->planepts, sizeof(n->planepts));
	memcpy(&n->plane, &f->plane, sizeof(plane_t));
	if (f->face_winding)
		n->face_winding = Winding_Clone(f->face_winding);
	else
		n->face_winding = NULL;
	n->pShader = QERApp_Shader_ForName( n->texdef.name );
	n->pShader->IncRef();
	n->d_texture = n->pShader->getTexture();
	return n;
}

/*
================
Clamp
================
*/
void Clamp(float& f, int nClamp)
{
  float fFrac = f - static_cast<int>(f);
  f = static_cast<int>(f) % nClamp;
  f += fFrac;
}

/*
================
Face_MoveTexture
================
*/
void Face_MoveTexture(face_t *f, vec3_t delta)
{
	vec3_t vX, vY;

  if (g_qeglobals.m_bBrushPrimitMode)
		Face_MoveTexture_BrushPrimit( f, delta );
	else
	{
		TextureAxisFromPlane(&f->plane, vX, vY);

		vec3_t vDP, vShift;
		vDP[0] = DotProduct(delta, vX);
		vDP[1] = DotProduct(delta, vY);

		double fAngle = f->texdef.rotate  / 180 * Q_PI;
		double c = cos(fAngle);
		double s = sin(fAngle);

		vShift[0] = vDP[0] * c - vDP[1] * s;
		vShift[1] = vDP[0] * s + vDP[1] * c;

		if (!f->texdef.scale[0])
			f->texdef.scale[0] = 1;
		if (!f->texdef.scale[1])
			f->texdef.scale[1] = 1;

		f->texdef.shift[0] -= vShift[0] / f->texdef.scale[0];
		f->texdef.shift[1] -= vShift[1] / f->texdef.scale[1];
  
		// clamp the shifts
		Clamp(f->texdef.shift[0], f->d_texture->width);
		Clamp(f->texdef.shift[1], f->d_texture->height);
	}
}

/*
================
Face_SetColor
================
*/
void Face_SetColor (brush_t *b, face_t *f, float fCurveColor) 
{
	float	shade;
	qtexture_t *q;

	q = f->d_texture;

	// set shading for face
	shade = SetShadeForPlane (&f->plane);
	if (g_pParentWnd->GetCamera()->Camera().draw_mode == cd_texture && !b->owner->eclass->fixedsize)
	{
		//if (b->curveBrush)
		//  shade = fCurveColor;
		f->d_color[0] = 
		f->d_color[1] = 
		f->d_color[2] = shade;
	}
	else
	{
		f->d_color[0] = shade*q->color[0];
		f->d_color[1] = shade*q->color[1];
		f->d_color[2] = shade*q->color[2];
	}
}

/*
================
Face_TextureVectors
================
*/
void Face_TextureVectors (face_t *f, float STfromXYZ[2][4])
{
	vec3_t		pvecs[2];
	int			sv, tv;
	float		ang, sinv, cosv;
	float		ns, nt;
	int			i,j;
	qtexture_t *q;
	texdef_t	*td;

#ifdef _DEBUG
  // this code is not supposed to be used while in BP mode, warning here can help spot the problem
	if (g_qeglobals.m_bBrushPrimitMode && !g_qeglobals.bNeedConvert)
		Sys_Printf("Warning : illegal call of Face_TextureVectors in brush primitive mode\n");
#endif

	td = &f->texdef;
	q = f->d_texture;

	memset (STfromXYZ, 0, 8*sizeof(float));

	if (!td->scale[0])
		td->scale[0] = (g_PrefsDlg.m_bHiColorTextures) ? 0.5 : 1;
	if (!td->scale[1])
		td->scale[1] = (g_PrefsDlg.m_bHiColorTextures) ? 0.5 : 1;

	// get natural texture axis
	TextureAxisFromPlane(&f->plane, pvecs[0], pvecs[1]);

	// rotate axis
	if (td->rotate == 0)
		{ sinv = 0 ; cosv = 1; }
	else if (td->rotate == 90)
		{ sinv = 1 ; cosv = 0; }
	else if (td->rotate == 180)
		{ sinv = 0 ; cosv = -1; }
	else if (td->rotate == 270)
		{ sinv = -1 ; cosv = 0; }
	else
	{	
		ang = td->rotate / 180 * Q_PI;
		sinv = sin(ang);
		cosv = cos(ang);
	}

	if (pvecs[0][0])
		sv = 0;
	else if (pvecs[0][1])
		sv = 1;
	else
		sv = 2;
				
	if (pvecs[1][0])
		tv = 0;
	else if (pvecs[1][1])
		tv = 1;
	else
		tv = 2;
					
	for (i=0 ; i<2 ; i++) {
		ns = cosv * pvecs[i][sv] - sinv * pvecs[i][tv];
		nt = sinv * pvecs[i][sv] +  cosv * pvecs[i][tv];
		STfromXYZ[i][sv] = ns;
		STfromXYZ[i][tv] = nt;
	}

	// scale
	for (i=0 ; i<2 ; i++)
		for (j=0 ; j<3 ; j++)
			STfromXYZ[i][j] = STfromXYZ[i][j] / td->scale[i];

	// shift
	STfromXYZ[0][3] = td->shift[0];
	STfromXYZ[1][3] = td->shift[1];

	for (j=0 ; j<4 ; j++) {
		STfromXYZ[0][j] /= q->width;
		STfromXYZ[1][j] /= q->height;
	}
}
	
/*
================
Face_MakePlane
================
*/
void Face_MakePlane (face_t *f)
{
	int		j;
	vec3_t	t1, t2, t3;

	// convert to a vector / dist plane
	for (j=0 ; j<3 ; j++)
	{
		t1[j] = f->planepts[0][j] - f->planepts[1][j];
		t2[j] = f->planepts[2][j] - f->planepts[1][j];
		t3[j] = f->planepts[1][j];
	}
	
	CrossProduct(t1,t2, f->plane.normal);
	if (VectorCompare (f->plane.normal, vec3_origin))
		printf ("WARNING: brush plane with no normal\n");
	VectorNormalize (f->plane.normal);
	f->plane.dist = DotProduct (t3, f->plane.normal);
}

/*
================
EmitTextureCoordinates
================
*/
void EmitTextureCoordinates ( float *xyzst, qtexture_t *q, face_t *f)
{
	float	STfromXYZ[2][4];

	Face_TextureVectors (f,  STfromXYZ);
	xyzst[3] = DotProduct (xyzst, STfromXYZ[0]) + STfromXYZ[0][3];
	xyzst[4] = DotProduct (xyzst, STfromXYZ[1]) + STfromXYZ[1][3];
}

//==========================================================================

/*
================
Brush_MakeFacePlanes
================
*/
void Brush_MakeFacePlanes (brush_t *b)
{
	face_t	*f;

	for (f=b->brush_faces ; f ; f=f->next)
	{
		Face_MakePlane (f);
	}
}

/*
================
DrawBrushEntityName
================
*/
void DrawBrushEntityName (brush_t *b)
{
	char	*name;
	//float	a, s, c;
	//vec3_t	mid;
	//int		i;

	if (!b->owner)
		return;		// during contruction

	if (b->owner == world_entity)
		return;

	if (b != b->owner->brushes.onext)
		return;	// not key brush

// MERGEME
#if 0
	if (!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ANGLES))
	{
		// draw the angle pointer
		a = FloatForKey (b->owner, "angle");
		if (a)
		{
			s = sin (a/180*Q_PI);
			c = cos (a/180*Q_PI);
			for (i=0 ; i<3 ; i++)
				mid[i] = (b->mins[i] + b->maxs[i])*0.5; 

			qglBegin (GL_LINE_STRIP);
			qglVertex3fv (mid);
			mid[0] += c*8;
			mid[1] += s*8;
			mid[2] += s*8;
			qglVertex3fv (mid);
			mid[0] -= c*4;
			mid[1] -= s*4;
			mid[2] -= s*4;
			mid[0] -= s*4;
			mid[1] += c*4;
			mid[2] += c*4;
			qglVertex3fv (mid);
			mid[0] += c*4;
			mid[1] += s*4;
			mid[2] += s*4;
			mid[0] += s*4;
			mid[1] -= c*4;
			mid[2] -= c*4;
			qglVertex3fv (mid);
			mid[0] -= c*4;
			mid[1] -= s*4;
			mid[2] -= s*4;
			mid[0] += s*4;
			mid[1] -= c*4;
			mid[2] -= c*4;
			qglVertex3fv (mid);
			qglEnd ();
		}
	}
#endif

	if (g_qeglobals.d_savedinfo.show_names)
	{
		name = ValueForKey (b->owner, "classname");
		qglRasterPos3f (b->mins[0]+4, b->mins[1]+4, b->mins[2]+4);
		qglCallLists (strlen(name), GL_UNSIGNED_BYTE, name);
	}
}

/*
=================
Brush_MakeFaceWinding

returns the visible polygon on a face
=================
*/
winding_t *Brush_MakeFaceWinding (brush_t *b, face_t *face)
{
	winding_t	*w;
	face_t		*clip;
	plane_t			plane;
	qboolean		past;

	// get a poly that covers an effectively infinite area
	w = Winding_BaseForPlane (&face->plane);

	// chop the poly by all of the other faces
	past = false;
	for (clip = b->brush_faces ; clip && w ; clip=clip->next)
	{
		if (clip == face)
		{
			past = true;
			continue;
		}
		if (DotProduct (face->plane.normal, clip->plane.normal) > 0.999
			&& fabs(face->plane.dist - clip->plane.dist) < 0.01 )
		{	// identical plane, use the later one
			if (past)
			{
				free (w);
				return NULL;
			}
			continue;
		}

		// flip the plane, because we want to keep the back side
		VectorSubtract (vec3_origin,clip->plane.normal, plane.normal);
		plane.dist = -clip->plane.dist;
		
		w = Winding_Clip (w, &plane, false);
		if (!w)
			return w;
	}
	
	if (w->numpoints < 3)
	{
		free(w);
		w = NULL;
	}

	if (!w)
		printf ("unused plane\n");

	return w;
}

/*
=================
Brush_SnapPlanepts
=================
*/
void Brush_SnapPlanepts (brush_t *b)
{
	int		i, j;
	face_t	*f;

  if (g_PrefsDlg.m_bNoClamp)
    return;

	for (f=b->brush_faces ; f; f=f->next)
		for (i=0 ; i<3 ; i++)
			for (j=0 ; j<3 ; j++)
				f->planepts[i][j] = floor (f->planepts[i][j] + 0.5);
}
	
/*
** Brush_Build
**
** Builds a brush rendering data and also sets the min/max bounds
*/
// TTimo
// added a bConvert flag to convert between old and new brush texture formats
// TTimo
// brush grouping: update the group treeview if necessary
void Brush_Build( brush_t *b, bool bSnap, bool bMarkMap, bool bConvert )
{
	bool		bLocalConvert;

#ifdef _DEBUG
	if (!g_qeglobals.m_bBrushPrimitMode && bConvert)
		Sys_Printf("Warning : conversion from brush primitive to old brush format not implemented\n");
#endif

	// if bConvert is set and g_qeglobals.bNeedConvert is not, that just means we need convert for this brush only
	if (bConvert && !g_qeglobals.bNeedConvert)
	{
#ifdef _DEBUG
    //++timo FIXME: it's not very clear when this can happen, I guess while dealing with plugins that send brushes
    // back and forth in one format or the other .. more when mixing BP / noBP in the same maps.
#endif
		bLocalConvert = true;
		g_qeglobals.bNeedConvert = true;
	}
  else
    bLocalConvert = false;

	/*
	** build the windings and generate the bounding box
	*/
	Brush_BuildWindings(b, bSnap);

	Patch_BuildPoints (b);

	/*
	** move the points and edges if in select mode
	*/
	if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge)
		SetupVertexSelection ();

    if (b->itemOwner == NULL)
      Group_AddToProperGroup(b);

	if (bMarkMap)
	{
		Sys_MarkMapModified();
	}

	if (bLocalConvert)
		g_qeglobals.bNeedConvert = false;
}

/*
==============
Brush_SplitBrushByFace

The incoming brush is NOT freed.
The incoming face is NOT left referenced.
==============
*/
void Brush_SplitBrushByFace (brush_t *in, face_t *f, brush_t **front, brush_t **back)
{
	brush_t	*b;
	face_t	*nf;
	vec3_t	temp;

	b = Brush_Clone (in);
	nf = Face_Clone (f);

	nf->texdef = b->brush_faces->texdef;
	nf->next = b->brush_faces;
	b->brush_faces = nf;

	Brush_Build( b );
	Brush_RemoveEmptyFaces ( b );
	if ( !b->brush_faces )
	{	// completely clipped away
		Brush_Free (b);
		*back = NULL;
	}
	else
	{
		Entity_LinkBrush (in->owner, b);
		*back = b;
	}

	b = Brush_Clone (in);
	nf = Face_Clone (f);
	// swap the plane winding
	VectorCopy (nf->planepts[0], temp);
	VectorCopy (nf->planepts[1], nf->planepts[0]);
	VectorCopy (temp, nf->planepts[1]);

	nf->texdef = b->brush_faces->texdef;
	nf->next = b->brush_faces;
	b->brush_faces = nf;

	Brush_Build( b );
	Brush_RemoveEmptyFaces ( b );
	if ( !b->brush_faces )
	{	// completely clipped away
		Brush_Free (b);
		*front = NULL;
	}
	else
	{
		Entity_LinkBrush (in->owner, b);
		*front = b;
	}
}

/*
=================
Brush_BestSplitFace

returns the best face to split the brush with.
return NULL if the brush is convex
=================
*/
face_t *Brush_BestSplitFace(brush_t *b)
{
	face_t *face, *f, *bestface;
	winding_t *front, *back;
	int splits, tinywindings, value, bestvalue;

	bestvalue = 999999;
	bestface = NULL;
	for (face = b->brush_faces; face; face = face->next)
	{
		splits = 0;
		tinywindings = 0;
		for (f = b->brush_faces; f; f = f->next)
		{
			if (f == face) continue;
			//
			Winding_SplitEpsilon(f->face_winding, face->plane.normal, face->plane.dist, 0.1, &front, &back);

			if (!front)
			{
				Winding_Free(back);
			}
			else if (!back)
			{
				Winding_Free(front);
			}
			else
			{
				splits++;
				if (Winding_IsTiny(front)) tinywindings++;
				if (Winding_IsTiny(back)) tinywindings++;
			}
		}
		if (splits)
		{
			value = splits + 50 * tinywindings;
			if (value < bestvalue)
			{
				bestvalue = value;
				bestface = face;
			}
		}
	}
	return bestface;
}

/*
=================
Brush_MakeConvexBrushes

MrE FIXME: this doesn't work because the old
		   Brush_SplitBrushByFace is used
Turns the brush into a minimal number of convex brushes.
If the input brush is convex then it will be returned.
Otherwise the input brush will be freed.
NOTE: the input brush should have windings for the faces.
=================
*/
brush_t *Brush_MakeConvexBrushes(brush_t *b)
{
	brush_t *front, *back, *end;
	face_t *face;

	b->next = NULL;
	face = Brush_BestSplitFace(b);
	if (!face) return b;
	Brush_SplitBrushByFace(b, face, &front, &back);
	//this should never happen
	if (!front && !back) return b;
	Brush_Free(b);
	if (!front)
		return Brush_MakeConvexBrushes(back);
	b = Brush_MakeConvexBrushes(front);
	if (back)
	{
		for (end = b; end->next; end = end->next);
		end->next = Brush_MakeConvexBrushes(back);
	}
	return b;
}

/*
=================
Brush_Convex
=================
*/
int Brush_Convex(brush_t *b)
{
	face_t *face1, *face2;

	for (face1 = b->brush_faces; face1; face1 = face1->next)
	{
		if (!face1->face_winding) continue;
		for (face2 = b->brush_faces; face2; face2 = face2->next)
		{
			if (face1 == face2) continue;
			if (!face2->face_winding) continue;
			if (Winding_PlanesConcave(face1->face_winding, face2->face_winding,
										face1->plane.normal, face2->plane.normal,
										face1->plane.dist, face2->plane.dist))
			{
				return false;
			}
		}
	}
	return true;
}

/*
=================
Brush_MoveVertexes_old1

- The input brush must have face windings.
- The input brush must be a brush with faces that do not intersect.
- The input brush does not have to be convex.
- The vertex will not be moved if the movement either causes the
  brush to have faces that intersect or causes the brush to be
  flipped inside out.
  (For instance a tetrahedron can easily be flipped inside out
  without having faces that intersect.)
- The created brush does not have to be convex.
- Returns true if the vertex movement is performed.
=================
*/

#define MAX_MOVE_FACES		64
#define INTERSECT_EPSILON	0.1
#define POINT_EPSILON		0.3

int Brush_MoveVertex_old1(brush_t *b, vec3_t vertex, vec3_t delta, vec3_t end, bool bSnap)
{
	face_t *f, *face, *newface, *lastface, *nextface;
	face_t *movefaces[MAX_MOVE_FACES];
	int movefacepoints[MAX_MOVE_FACES];
	winding_t *w, tmpw;
	int i, j, k, nummovefaces, result;
	float dot;

	result = false;
	//
	tmpw.numpoints = 3;
	tmpw.maxpoints = 3;
	VectorAdd(vertex, delta, end);
	//snap or not?
	if (bSnap)
		for (i = 0; i < 3; i++)
			end[i] = floor(end[i] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize;
	//chop off triangles from all brush faces that use the to be moved vertex
	//store pointers to these chopped off triangles in movefaces[]
	nummovefaces = 0;
	for (face = b->brush_faces; face; face = face->next)
	{
		w = face->face_winding;
		if (!w) continue;
		for (i = 0; i < w->numpoints; i++)
		{
			if (Point_Equal(w->points[i], vertex, POINT_EPSILON))
			{
				if (face->face_winding->numpoints <= 3)
				{
					movefacepoints[nummovefaces] = i;
					movefaces[nummovefaces++] = face;
					break;
				}
				dot = DotProduct(end, face->plane.normal) - face->plane.dist;
				//if the end point is in front of the face plane
				if (dot > 0.1)
				{
					//fanout triangle subdivision
					for (k = i; k < i + w->numpoints-3; k++)
					{
						VectorCopy(w->points[i], tmpw.points[0]);
						VectorCopy(w->points[(k+1) % w->numpoints], tmpw.points[1]);
						VectorCopy(w->points[(k+2) % w->numpoints], tmpw.points[2]);
						//
						newface = Face_Clone(face);
						//get the original
						for (f = face; f->original; f = f->original) ;
						newface->original = f;
						//store the new winding
						if (newface->face_winding) Winding_Free(newface->face_winding);
						newface->face_winding = Winding_Clone(&tmpw);
						//get the texture
						newface->d_texture = QERApp_Texture_ForName2( newface->texdef.name );
						//add the face to the brush
						newface->next = b->brush_faces;
						b->brush_faces = newface;
						//add this new triangle to the move faces
						movefacepoints[nummovefaces] = 0;
						movefaces[nummovefaces++] = newface;
					}
					//give the original face a new winding
					VectorCopy(w->points[(i-2+w->numpoints) % w->numpoints], tmpw.points[0]);
					VectorCopy(w->points[(i-1+w->numpoints) % w->numpoints], tmpw.points[1]);
					VectorCopy(w->points[i], tmpw.points[2]);
					Winding_Free(face->face_winding);
					face->face_winding = Winding_Clone(&tmpw);
					//add the original face to the move faces
					movefacepoints[nummovefaces] = 2;
					movefaces[nummovefaces++] = face;
				}
				else
				{
					//chop a triangle off the face
					VectorCopy(w->points[(i-1+w->numpoints) % w->numpoints], tmpw.points[0]);
					VectorCopy(w->points[i], tmpw.points[1]);
					VectorCopy(w->points[(i+1) % w->numpoints], tmpw.points[2]);
					//remove the point from the face winding
					Winding_RemovePoint(w, i);
					//get texture crap right
					Face_SetColor(b, face, 1.0);
					for (j = 0; j < w->numpoints; j++)
						EmitTextureCoordinates(w->points[j], face->d_texture, face);
					//make a triangle face
					newface = Face_Clone(face);
					//get the original
					for (f = face; f->original; f = f->original) ;
					newface->original = f;
					//store the new winding
					if (newface->face_winding) Winding_Free(newface->face_winding);
					newface->face_winding = Winding_Clone(&tmpw);
					//get the texture
					newface->d_texture = QERApp_Texture_ForName2( newface->texdef.name );
					//add the face to the brush
					newface->next = b->brush_faces;
					b->brush_faces = newface;
					//
					movefacepoints[nummovefaces] = 1;
					movefaces[nummovefaces++] = newface;
				}
				break;
			}
		}
	}
	//now movefaces contains pointers to triangle faces that
	//contain the to be moved vertex

	//check if the move is valid
	int l;
	vec3_t p1, p2;
	winding_t *w2;
	plane_t plane;

	face = NULL;
	VectorCopy(vertex, tmpw.points[1]);
	VectorCopy(end, tmpw.points[2]);
	for (face = b->brush_faces; face; face = face->next)
	{
		for (i = 0; i < nummovefaces; i++)
		{
			if (face == movefaces[i])
				break;
		}
		if (i < nummovefaces)
			continue;
		//the delta vector may not intersect with any of the not move faces
		if (Winding_VectorIntersect(face->face_winding, &face->plane, vertex, end, INTERSECT_EPSILON))
			break;
		//if the end point of the to be moved vertex is near this not move face
		if (abs(DotProduct(face->plane.normal, end) - face->plane.dist) < 0.5)
		{
			//the end point may not be inside or very close to the not move face winding
			if (Winding_PointInside(face->face_winding, &face->plane, end, 0.5))
				break;
		}
		for (i = 0; i < nummovefaces; i++)
		{
			w = movefaces[i]->face_winding;
			j = movefacepoints[i];
			for (k = -1; k <= 1; k += 2)
			{
				//check if the new edge will not intersect with the not move face
				VectorCopy(w->points[(j + k + w->numpoints) % w->numpoints], tmpw.points[0]);
				if (Winding_VectorIntersect(face->face_winding, &face->plane, tmpw.points[0], end, INTERSECT_EPSILON))
				{
					//ok the new edge instersects with the not move face
					//we can't perform the vertex movement
					//break;
				}
				//check if the not move face intersects the "movement winding"
				Winding_Plane(&tmpw, plane.normal, &plane.dist);
				w2 = face->face_winding;
				for (l = 0; l < w2->numpoints; l++)
				{
					VectorCopy(w2->points[l], p1);
					if (Point_Equal(p1, tmpw.points[0], POINT_EPSILON)) continue;
					VectorCopy(w2->points[(l+1) % w2->numpoints], p2);
					if (Point_Equal(p2, tmpw.points[0], POINT_EPSILON)) continue;
					if (Winding_VectorIntersect(&tmpw, &plane, p1, p2, INTERSECT_EPSILON))
						break;
				}
				if (l < w2->numpoints)
				{
					//ok this not move face intersects the "movement winding"
					//we can't perform the vertex movement
					break;
				}
			}
			if (k <= 1) break;
		}
		if (i < nummovefaces)
			break;
	}
	if (!face)
	{
		//ok the move was valid
		//now move all the vertexes of the movefaces
		for (i = 0; i < nummovefaces; i++)
		{
			VectorCopy(end, movefaces[i]->face_winding->points[movefacepoints[i]]);
			//create new face plane
			for (j = 0; j < 3; j++)
			{
				VectorCopy(movefaces[i]->face_winding->points[j], movefaces[i]->planepts[j]);
			}
			Face_MakePlane(movefaces[i]);
		}
		result = true;
	}
	//get texture crap right
	for (i = 0; i < nummovefaces; i++)
	{
		Face_SetColor(b, movefaces[i], 1.0);
		for (j = 0; j < movefaces[i]->face_winding->numpoints; j++)
			EmitTextureCoordinates(movefaces[i]->face_winding->points[j], movefaces[i]->d_texture, movefaces[i]);
	}

	//now try to merge faces with their original faces
	lastface = NULL;
	for (face = b->brush_faces; face; face = nextface)
	{
		nextface = face->next;
		if (!face->original)
		{
			lastface = face;
			continue;
		}
		if (!Plane_Equal(&face->plane, &face->original->plane, false))
		{
			lastface = face;
			continue;
		}
		w = Winding_TryMerge(face->face_winding, face->original->face_winding, face->plane.normal, true);
		if (!w)
		{
			lastface = face;
			continue;
		}
		Winding_Free(face->original->face_winding);
		face->original->face_winding = w;
		//get texture crap right
		Face_SetColor(b, face->original, 1.0);
		for (j = 0; j < face->original->face_winding->numpoints; j++)
			EmitTextureCoordinates(face->original->face_winding->points[j], face->original->d_texture, face->original);
		//remove the face that was merged with the original
		if (lastface) lastface->next = face->next;
		else b->brush_faces = face->next;
		Face_Free(face);
	}
	return result;
}

/*
=================
Brush_MoveVertexes_old2

- The input brush must be convex
- The input brush must have face windings.
- The output brush will be convex.
- Returns true if the vertex movement is performed.
=================
*/

#define MAX_MOVE_FACES		64
#define INTERSECT_EPSILON	0.1
#define POINT_EPSILON		0.3

int Brush_MoveVertex_old2(brush_t *b, vec3_t vertex, vec3_t delta, vec3_t end, bool bSnap)
{
	face_t *f, *face, *newface, *lastface, *nextface;
	face_t *movefaces[MAX_MOVE_FACES];
	int movefacepoints[MAX_MOVE_FACES];
	winding_t *w, tmpw;
	int i, j, k, nummovefaces, result;
	float dot;

	result = true;
	//
	tmpw.numpoints = 3;
	tmpw.maxpoints = 3;
	VectorAdd(vertex, delta, end);
	//snap or not?
	if (bSnap)
		for (i = 0; i < 3; i++)
			end[i] = floor(end[i] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize;
	//chop off triangles from all brush faces that use the to be moved vertex
	//store pointers to these chopped off triangles in movefaces[]
	nummovefaces = 0;
	for (face = b->brush_faces; face; face = face->next)
	{
		w = face->face_winding;
		if (!w) continue;
		for (i = 0; i < w->numpoints; i++)
		{
			if (Point_Equal(w->points[i], vertex, POINT_EPSILON))
			{
				if (face->face_winding->numpoints <= 3)
				{
					movefacepoints[nummovefaces] = i;
					movefaces[nummovefaces++] = face;
					break;
				}
				dot = DotProduct(end, face->plane.normal) - face->plane.dist;
				//if the end point is in front of the face plane
				if (dot > 0.1)
				{
					//fanout triangle subdivision
					for (k = i; k < i + w->numpoints-3; k++)
					{
						VectorCopy(w->points[i], tmpw.points[0]);
						VectorCopy(w->points[(k+1) % w->numpoints], tmpw.points[1]);
						VectorCopy(w->points[(k+2) % w->numpoints], tmpw.points[2]);
						//
						newface = Face_Clone(face);
						//get the original
						for (f = face; f->original; f = f->original) ;
						newface->original = f;
						//store the new winding
						if (newface->face_winding) Winding_Free(newface->face_winding);
						newface->face_winding = Winding_Clone(&tmpw);
						//get the texture
						newface->d_texture = QERApp_Texture_ForName2( newface->texdef.name );
						//add the face to the brush
						newface->next = b->brush_faces;
						b->brush_faces = newface;
						//add this new triangle to the move faces
						movefacepoints[nummovefaces] = 0;
						movefaces[nummovefaces++] = newface;
					}
					//give the original face a new winding
					VectorCopy(w->points[(i-2+w->numpoints) % w->numpoints], tmpw.points[0]);
					VectorCopy(w->points[(i-1+w->numpoints) % w->numpoints], tmpw.points[1]);
					VectorCopy(w->points[i], tmpw.points[2]);
					Winding_Free(face->face_winding);
					face->face_winding = Winding_Clone(&tmpw);
					//add the original face to the move faces
					movefacepoints[nummovefaces] = 2;
					movefaces[nummovefaces++] = face;
				}
				else
				{
					//chop a triangle off the face
					VectorCopy(w->points[(i-1+w->numpoints) % w->numpoints], tmpw.points[0]);
					VectorCopy(w->points[i], tmpw.points[1]);
					VectorCopy(w->points[(i+1) % w->numpoints], tmpw.points[2]);
					//remove the point from the face winding
					Winding_RemovePoint(w, i);
					//get texture crap right
					Face_SetColor(b, face, 1.0);
					for (j = 0; j < w->numpoints; j++)
						EmitTextureCoordinates(w->points[j], face->d_texture, face);
					//make a triangle face
					newface = Face_Clone(face);
					//get the original
					for (f = face; f->original; f = f->original) ;
					newface->original = f;
					//store the new winding
					if (newface->face_winding) Winding_Free(newface->face_winding);
					newface->face_winding = Winding_Clone(&tmpw);
					//get the texture
					newface->d_texture = QERApp_Texture_ForName2( newface->texdef.name );
					//add the face to the brush
					newface->next = b->brush_faces;
					b->brush_faces = newface;
					//
					movefacepoints[nummovefaces] = 1;
					movefaces[nummovefaces++] = newface;
				}
				break;
			}
		}
	}
	//now movefaces contains pointers to triangle faces that
	//contain the to be moved vertex

	//move the vertex
	for (i = 0; i < nummovefaces; i++)
	{
		//move vertex to end position
		VectorCopy(end, movefaces[i]->face_winding->points[movefacepoints[i]]);
		//create new face plane
		for (j = 0; j < 3; j++)
		{
			VectorCopy(movefaces[i]->face_winding->points[j], movefaces[i]->planepts[j]);
		}
		Face_MakePlane(movefaces[i]);
	}
	//if the brush is no longer convex
	if (!Brush_Convex(b))
	{
		for (i = 0; i < nummovefaces; i++)
		{
			//move the vertex back to the initial position
			VectorCopy(vertex, movefaces[i]->face_winding->points[movefacepoints[i]]);
			//create new face plane
			for (j = 0; j < 3; j++)
			{
				VectorCopy(movefaces[i]->face_winding->points[j], movefaces[i]->planepts[j]);
			}
			Face_MakePlane(movefaces[i]);
		}
		result = false;
	}
	//get texture crap right
	for (i = 0; i < nummovefaces; i++)
	{
		Face_SetColor(b, movefaces[i], 1.0);
		for (j = 0; j < movefaces[i]->face_winding->numpoints; j++)
			EmitTextureCoordinates(movefaces[i]->face_winding->points[j], movefaces[i]->d_texture, movefaces[i]);
	}

	//now try to merge faces with their original faces
	lastface = NULL;
	for (face = b->brush_faces; face; face = nextface)
	{
		nextface = face->next;
		if (!face->original)
		{
			lastface = face;
			continue;
		}
		if (!Plane_Equal(&face->plane, &face->original->plane, false))
		{
			lastface = face;
			continue;
		}
		w = Winding_TryMerge(face->face_winding, face->original->face_winding, face->plane.normal, true);
		if (!w)
		{
			lastface = face;
			continue;
		}
		Winding_Free(face->original->face_winding);
		face->original->face_winding = w;
		//get texture crap right
		Face_SetColor(b, face->original, 1.0);
		for (j = 0; j < face->original->face_winding->numpoints; j++)
			EmitTextureCoordinates(face->original->face_winding->points[j], face->original->d_texture, face->original);
		//remove the face that was merged with the original
		if (lastface) lastface->next = face->next;
		else b->brush_faces = face->next;
		Face_Free(face);
	}
	return result;
}

/*
=================
Brush_MoveVertexes

- The input brush must be convex
- The input brush must have face windings.
- The output brush will be convex.
- Returns true if the WHOLE vertex movement is performed.
=================
*/

#define MAX_MOVE_FACES		64

int Brush_MoveVertex(brush_t *b, vec3_t vertex, vec3_t delta, vec3_t end, bool bSnap)
{
	face_t *f, *face, *newface, *lastface, *nextface;
	face_t *movefaces[MAX_MOVE_FACES];
	int movefacepoints[MAX_MOVE_FACES];
	winding_t *w, tmpw;
	vec3_t start, mid;
	plane_t plane;
	int i, j, k, nummovefaces, result, done;
	float dot, front, back, frac, smallestfrac;

	result = true;
	//
	tmpw.numpoints = 3;
	tmpw.maxpoints = 3;
	VectorCopy(vertex, start);
	VectorAdd(vertex, delta, end);
	//snap or not?
	if (bSnap)
		for (i = 0; i < 3; i++)
			end[i] = floor(end[i] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize;
	//
	VectorCopy(end, mid);
	//if the start and end are the same
	if (Point_Equal(start, end, 0.3)) return false;
	//the end point may not be the same as another vertex
	for (face = b->brush_faces; face; face = face->next)
	{
		w = face->face_winding;
		if (!w) continue;
		for (i = 0; i < w->numpoints; i++)
		{
			if (Point_Equal(w->points[i], end, 0.3))
			{
				VectorCopy(vertex, end);
				return false;
			}
		}
	}
	//
	done = false;
	while(!done)
	{
		//chop off triangles from all brush faces that use the to be moved vertex
		//store pointers to these chopped off triangles in movefaces[]
		nummovefaces = 0;
		for (face = b->brush_faces; face; face = face->next)
		{
			w = face->face_winding;
			if (!w) continue;
			for (i = 0; i < w->numpoints; i++)
			{
				if (Point_Equal(w->points[i], start, 0.2))
				{
					if (face->face_winding->numpoints <= 3)
					{
						movefacepoints[nummovefaces] = i;
						movefaces[nummovefaces++] = face;
						break;
					}
					dot = DotProduct(end, face->plane.normal) - face->plane.dist;
					//if the end point is in front of the face plane
					if (dot > 0.1)
					{
						//fanout triangle subdivision
						for (k = i; k < i + w->numpoints-3; k++)
						{
							VectorCopy(w->points[i], tmpw.points[0]);
							VectorCopy(w->points[(k+1) % w->numpoints], tmpw.points[1]);
							VectorCopy(w->points[(k+2) % w->numpoints], tmpw.points[2]);
							//
							newface = Face_Clone(face);
							//get the original
							for (f = face; f->original; f = f->original) ;
							newface->original = f;
							//store the new winding
							if (newface->face_winding) Winding_Free(newface->face_winding);
							newface->face_winding = Winding_Clone(&tmpw);
							//get the texture information
							  newface->pShader = face->pShader;
							  newface->d_texture = face->d_texture;
							//add the face to the brush
							newface->next = b->brush_faces;
							b->brush_faces = newface;
							//add this new triangle to the move faces
							movefacepoints[nummovefaces] = 0;
							movefaces[nummovefaces++] = newface;
						}
						//give the original face a new winding
						VectorCopy(w->points[(i-2+w->numpoints) % w->numpoints], tmpw.points[0]);
						VectorCopy(w->points[(i-1+w->numpoints) % w->numpoints], tmpw.points[1]);
						VectorCopy(w->points[i], tmpw.points[2]);
						Winding_Free(face->face_winding);
						face->face_winding = Winding_Clone(&tmpw);
						//add the original face to the move faces
						movefacepoints[nummovefaces] = 2;
						movefaces[nummovefaces++] = face;
					}
					else
					{
						//chop a triangle off the face
						VectorCopy(w->points[(i-1+w->numpoints) % w->numpoints], tmpw.points[0]);
						VectorCopy(w->points[i], tmpw.points[1]);
						VectorCopy(w->points[(i+1) % w->numpoints], tmpw.points[2]);
						//remove the point from the face winding
						Winding_RemovePoint(w, i);
						//get texture crap right
						Face_SetColor(b, face, 1.0);
						for (j = 0; j < w->numpoints; j++)
							EmitTextureCoordinates(w->points[j], face->d_texture, face);
						//make a triangle face
						newface = Face_Clone(face);
						//get the original
						for (f = face; f->original; f = f->original) ;
						newface->original = f;
						//store the new winding
						if (newface->face_winding) Winding_Free(newface->face_winding);
						newface->face_winding = Winding_Clone(&tmpw);
						//get the texture
		              newface->pShader = face->pShader;
						newface->d_texture = newface->pShader->getTexture();
//						newface->d_texture = QERApp_Texture_ForName2( newface->texdef.name );
						//add the face to the brush
						newface->next = b->brush_faces;
						b->brush_faces = newface;
						//
						movefacepoints[nummovefaces] = 1;
						movefaces[nummovefaces++] = newface;
					}
					break;
				}
			}
		}
		//now movefaces contains pointers to triangle faces that
		//contain the to be moved vertex
		//
		done = true;
		VectorCopy(end, mid);
		smallestfrac = 1;
		for (face = b->brush_faces; face; face = face->next)
		{
			//check if there is a move face that has this face as the original
			for (i = 0; i < nummovefaces; i++)
			{
				if (movefaces[i]->original == face) break;
			}
			if (i >= nummovefaces) continue;
			//check if the original is not a move face itself
			for (j = 0; j < nummovefaces; j++)
			{
				if (face == movefaces[j]) break;
			}
			//if the original is not a move face itself
			if (j >= nummovefaces)
			{
				memcpy(&plane, &movefaces[i]->original->plane, sizeof(plane_t));
			}
			else
			{
				k = movefacepoints[j];
				w = movefaces[j]->face_winding;
				VectorCopy(w->points[(k+1)%w->numpoints], tmpw.points[0]);
				VectorCopy(w->points[(k+2)%w->numpoints], tmpw.points[1]);
				//
				k = movefacepoints[i];
				w = movefaces[i]->face_winding;
				VectorCopy(w->points[(k+1)%w->numpoints], tmpw.points[2]);
				if (!Plane_FromPoints(tmpw.points[0], tmpw.points[1], tmpw.points[2], &plane))
				{
					VectorCopy(w->points[(k+2)%w->numpoints], tmpw.points[2]);
					if (!Plane_FromPoints(tmpw.points[0], tmpw.points[1], tmpw.points[2], &plane))
						//this should never happen otherwise the face merge did a crappy job a previous pass
						continue;
				}
			}
			//now we've got the plane to check agains
			front = DotProduct(start, plane.normal) - plane.dist;
			back = DotProduct(end, plane.normal) - plane.dist;
			//if the whole move is at one side of the plane
			if (front < 0.01 && back < 0.01) continue;
			if (front > -0.01 && back > -0.01) continue;
			//if there's no movement orthogonal to this plane at all
			if (fabs(front-back) < 0.001) continue;
			//ok first only move till the plane is hit
			frac = front/(front-back);
			if (frac < smallestfrac)
			{
				mid[0] = start[0] + (end[0] - start[0]) * frac;
				mid[1] = start[1] + (end[1] - start[1]) * frac;
				mid[2] = start[2] + (end[2] - start[2]) * frac;
				smallestfrac = frac;
			}
			//
			done = false;
		}

		//move the vertex
		for (i = 0; i < nummovefaces; i++)
		{
			//move vertex to end position
			VectorCopy(mid, movefaces[i]->face_winding->points[movefacepoints[i]]);
			//create new face plane
			for (j = 0; j < 3; j++)
			{
				VectorCopy(movefaces[i]->face_winding->points[j], movefaces[i]->planepts[j]);
			}
			Face_MakePlane(movefaces[i]);
			if (VectorLength(movefaces[i]->plane.normal) < 0.1)
				result = false;
		}
		//if the brush is no longer convex
		if (!result || !Brush_Convex(b))
		{
			for (i = 0; i < nummovefaces; i++)
			{
				//move the vertex back to the initial position
				VectorCopy(start, movefaces[i]->face_winding->points[movefacepoints[i]]);
				//create new face plane
				for (j = 0; j < 3; j++)
				{
					VectorCopy(movefaces[i]->face_winding->points[j], movefaces[i]->planepts[j]);
				}
				Face_MakePlane(movefaces[i]);
			}
			result = false;
			VectorCopy(start, end);
			done = true;
		}
		else
		{
			VectorCopy(mid, start);
		}
		//get texture crap right
		for (i = 0; i < nummovefaces; i++)
		{
			Face_SetColor(b, movefaces[i], 1.0);
			for (j = 0; j < movefaces[i]->face_winding->numpoints; j++)
				EmitTextureCoordinates(movefaces[i]->face_winding->points[j], movefaces[i]->d_texture, movefaces[i]);
		}

		//now try to merge faces with their original faces
		lastface = NULL;
		for (face = b->brush_faces; face; face = nextface)
		{
			nextface = face->next;
			if (!face->original)
			{
				lastface = face;
				continue;
			}
			if (!Plane_Equal(&face->plane, &face->original->plane, false))
			{
				lastface = face;
				continue;
			}
			w = Winding_TryMerge(face->face_winding, face->original->face_winding, face->plane.normal, true);
			if (!w)
			{
				lastface = face;
				continue;
			}
			Winding_Free(face->original->face_winding);
			face->original->face_winding = w;
			//get texture crap right
			Face_SetColor(b, face->original, 1.0);
			for (j = 0; j < face->original->face_winding->numpoints; j++)
				EmitTextureCoordinates(face->original->face_winding->points[j], face->original->d_texture, face->original);
			//remove the face that was merged with the original
			if (lastface) lastface->next = face->next;
			else b->brush_faces = face->next;
			Face_Free(face);
		}
	}
	return result;
}

/*
=================
Brush_InsertVertexBetween
=================
*/
int Brush_InsertVertexBetween(brush_t *b, vec3_t p1, vec3_t p2)
{
	face_t *face;
	winding_t *w, *neww;
	vec3_t point;
	int i, insert;

	if (Point_Equal(p1, p2, 0.4))
		return false;
	VectorAdd(p1, p2, point);
	VectorScale(point, 0.5, point);
	insert = false;
	//the end point may not be the same as another vertex
	for (face = b->brush_faces; face; face = face->next)
	{
		w = face->face_winding;
		if (!w) continue;
		neww = NULL;
		for (i = 0; i < w->numpoints; i++)
		{
			if (!Point_Equal(w->points[i], p1, 0.1))
				continue;
			if (Point_Equal(w->points[(i+1) % w->numpoints], p2, 0.1))
			{
				neww = Winding_InsertPoint(w, point, (i+1) % w->numpoints);
				break;
			}
			else if (Point_Equal(w->points[(i-1+w->numpoints) % w->numpoints], p2, 0.3))
			{
				neww = Winding_InsertPoint(w, point, i);
				break;
			}
		}
		if (neww)
		{
			Winding_Free(face->face_winding);
			face->face_winding = neww;
			insert = true;
		}
	}
	return insert;
}


/*
=================
Brush_ResetFaceOriginals
=================
*/
void Brush_ResetFaceOriginals(brush_t *b)
{
	face_t *face;

	for (face = b->brush_faces; face; face = face->next)
	{
		face->original = NULL;
	}
}

/*
=================
Brush_Parse

The brush is NOT linked to any list
=================
*/
//++timo FIXME: when using old brush primitives, the test loop for "Brush" and "patchDef2" "patchDef3" is ran
// before each face parsing. It works, but it's a performance hit
brush_t *Brush_Parse (void)
{
	brush_t		*b;
	face_t		*f;
	int			i,j;
	
	g_qeglobals.d_parsed_brushes++;
  b = Brush_Alloc();

  do
	{
		if (!GetToken (true))
			break;
		if (!strcmp (token, "}") )
			break;
		
		// handle "Brush" primitive
		if (strcmpi(token, "brushDef") == 0)
		{
			// Timo parsing new brush format
			g_qeglobals.bPrimitBrushes=true;
      //++timo FIXME: we'll soon allow mixing of the two formats in the same map
			if (!g_qeglobals.m_bBrushPrimitMode)
			{
  			// check the map is not mixing the two kinds of brushes
				if (g_qeglobals.bOldBrushes)
					Sys_Printf("Warning: standard format and brush primitives in the same file are not allowed ( Brush_Parse )\n");
        // prompt the user once about converting (when conversion has begun this flag is true)
        if (!g_qeglobals.bNeedConvert)
        {
          // we didn't turn the conversion flag on yet, it's time to ask the user
          if (MessageBox( g_qeglobals.d_hwndMain, "This map was saved using brush primitives format, and your project settings use the standard format.\nDo you want to convert it back to standard format?\nNOTE: due to limitations of the standard format, some textures may be misplaced after conversion.", "Conversion required", MB_YESNO ) == IDNO )
          {
            // we need a global flag to cancel the whole map loading process
            g_bCancel_Map_LoadFile = true;
            return NULL;
          }
        }
        // set the "conversion needed" flag (conversion actually happens in Brush_Build
				g_qeglobals.bNeedConvert=true;
			}
			
			BrushPrimit_Parse(b);
			if (b == NULL)
			{
				Warning ("parsing brush primitive");
				return NULL;
			}
			else
      {
        
				continue;
      }
		}
		if (strcmpi(token, "patchDef2") == 0 || strcmpi(token, "patchDef3") == 0)
		{
			free (b);
			
			// double string compare but will go away soon
			b = Patch_Parse(strcmpi(token, "patchDef2") == 0);
			if (b == NULL)
			{
				Warning ("parsing patch/brush");
				return NULL;
			}
			else
			{
				continue;
			}
			// handle inline patch
		}
		else
		{
			// Timo parsing old brush format
			g_qeglobals.bOldBrushes=true;
			if (g_qeglobals.m_bBrushPrimitMode)
			{
				// check the map is not mixing the two kinds of brushes
				if (g_qeglobals.bPrimitBrushes)
					Sys_Printf("Warning : old brushes and brush primitive in the same file are not allowed ( Brush_Parse )\n");
        // prompt the user once about converting (when conversion has begun this flag is true)
        if (!g_qeglobals.bNeedConvert)
        {
          // we didn't turn the conversion flag on yet, it's time to ask the user
          if (MessageBox( g_qeglobals.d_hwndMain, "This map was saved using standard format, and your project settings use the new \"brush primitives\" format. Do you want to convert it?\nNOTE: Brush primitives is an experimental feature, backup your work first. Next versions of Radiant will allow mixing the two formats in the same maps for a smooth transition.", "Conversion required", MB_YESNO ) == IDNO )
          {
            // we need a global flag to cancel the whole map loading process
            g_bCancel_Map_LoadFile = true;
            return NULL;
          }
        }
        // set the "conversion needed" flag (conversion actually happens in Brush_Build
				g_qeglobals.bNeedConvert=true;
			}
			
			f = Face_Alloc();
			
			// add the brush to the end of the chain, so
			// loading and saving a map doesn't reverse the order
			
			f->next = NULL;
			if (!b->brush_faces)
			{
				b->brush_faces = f;
			}
			else
			{
				face_t *scan;
				for (scan=b->brush_faces ; scan->next ; scan=scan->next)
					;
				scan->next = f;
			}
			
			// read the three point plane definition
			for (i=0 ; i<3 ; i++)
			{
				if (i != 0)
					GetToken (true);
				if (strcmp (token, "(") )
				{
					Warning ("parsing brush");
					return NULL;
				}
				
				for (j=0 ; j<3 ; j++)
				{
					GetToken (false);
					f->planepts[i][j] = atof(token);
				}
				
				GetToken (false);
				if (strcmp (token, ")") )
				{
					Warning ("parsing brush");
					return NULL;
				}
			}
		}

		// Timo
		// if we have a surface plugin, we'll call the plugin parsing
		if (g_qeglobals.bSurfacePropertiesPlugin)
		{
			GETPLUGINTEXDEF(f)->ParseTexdef();
		}
		else
		{
			
			// read the texturedef
			GetToken (false);
			f->texdef.SetName(token);
			if (token[0] == '(')
			{
				int i = 32;
			}
			GetToken (false);
			f->texdef.shift[0] = atoi(token);
			GetToken (false);
			f->texdef.shift[1] = atoi(token);
			GetToken (false);
			f->texdef.rotate = atoi(token);	
			GetToken (false);
			f->texdef.scale[0] = atof(token);
			GetToken (false);
			f->texdef.scale[1] = atof(token);
			
			// get the shader
			f->pShader = QERApp_Shader_ForName( f->texdef.name );
			f->pShader->IncRef();
			//++timo to-be-removed?
			f->d_texture = f->pShader->getTexture();
			//f->d_texture = Texture_ForName( f->texdef.name );
			// the flags and value field aren't necessarily present
			//++timo FIXME: access through the IShader object?
			f->texdef.flags = f->d_texture->flags;
			f->texdef.value = f->d_texture->value;
			f->texdef.contents = f->d_texture->contents;
			
			if (TokenAvailable ())
			{
				GetToken (false);
				f->texdef.contents = atoi(token);
				GetToken (false);
				f->texdef.flags = atoi(token);
				GetToken (false);
				f->texdef.value = atoi(token);
			}
			
		}
	} while (1);
	
	return b;
}

/*
=================
QERApp_MapPrintf_FILE
callback for surface properties plugin
must fit a PFN_QERAPP_MAPPRINTF ( see isurfaceplugin.h )
=================
*/
// carefully initialize !
FILE * g_File;
void WINAPI QERApp_MapPrintf_FILE( char *text, ... )
{
	va_list argptr;
	char	buf[32768];

	va_start (argptr,text);
	vsprintf (buf, text,argptr);
	va_end (argptr);

	fprintf( g_File, buf );
}


/*
==============
Brush_SetEpair
sets an epair for the given brush
==============
*/
void Brush_SetEpair(brush_t *b, const char *pKey, const char *pValue)
{
	if (g_qeglobals.m_bBrushPrimitMode)
	{
    if (b->patchBrush)
    {
      Patch_SetEpair(b->pPatch, pKey, pValue);
    }
    else
    {
		  SetKeyValue(b->epairs, pKey, pValue);
    }
	}
	else
	{
		Sys_Printf("Can only set key/values in Brush primitive mode\n");
	}
}

/* 
=================
Brush_GetKeyValue
=================
*/
const char* Brush_GetKeyValue(brush_t *b, const char *pKey)
{
	if (g_qeglobals.m_bBrushPrimitMode)
	{
    if (b->patchBrush)
    {
      return Patch_GetKeyValue(b->pPatch, pKey);
    }
    else
    {
		  return ValueForKey(b->epairs, pKey);
    }
	}
	else
	{
		Sys_Printf("Can only set brush/patch key/values in Brush primitive mode\n");
	}
  return "";
}

/*
=================
CheckName
temporary stuff, detect potential problems when saving the texture name
=================
*/
void CheckName( face_t *fa, char *pname )
{
  if (!strlen(fa->texdef.name))
	{
#ifdef _DEBUG
		Sys_Printf("WARNING: unexpected texdef.name is empty in Brush.cpp CheckName\n");
#endif
		fa->texdef.SetName("radiant/notex");
		strcpy(pname, "radiant/notex");
		return;
	}

	// some manage to get long filename textures (with spaces) in their maps
	if (strchr( fa->texdef.name, ' ' ))
	{
		char Msg1[1024];
		sprintf( Msg1, "Can't save texture with spaces in name. Rename %s\nNOTE: This message may popup several times .. once for each buggy face detected.", fa->texdef.name );
		Sys_Printf("%s\n", Msg1 );
		MessageBox( g_qeglobals.d_hwndMain, Msg1, "Error saving map", MB_OK );
		strcpy( pname, "radiant/notex" );
		return;
	}

  //++timo FIXME: bug #103494 detection attempt
  // TODO: clean this detection part when bug will have disappeared
	if (fa->texdef.name[0] == '(')
  {
		char *text = "Bug #103494 detected, dropping texture. Please report to timo@qeradiant.com if you have a way to reproduce!\nNOTE: this message may popup several times .. once for each buggy face detected.";
		Sys_Printf("%s\n", text);
		MessageBox( g_qeglobals.d_hwndMain, text, "Error saving map", MB_OK );
		// need to cleanup this dead face name or we may loop endlessly
		fa->texdef.SetName("radiant/notex");
		strcpy( pname, "radiant/notex" );
		return;
	}

	strcpy( pname, fa->texdef.name );
}

/*
=================
Brush_Write
save all brushes as Brush primitive format
=================
*/
void Brush_Write (brush_t *b, FILE *f)
{
	epair_t	*ep;
	face_t	*fa;
	char	pname[1024];
	int		i;
	
	if (b->patchBrush)
	{
		Patch_Write(b->pPatch, f);
		return;
	}
	if (g_qeglobals.m_bBrushPrimitMode)
	{
		// save brush primitive format
		fprintf (f, "{\nbrushDef\n{\n");
		// brush epairs
		if (b->epairs)
			for (ep = b->epairs ; ep ; ep=ep->next)
				fprintf (f, "\"%s\" \"%s\"\n", ep->key, ep->value);
		for (fa=b->brush_faces ; fa ; fa=fa->next)
		{
			// save planepts
			for (i=0 ; i<3 ; i++)
			{
				fprintf(f, "( ");
				for (int j = 0; j < 3; j++)
					if (fa->planepts[i][j] == static_cast<int>(fa->planepts[i][j]))
						fprintf(f, "%i ", static_cast<int>(fa->planepts[i][j]));
					else
						fprintf(f, "%f ", fa->planepts[i][j]);
				fprintf(f, ") ");
			}
			// save texture coordinates
			fprintf(f,"( ( ");
			for (i=0 ; i<3 ; i++)
				if (fa->brushprimit_texdef.coords[0][i] == static_cast<int>(fa->brushprimit_texdef.coords[0][i]))
					fprintf(f,"%i ",static_cast<int>(fa->brushprimit_texdef.coords[0][i]));
				else
					fprintf(f,"%f ",fa->brushprimit_texdef.coords[0][i]);
			fprintf(f,") ( ");
			for (i=0 ; i<3 ; i++)
				if (fa->brushprimit_texdef.coords[1][i] == static_cast<int>(fa->brushprimit_texdef.coords[1][i]))
					fprintf(f,"%i ",static_cast<int>(fa->brushprimit_texdef.coords[1][i]));
				else
					fprintf(f,"%f ",fa->brushprimit_texdef.coords[1][i]);
			fprintf(f,") ) ");
#ifdef _DEBUG
			// save texture attribs
			//++timo surface properties plugin not implemented for brush primitives
			if (g_qeglobals.bSurfacePropertiesPlugin)
				Sys_Printf("WARNING: surface properties plugin not supported with brush primitives (yet)\n");
#endif

			CheckName( fa, pname );
/*
      char *pName = strlen(fa->texdef.name) > 0 ? fa->texdef.name : "unnamed";

      //++timo FIXME: bug #103494 detection attempt
      // TODO: clean this detection part when solved (remove below as well)
      if (pName[0] == '(')
      {
        char *text = "Bug #103494 detected, dropping texture. Please report to timo@qeradiant.com if you have a way to reproduce!";
        Sys_Printf("%s\n", text);
        MessageBox( g_qeglobals.d_hwndMain, text, "Error saving map", MB_OK );
        pName = "radiant/notex";
      } */

			fprintf(f, "%s ", pname );
			fprintf(f, "%i %i %i\n", fa->texdef.contents, fa->texdef.flags, fa->texdef.value);
		}
		fprintf (f, "}\n}\n");
	}
	else
	{
		fprintf (f, "{\n");
		for (fa=b->brush_faces ; fa ; fa=fa->next)
		{
			for (i=0 ; i<3 ; i++)
			{
				fprintf(f, "( ");
				for (int j = 0; j < 3; j++)
				{
					if (fa->planepts[i][j] == static_cast<int>(fa->planepts[i][j]))
						fprintf(f, "%i ", static_cast<int>(fa->planepts[i][j]));
					else
						fprintf(f, "%f ", fa->planepts[i][j]);
				}
				fprintf(f, ") ");
			}
			
			if (g_qeglobals.bSurfacePropertiesPlugin)
			{
				g_File = f;
#ifdef _DEBUG
				if (!fa->pData)
					Sys_Printf("ERROR: unexpected IPluginTexdef* is NULL in Brush_Write\n");
				else
#endif
				GETPLUGINTEXDEF(fa)->WriteTexdef( QERApp_MapPrintf_FILE );
			}
			else
			{
/*				pname = fa->texdef.name;
				if (pname[0] == 0)
					pname = "radiant/notex"; */

				CheckName( fa, pname );
/*
				//++timo TODO: detect long file names (with spaces in) and refuse to save them

        //++timo FIXME: bug #103494 detection attempt
        // TODO: clean this detection part when solved (remove above as well)
        if (pname[0] == '(')
        {
          char *text = "Bug #103494 detected, dropping texture. Please report to timo@qeradiant.com if you have a way to reproduce!\nNOTE: this message may popup several times .. once for each buggy face detected.";
          Sys_Printf("%s\n", text);
          MessageBox( g_qeglobals.d_hwndMain, text, "Error saving map", MB_OK );
					// need to cleanup this dead face name or we may loop endlessly
					strcpy(fa->texdef.name, "radiant/notex");
          pname = "radiant/notex";
        } */
				
				fprintf (f, "%s %i %i %i ", pname,
					(int)fa->texdef.shift[0], (int)fa->texdef.shift[1],
					(int)fa->texdef.rotate);
				
				if (fa->texdef.scale[0] == (int)fa->texdef.scale[0])
					fprintf (f, "%i ", (int)fa->texdef.scale[0]);
				else
					fprintf (f, "%f ", (float)fa->texdef.scale[0]);
				if (fa->texdef.scale[1] == (int)fa->texdef.scale[1])
					fprintf (f, "%i", (int)fa->texdef.scale[1]);
				else
					fprintf (f, "%f", (float)fa->texdef.scale[1]);
				
				fprintf (f, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value);
			}
			fprintf (f, "\n");
		}
		fprintf (f, "}\n");
	}
}

/*
=================
QERApp_MapPrintf_MEMFILE
callback for surface properties plugin
must fit a PFN_QERAPP_MAPPRINTF ( see isurfaceplugin.h )
=================
*/
// carefully initialize !
CMemFile * g_pMemFile;
void WINAPI QERApp_MapPrintf_MEMFILE( char *text, ... )
{
	va_list argptr;
	char	buf[32768];

	va_start (argptr,text);
	vsprintf (buf, text,argptr);
	va_end (argptr);

	MemFile_fprintf( g_pMemFile, buf );
}

/*
=================
Brush_Write to a CMemFile*
save all brushes as Brush primitive format
=================
*/
void Brush_Write (brush_t *b, CMemFile *pMemFile)
{
	epair_t *ep;
	face_t	*fa;
	char pname[1024];
	int		i;
	
	if (b->patchBrush)
	{
		Patch_Write(b->pPatch, pMemFile);
		return;
	}
	//++timo NOTE: it's not very difficult to add since the surface properties plugin
	// writes throught a printf-style function prototype
	if (g_qeglobals.bSurfacePropertiesPlugin)
	{
		Sys_Printf("WARNING: Brush_Write to a CMemFile and Surface Properties plugin not done\n");
	}
	if (g_qeglobals.m_bBrushPrimitMode)
	{
		// brush primitive format
		MemFile_fprintf (pMemFile, "{\nBrushDef\n{\n");
		// brush epairs
		if (b->epairs)
			for( ep = b->epairs ; ep ; ep=ep->next )
				MemFile_fprintf (pMemFile, "\"%s\" \"%s\"\n", ep->key, ep->value );
		for (fa=b->brush_faces ; fa ; fa=fa->next)
		{
			// save planepts
			for (i=0 ; i<3 ; i++)
			{
				MemFile_fprintf(pMemFile, "( ");
				for (int j = 0; j < 3; j++)
					if (fa->planepts[i][j] == static_cast<int>(fa->planepts[i][j]))
						MemFile_fprintf(pMemFile, "%i ", static_cast<int>(fa->planepts[i][j]));
					else
						MemFile_fprintf(pMemFile, "%f ", fa->planepts[i][j]);
				MemFile_fprintf(pMemFile, ") ");
			}
			// save texture coordinates
			MemFile_fprintf(pMemFile,"( ( ");
			for (i=0 ; i<3 ; i++)
				if (fa->brushprimit_texdef.coords[0][i] == static_cast<int>(fa->brushprimit_texdef.coords[0][i]))
					MemFile_fprintf(pMemFile,"%i ",static_cast<int>(fa->brushprimit_texdef.coords[0][i]));
				else
					MemFile_fprintf(pMemFile,"%f ",fa->brushprimit_texdef.coords[0][i]);
			MemFile_fprintf(pMemFile,") ( ");
			for (i=0 ; i<3 ; i++)
				if (fa->brushprimit_texdef.coords[1][i] == static_cast<int>(fa->brushprimit_texdef.coords[1][i]))
					MemFile_fprintf(pMemFile,"%i ",static_cast<int>(fa->brushprimit_texdef.coords[1][i]));
				else
					MemFile_fprintf(pMemFile,"%f ",fa->brushprimit_texdef.coords[1][i]);
			MemFile_fprintf(pMemFile,") ) ");
			CheckName( fa, pname );
//      char *pName = strlen(fa->texdef.name) > 0 ? fa->texdef.name : "unnamed";
			// save texture attribs
			MemFile_fprintf(pMemFile, "%s ", pname);
			MemFile_fprintf(pMemFile, "%i %i %i\n", fa->texdef.contents, fa->texdef.flags, fa->texdef.value);
		}
		MemFile_fprintf (pMemFile, "}\n}\n");
	}
	else
	{
		// old brushes format
		// also handle surface properties plugin
		MemFile_fprintf (pMemFile, "{\n");
		for (fa=b->brush_faces ; fa ; fa=fa->next)
		{
			for (i=0 ; i<3 ; i++)
			{
				MemFile_fprintf(pMemFile, "( ");
				for (int j = 0; j < 3; j++)
				{
					if (fa->planepts[i][j] == static_cast<int>(fa->planepts[i][j]))
						MemFile_fprintf(pMemFile, "%i ", static_cast<int>(fa->planepts[i][j]));
					else
						MemFile_fprintf(pMemFile, "%f ", fa->planepts[i][j]);
				}
				MemFile_fprintf(pMemFile, ") ");
			}
			
			if (g_qeglobals.bSurfacePropertiesPlugin)
			{
				g_pMemFile = pMemFile;
#ifdef _DEBUG
				if (!fa->pData)
					Sys_Printf("ERROR: unexpected IPluginTexdef* is NULL in Brush_Write\n");
				else
#endif
				GETPLUGINTEXDEF(fa)->WriteTexdef( QERApp_MapPrintf_MEMFILE );
			}
			else
			{
				CheckName( fa, pname );
/*				pname = fa->texdef.name;
				if (pname[0] == 0)
					pname = "unnamed"; */
				
				MemFile_fprintf (pMemFile, "%s %i %i %i ", pname,
					(int)fa->texdef.shift[0], (int)fa->texdef.shift[1],
					(int)fa->texdef.rotate);
				
				if (fa->texdef.scale[0] == (int)fa->texdef.scale[0])
					MemFile_fprintf (pMemFile, "%i ", (int)fa->texdef.scale[0]);
				else
					MemFile_fprintf (pMemFile, "%f ", (float)fa->texdef.scale[0]);
				if (fa->texdef.scale[1] == (int)fa->texdef.scale[1])
					MemFile_fprintf (pMemFile, "%i", (int)fa->texdef.scale[1]);
				else
					MemFile_fprintf (pMemFile, "%f", (float)fa->texdef.scale[1]);
				
				MemFile_fprintf (pMemFile, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value);
			}
			MemFile_fprintf (pMemFile, "\n");
		}
		MemFile_fprintf (pMemFile, "}\n");
	}
	
	
}


/*
=============
Brush_Create

Create non-textured blocks for entities
The brush is NOT linked to any list
=============
*/
brush_t	*Brush_Create (vec3_t mins, vec3_t maxs, texdef_t *texdef)
{
	int		i, j;
	vec3_t	pts[4][2];
	face_t	*f;
	brush_t	*b;

	// brush primitive mode : convert texdef to brushprimit_texdef ?
	// most of the time texdef is empty
	if (g_qeglobals.m_bBrushPrimitMode)
	{
		// check texdef is empty .. if there are cases it's not we need to write some conversion code
		if (texdef->shift[0]!=0 || texdef->shift[1]!=0 || texdef->scale[0]!=0 || texdef->scale[1]!=0 || texdef->rotate!=0)
			Sys_Printf("Warning : non-zero texdef detected in Brush_Create .. need brush primitive conversion\n");
	}

	for (i=0 ; i<3 ; i++)
	{
		if (maxs[i] < mins[i])
			Error ("Brush_InitSolid: backwards");
	}

	b = Brush_Alloc();
	
	pts[0][0][0] = mins[0];
	pts[0][0][1] = mins[1];
	
	pts[1][0][0] = mins[0];
	pts[1][0][1] = maxs[1];
	
	pts[2][0][0] = maxs[0];
	pts[2][0][1] = maxs[1];
	
	pts[3][0][0] = maxs[0];
	pts[3][0][1] = mins[1];
	
	for (i=0 ; i<4 ; i++)
	{
		pts[i][0][2] = mins[2];
		pts[i][1][0] = pts[i][0][0];
		pts[i][1][1] = pts[i][0][1];
		pts[i][1][2] = maxs[2];
	}

	for (i=0 ; i<4 ; i++)
	{
		f = Face_Alloc();
		f->texdef = *texdef;
		f->texdef.flags &= ~SURF_KEEP;
		f->texdef.contents &= ~CONTENTS_KEEP;
		f->next = b->brush_faces;
		b->brush_faces = f;
		j = (i+1)%4;

		VectorCopy (pts[j][1], f->planepts[0]);
		VectorCopy (pts[i][1], f->planepts[1]);
		VectorCopy (pts[i][0], f->planepts[2]);
	}
	
	f = Face_Alloc();
	f->texdef = *texdef;
	f->texdef.flags &= ~SURF_KEEP;
	f->texdef.contents &= ~CONTENTS_KEEP;
	f->next = b->brush_faces;
	b->brush_faces = f;

	VectorCopy (pts[0][1], f->planepts[0]);
	VectorCopy (pts[1][1], f->planepts[1]);
	VectorCopy (pts[2][1], f->planepts[2]);

	f = Face_Alloc();
	f->texdef = *texdef;
	f->texdef.flags &= ~SURF_KEEP;
	f->texdef.contents &= ~CONTENTS_KEEP;
	f->next = b->brush_faces;
	b->brush_faces = f;

	VectorCopy (pts[2][0], f->planepts[0]);
	VectorCopy (pts[1][0], f->planepts[1]);
	VectorCopy (pts[0][0], f->planepts[2]);

	return b;
}

/*
=============
Brush_CreatePyramid

Create non-textured pyramid for light entities
The brush is NOT linked to any list
=============
*/
brush_t	*Brush_CreatePyramid (vec3_t mins, vec3_t maxs, texdef_t *texdef)
{
	//++timo handle new brush primitive ? return here ??
	return Brush_Create(mins, maxs, texdef);

	for (int i=0 ; i<3 ; i++)
		if (maxs[i] < mins[i])
			Error ("Brush_InitSolid: backwards");

	brush_t* b = Brush_Alloc();

	vec3_t corners[4];

	float fMid = Q_rint(mins[2] + (Q_rint((maxs[2] - mins[2]) / 2)));

	corners[0][0] = mins[0];
	corners[0][1] = mins[1];
	corners[0][2] = fMid;

	corners[1][0] = mins[0];
	corners[1][1] = maxs[1];
	corners[1][2] = fMid;

	corners[2][0] = maxs[0];
	corners[2][1] = maxs[1];
	corners[2][2] = fMid;

	corners[3][0] = maxs[0];
	corners[3][1] = mins[1];
	corners[3][2] = fMid;

	vec3_t top, bottom;

	top[0] = Q_rint(mins[0] + ((maxs[0] - mins[0]) / 2));
	top[1] = Q_rint(mins[1] + ((maxs[1] - mins[1]) / 2));
	top[2] = Q_rint(maxs[2]);

	VectorCopy(top, bottom);
	bottom[2] = mins[2];

	// sides
	for (i = 0; i < 4; i++)
	{
		face_t* f = Face_Alloc();
		f->texdef = *texdef;
		f->texdef.flags &= ~SURF_KEEP;
		f->texdef.contents &= ~CONTENTS_KEEP;
		f->next = b->brush_faces;
		b->brush_faces = f;
		int j = (i+1)%4;

		VectorCopy (top, f->planepts[0]);
		VectorCopy (corners[i], f->planepts[1]);
		VectorCopy(corners[j], f->planepts[2]);

		f = Face_Alloc();
		f->texdef = *texdef;
		f->texdef.flags &= ~SURF_KEEP;
		f->texdef.contents &= ~CONTENTS_KEEP;
		f->next = b->brush_faces;
		b->brush_faces = f;

		VectorCopy (bottom, f->planepts[2]);
		VectorCopy (corners[i], f->planepts[1]);
		VectorCopy(corners[j], f->planepts[0]);
	}

	return b;
}




/*
=============
Brush_MakeSided

Makes the current brush have the given number of 2d sides
=============
*/
void Brush_MakeSided (int sides)
{
	int		i, axis;
	vec3_t	mins, maxs;
	brush_t	*b;
	texdef_t	*texdef;
	face_t	*f;
	vec3_t	mid;
	float	width;
	float	sv, cv;

	if (sides < 3)
	{
		Sys_Status ("Bad sides number", 0);
		return;
	}

	if (sides >= MAX_POINTS_ON_WINDING-4)
	{
		Sys_Printf("too many sides.\n");
		return;
	}

	if (!QE_SingleBrush ())
	{
		Sys_Status ("Must have a single brush selected", 0 );
		return;
	}

	b = selected_brushes.next;
	VectorCopy (b->mins, mins);
	VectorCopy (b->maxs, maxs);
	texdef = &g_qeglobals.d_texturewin.texdef;

	Brush_Free (b);

	if (g_pParentWnd->ActiveXY())
	{
		switch(g_pParentWnd->ActiveXY()->GetViewType())
		{
			case XY: axis = 2; break;
			case XZ: axis = 1; break;
			case YZ: axis = 0; break;
		}
	}
	else
	{
		axis = 2;
	}

	// find center of brush
	width = 8;
	for (i = 0; i < 3; i++)
	{
		mid[i] = (maxs[i] + mins[i]) * 0.5;
		if (i == axis) continue;
		if ((maxs[i] - mins[i]) * 0.5 > width)
			width = (maxs[i] - mins[i]) * 0.5;
	}

	b = Brush_Alloc();
		
	// create top face
	f = Face_Alloc();
	f->texdef = *texdef;
	f->next = b->brush_faces;
	b->brush_faces = f;

	f->planepts[2][(axis+1)%3] = mins[(axis+1)%3]; f->planepts[2][(axis+2)%3] = mins[(axis+2)%3]; f->planepts[2][axis] = maxs[axis];
	f->planepts[1][(axis+1)%3] = maxs[(axis+1)%3]; f->planepts[1][(axis+2)%3] = mins[(axis+2)%3]; f->planepts[1][axis] = maxs[axis];
	f->planepts[0][(axis+1)%3] = maxs[(axis+1)%3]; f->planepts[0][(axis+2)%3] = maxs[(axis+2)%3]; f->planepts[0][axis] = maxs[axis];

	// create bottom face
	f = Face_Alloc();
	f->texdef = *texdef;
	f->next = b->brush_faces;
	b->brush_faces = f;

	f->planepts[0][(axis+1)%3] = mins[(axis+1)%3]; f->planepts[0][(axis+2)%3] = mins[(axis+2)%3]; f->planepts[0][axis] = mins[axis];
	f->planepts[1][(axis+1)%3] = maxs[(axis+1)%3]; f->planepts[1][(axis+2)%3] = mins[(axis+2)%3]; f->planepts[1][axis] = mins[axis];
	f->planepts[2][(axis+1)%3] = maxs[(axis+1)%3]; f->planepts[2][(axis+2)%3] = maxs[(axis+2)%3]; f->planepts[2][axis] = mins[axis];

	for (i=0 ; i<sides ; i++)
	{
		f = Face_Alloc();
		f->texdef = *texdef;
		f->next = b->brush_faces;
		b->brush_faces = f;

		sv = sin (i*3.14159265*2/sides);
		cv = cos (i*3.14159265*2/sides);

		f->planepts[0][(axis+1)%3] = floor(mid[(axis+1)%3]+width*cv+0.5);
		f->planepts[0][(axis+2)%3] = floor(mid[(axis+2)%3]+width*sv+0.5);
		f->planepts[0][axis] = mins[axis];

		f->planepts[1][(axis+1)%3] = f->planepts[0][(axis+1)%3];
		f->planepts[1][(axis+2)%3] = f->planepts[0][(axis+2)%3];
		f->planepts[1][axis] = maxs[axis];

		f->planepts[2][(axis+1)%3] = floor(f->planepts[0][(axis+1)%3] - width*sv + 0.5);
		f->planepts[2][(axis+2)%3] = floor(f->planepts[0][(axis+2)%3] + width*cv + 0.5);
		f->planepts[2][axis] = maxs[axis];
	}

	Brush_AddToList (b, &selected_brushes);

	Entity_LinkBrush (world_entity, b);

	Brush_Build( b );

	Sys_UpdateWindows (W_ALL);
}



/*
=============
Brush_Free

Frees the brush with all of its faces and display list.
Unlinks the brush from whichever chain it is in.
Decrements the owner entity's brushcount.
Removes owner entity if this was the last brush
unless owner is the world.
Removes from groups
=============
*/
void Brush_Free (brush_t *b, bool bRemoveNode)
{
	face_t	*f, *next;
	epair_t	*ep, *enext;

	// remove from group
	if (bRemoveNode)
		Group_RemoveBrush(b);

	// free the patch if it's there
	if (b->patchBrush)
	{
		Patch_Delete(b->pPatch);
	}

	// free faces
	for (f=b->brush_faces ; f ; f=next)
	{
		next = f->next;
		Face_Free( f );
	}

	//Timo : free brush epairs
	for (ep = b->epairs ; ep ; ep=enext )
	{
		enext = ep->next;
		free (ep->key);
		free (ep->value);
		free (ep);
	}

	// unlink from active/selected list
	if (b->next)
		Brush_RemoveFromList (b);

	// unlink from entity list
	if (b->onext)
		Entity_UnlinkBrush (b);

	free (b);
}

/*
=============
Face_MemorySize
=============
*/
int Face_MemorySize(face_t *f )
{
	int size = 0;

	if (f->face_winding)
	{
		size += _msize(f->face_winding);
	}
	//f->texdef.~texdef_t();;
	size += _msize(f);
	return size;
}

/*
=============
Brush_MemorySize
=============
*/
int Brush_MemorySize(brush_t *b)
{
	face_t	*f;
	epair_t	*ep;
	int size = 0;

	//
	if (b->patchBrush)
	{
		size += Patch_MemorySize(b->pPatch);
	}
	//
	for (f = b->brush_faces; f; f = f->next)
	{
		size += Face_MemorySize(f);
	}
	//
	for (ep = b->epairs; ep; ep = ep->next )
	{
		size += _msize(ep->key);
		size += _msize(ep->value);
		size += _msize(ep);
	}
	size += _msize(b);
	return size;
}


/*
============
Brush_Clone

Does NOT add the new brush to any lists
============
*/
brush_t *Brush_Clone (brush_t *b)
{
	brush_t	*n = NULL;
	face_t	*f, *nf;

	if (b->patchBrush)
	{
		patchMesh_t *p = Patch_Duplicate(b->pPatch);
		Brush_RemoveFromList(p->pSymbiot);
		Entity_UnlinkBrush(p->pSymbiot);
		n = p->pSymbiot;
	}
	else
	{
  	n = Brush_Alloc();
	  n->numberId = g_nBrushId++;
		n->owner = b->owner;
		for (f=b->brush_faces ; f ; f=f->next)
		{
			nf = Face_Clone( f );
			nf->next = n->brush_faces;
			n->brush_faces = nf;
		}
	}

	return n;
}

/*
============
Brush_Clone

Does NOT add the new brush to any lists
============
*/
brush_t *Brush_FullClone(brush_t *b)
{
	brush_t	*n = NULL;
	face_t *f, *nf, *f2, *nf2;
	int j;

	if (b->patchBrush)
	{
		patchMesh_t *p = Patch_Duplicate(b->pPatch);
		Brush_RemoveFromList(p->pSymbiot);
		Entity_UnlinkBrush(p->pSymbiot);
		n = p->pSymbiot;
		n->owner = b->owner;
		Brush_Build(n);
	}
	else
	{
  	n = Brush_Alloc();
   	n->numberId = g_nBrushId++;
		n->owner = b->owner;
		VectorCopy(b->mins, n->mins);
		VectorCopy(b->maxs, n->maxs);
		//
		for (f = b->brush_faces; f; f = f->next)
		{
			if (f->original) continue;
			nf = Face_FullClone(f);
			nf->next = n->brush_faces;
			n->brush_faces = nf;
			//copy all faces that have the original set to this face
			for (f2 = b->brush_faces; f2; f2 = f2->next)
			{
				if (f2->original == f)
				{
					nf2 = Face_FullClone(f2);
					nf2->next = n->brush_faces;
					n->brush_faces = nf2;
					//set original
					nf2->original = nf;
				}
			}
		}
		for (nf = n->brush_faces; nf; nf = nf->next)
		{
			Face_SetColor(n, nf, 1.0);
			if (nf->face_winding)
      {
        if (g_qeglobals.m_bBrushPrimitMode)
    			EmitBrushPrimitTextureCoordinates(nf,nf->face_winding);
        else
        {
				  for (j = 0; j < nf->face_winding->numpoints; j++)
  					EmitTextureCoordinates(nf->face_winding->points[j], nf->d_texture, nf);
        }
      }
		}
  }
	return n;
}

/*
==============
Brush_Ray

Itersects a ray with a brush
Returns the face hit and the distance along the ray the intersection occured at
Returns NULL and 0 if not hit at all
==============
*/
face_t *Brush_Ray (vec3_t origin, vec3_t dir, brush_t *b, float *dist)
{
	face_t	*f, *firstface;
	vec3_t	p1, p2;
	float	frac, d1, d2;
	int		i;

	VectorCopy (origin, p1);
	for (i=0 ; i<3 ; i++)
		p2[i] = p1[i] + dir[i]*16384;

	for (f=b->brush_faces ; f ; f=f->next)
	{
		d1 = DotProduct (p1, f->plane.normal) - f->plane.dist;
		d2 = DotProduct (p2, f->plane.normal) - f->plane.dist;
		if (d1 >= 0 && d2 >= 0)
		{
			*dist = 0;
			return NULL;	// ray is on front side of face
		}
		if (d1 <=0 && d2 <= 0)
			continue;
	// clip the ray to the plane
		frac = d1 / (d1 - d2);
		if (d1 > 0)
		{
			firstface = f;
			for (i=0 ; i<3 ; i++)
				p1[i] = p1[i] + frac *(p2[i] - p1[i]);
		}
		else
		{
			for (i=0 ; i<3 ; i++)
				p2[i] = p1[i] + frac *(p2[i] - p1[i]);
		}
	}

	// find distance p1 is along dir
	VectorSubtract (p1, origin, p1);
	d1 = DotProduct (p1, dir);

	*dist = d1;

	return firstface;
}

//PGM
face_t *Brush_Point (vec3_t origin, brush_t *b)
{
	face_t	*f;
	float	d1;

	for (f=b->brush_faces ; f ; f=f->next)
	{
		d1 = DotProduct (origin, f->plane.normal) - f->plane.dist;
		if (d1 > 0)
		{
			return NULL;	// point is on front side of face
		}
	}

	return b->brush_faces;
}
//PGM


void	Brush_AddToList (brush_t *b, brush_t *list)
{
	if (b->next || b->prev)
		Error ("Brush_AddToList: allready linked");
	
	if (list == &selected_brushes || list == &active_brushes)
	{
		if (b->patchBrush && list == &selected_brushes)
		{
			Patch_Select(b->pPatch);
		}
	}
	b->next = list->next;
	list->next->prev = b;
	list->next = b;
	b->prev = list;
	
	// TTimo messaging
	DispatchRadiantMsg( RADIANT_SELECTION );	
}

void	Brush_RemoveFromList (brush_t *b)
{
	if (!b->next || !b->prev)
		Error ("Brush_RemoveFromList: not linked");
	
	if (b->patchBrush)
	{
		Patch_Deselect(b->pPatch);
		//Patch_Deselect(b->nPatchID);
	}
	b->next->prev = b->prev;
	b->prev->next = b->next;
	b->next = b->prev = NULL;
}

/*
===============
SetFaceTexdef

Doesn't set the curve flags

NOTE : ( TTimo )
	never trust f->d_texture here, f->texdef and f->d_texture are out of sync when called by Brush_SetTexture
	use Texture_ForName() to find the right shader
	FIXME : send the right shader ( qtexture_t * ) in the parameters ?

TTimo: surface plugin, added an IPluginTexdef* parameter
		if not NULL, get ->Copy() of it into the face ( and remember to hook )
		if NULL, ask for a default

TTimo - shader code cleanup
  added IShader* parameter
===============
*/
void SetFaceTexdef2 (brush_t *b, face_t *f, IShader *pShader, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef* pPlugTexdef) {
	int		oldFlags;
	int		oldContents;
	face_t	*tf;

	oldFlags = f->texdef.flags;
	oldContents = f->texdef.contents;
	if (g_qeglobals.m_bBrushPrimitMode)
	{
		f->texdef = *texdef;
		ConvertTexMatWithQTexture( brushprimit_texdef, NULL, &f->brushprimit_texdef, QERApp_Shader_ForName( f->texdef.name )->getTexture() );
	}
	else
		if (bFitScale)
		{
			f->texdef = *texdef;
			// fit the scaling of the texture on the actual plane
			vec3_t p1,p2,p3; // absolute coordinates
			// compute absolute coordinates
			ComputeAbsolute(f,p1,p2,p3);
			// compute the scale
			vec3_t vx,vy;
			VectorSubtract(p2,p1,vx);
			VectorNormalize(vx);
			VectorSubtract(p3,p1,vy);
			VectorNormalize(vy);
			// assign scale
			VectorScale(vx,texdef->scale[0],vx);
			VectorScale(vy,texdef->scale[1],vy);
			VectorAdd(p1,vx,p2);
			VectorAdd(p1,vy,p3);
			// compute back shift scale rot
			AbsoluteToLocal(f->plane,f,p1,p2,p3);
		}
		else
			f->texdef = *texdef;
	f->texdef.flags = (f->texdef.flags & ~SURF_KEEP) | (oldFlags & SURF_KEEP);
	f->texdef.contents = (f->texdef.contents & ~CONTENTS_KEEP) | (oldContents & CONTENTS_KEEP);

	// surface plugin
	if (g_qeglobals.bSurfacePropertiesPlugin)
	{
#ifdef _DEBUG
		if (!f->pData)
			Sys_Printf("ERROR: unexpected IPluginTexdef* is NULL in SetFaceTexdef\n");
		else
#endif
			GETPLUGINTEXDEF(f)->DecRef();
		IPluginTexdef *pTexdef = NULL;
		if ( pPlugTexdef )
		{
			pTexdef = pPlugTexdef->Copy();
			pTexdef->Hook( f );
		}
		else
			pTexdef = g_SurfaceTable.m_pfnTexdefAlloc( f );
		f->pData = pTexdef;
	}

	// if this is a curve face, set all other curve faces to the same texdef
	if (f->texdef.flags & SURF_CURVE) 
	{
		for (tf = b->brush_faces ; tf ; tf = tf->next) 
		{
			if (tf->texdef.flags & SURF_CURVE) 
				tf->texdef = f->texdef;
		}
	}
}

/*
===============
SetFaceTexdef

Doesn't set the curve flags

NOTE : ( TTimo )
	never trust f->d_texture here, f->texdef and f->d_texture are out of sync when called by Brush_SetTexture
	use Texture_ForName() to find the right shader
	FIXME : send the right shader ( qtexture_t * ) in the parameters ?

TTimo: surface plugin, added an IPluginTexdef* parameter
		if not NULL, get ->Copy() of it into the face ( and remember to hook )
		if NULL, ask for a default
===============
*/
void SetFaceTexdef (brush_t *b, face_t *f, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef* pPlugTexdef) {
	int		oldFlags;
	int		oldContents;
	face_t	*tf;

	oldFlags = f->texdef.flags;
	oldContents = f->texdef.contents;
	if (g_qeglobals.m_bBrushPrimitMode)
	{
		f->texdef = *texdef;
		ConvertTexMatWithQTexture( brushprimit_texdef, NULL, &f->brushprimit_texdef, QERApp_Shader_ForName( f->texdef.name )->getTexture() );
	}
	else
		if (bFitScale)
		{
			f->texdef = *texdef;
			// fit the scaling of the texture on the actual plane
			vec3_t p1,p2,p3; // absolute coordinates
			// compute absolute coordinates
			ComputeAbsolute(f,p1,p2,p3);
			// compute the scale
			vec3_t vx,vy;
			VectorSubtract(p2,p1,vx);
			VectorNormalize(vx);
			VectorSubtract(p3,p1,vy);
			VectorNormalize(vy);
			// assign scale
			VectorScale(vx,texdef->scale[0],vx);
			VectorScale(vy,texdef->scale[1],vy);
			VectorAdd(p1,vx,p2);
			VectorAdd(p1,vy,p3);
			// compute back shift scale rot
			AbsoluteToLocal(f->plane,f,p1,p2,p3);
		}
		else
			f->texdef = *texdef;
	f->texdef.flags = (f->texdef.flags & ~SURF_KEEP) | (oldFlags & SURF_KEEP);
	f->texdef.contents = (f->texdef.contents & ~CONTENTS_KEEP) | (oldContents & CONTENTS_KEEP);

	// surface plugin
	if (g_qeglobals.bSurfacePropertiesPlugin)
	{
#ifdef _DEBUG
		if (!f->pData)
			Sys_Printf("ERROR: unexpected IPluginTexdef* is NULL in SetFaceTexdef\n");
		else
#endif
			GETPLUGINTEXDEF(f)->DecRef();
		IPluginTexdef *pTexdef = NULL;
		if ( pPlugTexdef )
		{
			pTexdef = pPlugTexdef->Copy();
			pTexdef->Hook( f );
		}
		else
			pTexdef = g_SurfaceTable.m_pfnTexdefAlloc( f );
		f->pData = pTexdef;
	}

	// if this is a curve face, set all other curve faces to the same texdef
	if (f->texdef.flags & SURF_CURVE) 
	{
		for (tf = b->brush_faces ; tf ; tf = tf->next) 
		{
			if (tf->texdef.flags & SURF_CURVE) 
				tf->texdef = f->texdef;
		}
	}
}

//++timo clean
#ifdef _DEBUG
/*
TTimo - shader code cleanup
  added pShader parameter
*/
void Brush_SetTexture2 (brush_t *b, IShader *pShader, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef* pTexdef)
{
	for (face_t* f = b->brush_faces ; f ; f = f->next) 
	{
		SetFaceTexdef2 (b, f, pShader, texdef, brushprimit_texdef, bFitScale, pTexdef);
	}
	Brush_Build( b );
	if (b->patchBrush)
	{
		//++timo clean
//		Sys_Printf("WARNING: Brush_SetTexture needs surface plugin code for patches\n");
		Patch_SetTexture(b->pPatch, texdef, pTexdef );
	}
}
#endif

void Brush_SetTexture (brush_t *b, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef* pTexdef)
{
	for (face_t* f = b->brush_faces ; f ; f = f->next) 
		SetFaceTexdef (b, f, texdef, brushprimit_texdef, bFitScale, pTexdef);
	Brush_Build( b );
	if (b->patchBrush)
		Patch_SetTexture(b->pPatch, texdef, pTexdef );
}


qboolean ClipLineToFace (vec3_t p1, vec3_t p2, face_t *f)
{
	float	d1, d2, fr;
	int		i;
	float	*v;

	d1 = DotProduct (p1, f->plane.normal) - f->plane.dist;
	d2 = DotProduct (p2, f->plane.normal) - f->plane.dist;

	if (d1 >= 0 && d2 >= 0)
		return false;		// totally outside
	if (d1 <= 0 && d2 <= 0)
		return true;		// totally inside

	fr = d1 / (d1 - d2);

	if (d1 > 0)
		v = p1;
	else
		v = p2;

	for (i=0 ; i<3 ; i++)
		v[i] = p1[i] + fr*(p2[i] - p1[i]);

	return true;
}


int AddPlanept (float *f)
{
	int		i;

	for (i=0 ; i<g_qeglobals.d_num_move_points ; i++)
		if (g_qeglobals.d_move_points[i] == f)
			return 0;
	g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = f;
	return 1;
}

/*
==============
Brush_SelectFaceForDragging

Adds the faces planepts to move_points, and
rotates and adds the planepts of adjacent face if shear is set
==============
*/
void Brush_SelectFaceForDragging (brush_t *b, face_t *f, qboolean shear)
{
	int		i;
	face_t	*f2;
	winding_t	*w;
	float	d;
	brush_t	*b2;
	int		c;

	if (b->owner->eclass->fixedsize)
		return;

	c = 0;
	for (i=0 ; i<3 ; i++)
		c += AddPlanept (f->planepts[i]);
	if (c == 0)
		return;		// allready completely added

	// select all points on this plane in all brushes the selection
	for (b2=selected_brushes.next ; b2 != &selected_brushes ; b2 = b2->next)
	{
		if (b2 == b)
			continue;
		for (f2=b2->brush_faces ; f2 ; f2=f2->next)
		{
			for (i=0 ; i<3 ; i++)
				if (fabs(DotProduct(f2->planepts[i], f->plane.normal)
				-f->plane.dist) > ON_EPSILON)
					break;
			if (i==3)
			{	// move this face as well
				Brush_SelectFaceForDragging (b2, f2, shear);
				break;
			}
		}
	}


	// if shearing, take all the planes adjacent to 
	// selected faces and rotate their points so the
	// edge clipped by a selcted face has two of the points
	if (!shear)
		return;

	for (f2=b->brush_faces ; f2 ; f2=f2->next)
	{
		if (f2 == f)
			continue;
		w = Brush_MakeFaceWinding (b, f2);
		if (!w)
			continue;

		// any points on f will become new control points
		for (i=0 ; i<w->numpoints ; i++)
		{
			d = DotProduct (w->points[i], f->plane.normal) 
				- f->plane.dist;
			if (d > -ON_EPSILON && d < ON_EPSILON)
				break;
		}

		//
		// if none of the points were on the plane,
		// leave it alone
		//
		if (i != w->numpoints)
		{
			if (i == 0)
			{	// see if the first clockwise point was the
				// last point on the winding
				d = DotProduct (w->points[w->numpoints-1]
					, f->plane.normal) - f->plane.dist;
				if (d > -ON_EPSILON && d < ON_EPSILON)
					i = w->numpoints - 1;
			}

			AddPlanept (f2->planepts[0]);

			VectorCopy (w->points[i], f2->planepts[0]);
			if (++i == w->numpoints)
				i = 0;
			
			// see if the next point is also on the plane
			d = DotProduct (w->points[i]
				, f->plane.normal) - f->plane.dist;
			if (d > -ON_EPSILON && d < ON_EPSILON)
				AddPlanept (f2->planepts[1]);

			VectorCopy (w->points[i], f2->planepts[1]);
			if (++i == w->numpoints)
				i = 0;

			// the third point is never on the plane

			VectorCopy (w->points[i], f2->planepts[2]);
		}

		free(w);
	}
}

/*
==============
Brush_SideSelect

The mouse click did not hit the brush, so grab one or more side
planes for dragging
==============
*/
void Brush_SideSelect (brush_t *b, vec3_t origin, vec3_t dir
					   , qboolean shear)
{
	face_t	*f, *f2;
	vec3_t	p1, p2;

  //if (b->patchBrush)
  //  return;
    //Patch_SideSelect(b->nPatchID, origin, dir);
	for (f=b->brush_faces ; f ; f=f->next)
	{
		VectorCopy (origin, p1);
		VectorMA (origin, 16384, dir, p2);

		for (f2=b->brush_faces ; f2 ; f2=f2->next)
		{
			if (f2 == f)
				continue;
			ClipLineToFace (p1, p2, f2);
		}

		if (f2)
			continue;

		if (VectorCompare (p1, origin))
			continue;
		if (ClipLineToFace (p1, p2, f))
			continue;

		Brush_SelectFaceForDragging (b, f, shear);
	}

	
}

bool g_bBuildWindingsNoTexBuild = false;

// TTimo: don't rebuild pShader and d_texture if it doesn't seem necessary
//    saves quite a lot of time, but on the other hand we've gotta make sure we clean the d_texture in some cases
//    ie when we want to update a shader
//    default will make Radiant rebuild the texture, but it can be turned off by setting the flag g_bBuildWindingsNoTexBuild
void Brush_BuildWindings( brush_t *b, bool bSnap )
{
	winding_t *w;
	face_t    *face;
	vec_t      v;

	if (bSnap)
		Brush_SnapPlanepts( b );

	// clear the mins/maxs bounds
	b->mins[0] = b->mins[1] = b->mins[2] = 99999;
	b->maxs[0] = b->maxs[1] = b->maxs[2] = -99999;

	Brush_MakeFacePlanes (b);

	face = b->brush_faces;

	float fCurveColor = 1.0;

	for ( ; face ; face=face->next)
	{
		int i, j;
		free(face->face_winding);
		w = face->face_winding = Brush_MakeFaceWinding (b, face);
    //++timo only experimental!
    if (!g_bBuildWindingsNoTexBuild || !face->d_texture)
    {
#ifdef _DEBUG
      // if there's no d_texture, then we expect pShader to be empty
      if (!face->d_texture && face->pShader)
        Sys_Printf("ERROR: unexpected face->pShader != NULL with face->d_texture == NULL in Brush_BuildWindings\n");
#endif
      if (face->pShader)
        face->pShader->DecRef();
      face->pShader = QERApp_Shader_ForName( face->texdef.name );
      face->pShader->IncRef();
      face->d_texture = face->pShader->getTexture();
    }

		if (!w)
			continue;
	
		for (i=0 ; i<w->numpoints ; i++)
		{
			// add to bounding box
			for (j=0 ; j<3 ; j++)
			{
				v = w->points[i][j];
				if (v > b->maxs[j])
					b->maxs[j] = v;
				if (v < b->mins[j])
					b->mins[j] = v;
			}
		}
		Face_SetColor (b, face, fCurveColor);

		fCurveColor -= .10;
		if (fCurveColor <= 0)
			fCurveColor = 1.0;

		// computing ST coordinates for the windings
		if (g_qeglobals.m_bBrushPrimitMode)
		{
			if (g_qeglobals.bNeedConvert)
			{
				// we have parsed old brushes format and need conversion
				// convert old brush texture representation to new format
				FaceToBrushPrimitFace(face);
#ifdef _DEBUG
				// use old texture coordinates code to check against
			    for (i=0 ; i<w->numpoints ; i++)
					EmitTextureCoordinates( w->points[i], face->d_texture, face);
#endif
			}
			// use new texture representation to compute texture coordinates
			// in debug mode we will check against old code and warn if there are differences
			EmitBrushPrimitTextureCoordinates(face,w);
		}
		else
		{
      if (g_qeglobals.bNeedConvert)
      {
        // we have parsed brush primitives and need conversion back to standard format
        // NOTE: converting back is a quick hack, there's some information lost and we can't do anything about it
				// FIXME: if we normalize the texture matrix to a standard 2x2 size, we end up with wrong scaling
				// I tried various tweaks, no luck .. seems shifting is lost
        brushprimit_texdef_t aux;
        ConvertTexMatWithQTexture( &face->brushprimit_texdef, face->d_texture, &aux, NULL );
        TexMatToFakeTexCoords( aux.coords, face->texdef.shift, &face->texdef.rotate, face->texdef.scale );
				face->texdef.scale[0]/=2.0;
				face->texdef.scale[1]/=2.0;
      }
		  for (i=0 ; i<w->numpoints ; i++)
			  EmitTextureCoordinates( w->points[i], face->d_texture, face);
		}
	}
}

/*
==================
Brush_RemoveEmptyFaces

Frees any overconstraining faces
==================
*/
void Brush_RemoveEmptyFaces ( brush_t *b )
{
	face_t	*f, *next;

	f = b->brush_faces;
	b->brush_faces = NULL;

	for ( ; f ; f=next)
	{
		next = f->next;
		if (!f->face_winding)
			Face_Free (f);
		else
		{
			f->next = b->brush_faces;
			b->brush_faces = f;
		}

	}
}

void Brush_SnapToGrid(brush_t *pb)
{
  // TTimo: some brushes are "special" and should not be snapped
  // specially fixed-size entity ones
  if (pb->owner->eclass->fixedsize == 1)
  {
    // snap the origin
    pb->owner->origin[0] = floor (pb->owner->origin[0] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize;
    pb->owner->origin[1] = floor (pb->owner->origin[1] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize;
    pb->owner->origin[2] = floor (pb->owner->origin[2] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize;
  }
  else
  {
    for (face_t *f = pb->brush_faces ; f; f = f->next)
    {
      for (int i = 0 ;i < 3 ;i++)
      {
        for (int j = 0 ;j < 3 ; j++)
        {
          f->planepts[i][j] = floor (f->planepts[i][j] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize;
        }
      }
    }
    Brush_Build(pb);
  }
}

void Brush_Rotate(brush_t *b, vec3_t vAngle, vec3_t vOrigin, bool bBuild)
{
	for (face_t* f=b->brush_faces ; f ; f=f->next)
	{
		for (int i=0 ; i<3 ; i++)
		{
			VectorRotate(f->planepts[i], vAngle, vOrigin, f->planepts[i]);
		}
	}
	if (bBuild)
	{
		Brush_Build(b, false, false);
	}
}

void Brush_Center(brush_t *b, vec3_t vNewCenter)
{
  vec3_t vMid;
  // get center of the brush
  for (int j = 0; j < 3; j++)
  {
    vMid[j] = b->mins[j] + abs((b->maxs[j] - b->mins[j]) * 0.5);
  }
  // calc distance between centers
  VectorSubtract(vNewCenter, vMid, vMid);
  Brush_Move(b, vMid, true);

}

// only designed for fixed size entity brushes
void Brush_Resize(brush_t *b, vec3_t vMin, vec3_t vMax)
{
  brush_t *b2 = Brush_Create(vMin, vMax, &b->brush_faces->texdef);

  face_t *next;
	for (face_t *f=b->brush_faces ; f ; f=next)
	{
		next = f->next;
		Face_Free( f );
	}

  b->brush_faces = b2->brush_faces;

	// unlink from active/selected list
	if (b2->next)
  Brush_RemoveFromList (b2);
  free(b2);
  Brush_Build(b, true);
}


eclass_t* HasModel(brush_t *b)
{
  vec3_t vMin, vMax;
  vMin[0] = vMin[1] = vMin[2] = 9999;
  vMax[0] = vMax[1] = vMax[2] = -9999;

  if (b->owner->md3Class != NULL)
  {
    return b->owner->md3Class;
  }

  if (Eclass_hasModel(b->owner->eclass, vMin, vMax))
  {
    return b->owner->eclass;
  }

  eclass_t *e = NULL;
  // FIXME: entity needs to track whether a cache hit failed and not ask again
  if (b->owner->eclass->nShowFlags & ECLASS_MISCMODEL)
  {
    char *pModel = ValueForKey(b->owner, "model");
    if (pModel != NULL && strlen(pModel) > 0)
    {
      e = GetCachedModel(b->owner, pModel, vMin, vMax);
      if (e != NULL)
      {
        // we need to scale the brush to the proper size based on the model load
        // recreate brush just like in load/save

		    VectorAdd (vMin, b->owner->origin, vMin);
		    VectorAdd (vMax, b->owner->origin, vMax);

        Brush_Resize(b, vMin, vMax);
        float a = FloatForKey (b->owner, "angle");
        if (a)
        {
          vec3_t vAngle;
          vAngle[0] = vAngle[1] = 0;
          vAngle[2] = a;
          Brush_Rotate(b, vAngle, b->owner->origin);
        }

/*
        //
        vec3_t vTemp, vTemp2;
        VectorSubtract(b->maxs, b->mins, vTemp);
        VectorSubtract(vMax, vMin, vTemp2);
        for (int i = 0; i < 3; i++)
        {
          if (vTemp[i] != 0)
          {
            vTemp2[i] /= vTemp[i];
          }
        }
        vec3_t vMid, vMid2;
        vMid[0] = vMid[1] = vMid[2] = 0.0;
        vMid2[0] = vMid2[1] = vMid2[2] = 0.0;

        for (int j = 0; j < 3; j++)
        {
          vMid2[j] = b->mins[j] + abs((b->maxs[j] - b->mins[j]) * 0.5);
        }

        //VectorSubtract(vMid2, vMid, vMid2);

		    for (face_t* f=b->brush_faces ; f ; f=f->next)
		    {
			    for (int i=0 ; i<3 ; i++)
			    {

            // scale
            VectorSubtract(f->planepts[i], vMid2, f->planepts[i]);
            f->planepts[i][0] *= vTemp2[0];
            f->planepts[i][1] *= vTemp2[1];
            f->planepts[i][2] *= vTemp2[2];
            VectorAdd(f->planepts[i], vMid2, f->planepts[i]);
          }
        }

        //Brush_Center(b, b->owner->origin);

        //Brush_SnapToGrid(b);
/*
        float a = FloatForKey (b->owner, "angle");
        if (a)
        {
          vec3_t vAngle;
          vAngle[0] = vAngle[1] = 0;
          vAngle[2] = a;
          Brush_Rotate(b, vAngle, b->owner->origin);
        }
        else
        {
          Brush_Build(b, true);
*/
//        }

        b->bModelFailed = false;
      }
      else
      {
        b->bModelFailed = true;
      }
    } 
  }
  return e;
}

static bool g_bInPaintedModel = false;
static bool g_bDoIt = false;
bool PaintedModel(brush_t *b, bool bOkToTexture)
{
    if (g_bInPaintedModel)
    { 
      return true;
    }
    
    if (g_PrefsDlg.m_nEntityShowState == ENTITY_BOX || b->bModelFailed)
    {
      return false;
    }
    else if (!IsBrushSelected(b) && (g_PrefsDlg.m_nEntityShowState & ENTITY_SELECTED_ONLY))
    {
	    return false;
    }

    g_bInPaintedModel = true;
    bool bReturn = false;

    eclass_t *pEclass = HasModel(b);

    if (pEclass)
    {
      qglPushAttrib(GL_ALL_ATTRIB_BITS);
      entitymodel *model = pEclass->model;


      float a = FloatForKey (b->owner, "angle");
      while (model != NULL)
      {
        if (bOkToTexture == false || g_PrefsDlg.m_nEntityShowState & ENTITY_WIREFRAME || model->nTextureBind == -1)	// skinned
        {
	        qglDisable( GL_CULL_FACE );
	        qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
	        qglDisable(GL_TEXTURE_2D);
          qglColor3fv(pEclass->color);
        }
        else
        {
          qglColor3f(1, 1, 1);
          qglEnable(GL_TEXTURE_2D);
	        qglBindTexture( GL_TEXTURE_2D, model->nTextureBind );
        }
        vec3_t v;
        
        int i,j;
        VectorAdd(b->maxs, b->mins, v);
        VectorScale(v, 0.5, v);
        VectorCopy(b->owner->origin, v);

	      
        //for (i = 0; i < 3; i++)
        //{
        //  v[i] -= (pEclass->mins[i] - b->mins[i]);
        //}

        //if (model->nModelPosition)
        //{
		      //v[2] = b->mins[2] - (pEclass->mins[2]);
        //}

        float s, c;
	      if (a)
        {
		      s = sin (a/180*Q_PI);
		      c = cos (a/180*Q_PI);
        }

        vec3_t vSin;
        vec3_t vCos;
        VectorClear(vSin);
        VectorClear(vCos);
        for ( j = 0; j < 3; j++)
        {
          if (b->owner->vRotation[j])
          {
            vSin[j] = sin(b->owner->vRotation[j]/180*Q_PI);
            vCos[j] = cos(b->owner->vRotation[j]/180*Q_PI);
          }
        }


	      qglBegin (GL_TRIANGLES);

        vec5_t vTest[3];
        for (i = 0; i < model->nTriCount; i++)
        {
          for (j = 0; j < 3; j++)
          {
#if 1
            float x = model->pTriList[i].v[j][0] + v[0];
            float y = model->pTriList[i].v[j][1] + v[1];
            if (a)
            {
              float x2 = (((x - v[0]) * c) - ((y - v[1]) * s)) + v[0];
              float y2 = (((x - v[0]) * s) + ((y - v[1]) * c)) + v[1];
              x = x2;
              y = y2;
            }
            //qglTexCoord2f (pEclass->pTriList[i].st[j][0] / pEclass->nSkinWidth, pEclass->pTriList[i].st[j][1] / pEclass->nSkinHeight);
            qglTexCoord2f (model->pTriList[i].st[j][0], model->pTriList[i].st[j][1]);
            qglVertex3f(x, y, model->pTriList[i].v[j][2] + v[2]);
#else
            float x = model->pTriList[i].v[j][0] + v[0];
            float y = model->pTriList[i].v[j][1] + v[1];
            float z = model->pTriList[i].v[j][2] + v[2];

            if (b->owner->vRotation[0])
            {
              float y2 = (((y - v[1]) * vCos[0]) - ((z - v[2]) * vSin[0])) + v[1];
              float z2 = (((y - v[1]) * vSin[0]) + ((z - v[2]) * vCos[0])) + v[2];
              y = y2;
              z = z2;
            }
            if (b->owner->vRotation[1])
            {
              float z2 = (((z - v[2]) * vCos[1]) - ((x - v[0]) * vSin[1])) + v[2];
              float x2 = (((z - v[2]) * vSin[1]) + ((x - v[0]) * vCos[1])) + v[0];
              x = x2;
              z = z2;
            }
            if (b->owner->vRotation[2])
            {
              float x2 = (((x - v[0]) * vCos[2]) - ((y - v[1]) * vSin[2])) + v[0];
              float y2 = (((x - v[0]) * vSin[2]) + ((y - v[1]) * vCos[2])) + v[1];
              x = x2;
              y = y2;
            }
            qglTexCoord2f (model->pTriList[i].st[j][0], model->pTriList[i].st[j][1]);
            qglVertex3f(x, y, z);
#endif
            if (g_bDoIt)
            {
              vTest[j][0] = x;
              vTest[j][1] = y;
              vTest[j][2] = model->pTriList[i].v[j][2] + v[2];
              vTest[j][3] = model->pTriList[i].st[j][0];
              vTest[j][4] = model->pTriList[i].st[j][1];
            }

          }
          if (g_bDoIt)
          {
            Patch_FromTriangle(vTest[0], vTest[1], vTest[2]);
          }
        }
        qglEnd();
        if (g_PrefsDlg.m_nEntityShowState & ENTITY_WIREFRAME)	// skinned
        {
          qglEnable(GL_CULL_FACE );
          qglEnable(GL_TEXTURE_2D);
	        qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
        }
        else
        {
	        qglDisable(GL_TEXTURE_2D);
        }
        model = model->pNext;
      }

      if (g_bDoIt)
      {
        g_bDoIt = false;
      }

      vec3_t vColor;
      VectorScale(pEclass->color, 0.50, vColor);

      vec3_t vCenter, vMin, vMax;
      VectorCopy(b->owner->origin, vCenter);

		  qglColor3fv(vColor);
      qglPointSize(4);

      qglBegin(GL_POINTS);
      qglVertex3fv(b->owner->origin);
      qglEnd();

      qglBegin(GL_LINES);
      vCenter[0] -= 8;
      qglVertex3fv(vCenter);
      vCenter[0] += 16;
      qglVertex3fv(vCenter);
      vCenter[0] -= 8;
      vCenter[1] -= 8;
      qglVertex3fv(vCenter);
      vCenter[1] += 16;
      qglVertex3fv(vCenter);
      vCenter[1] -= 8;
      vCenter[2] -= 8;
      qglVertex3fv(vCenter);
      vCenter[2] += 16;
      qglVertex3fv(vCenter);
      vCenter[2] -= 8;
      qglEnd();

      VectorCopy(vCenter, vMin);
      VectorCopy(vCenter, vMax);
      vMin[0] -= 4;
      vMin[1] -= 4;
      vMin[2] -= 4;
      vMax[0] += 4;
      vMax[1] += 4;
      vMax[2] += 4;

		    qglBegin(GL_LINE_LOOP);
          qglVertex3f(vMin[0],vMin[1],vMin[2]);
          qglVertex3f(vMax[0],vMin[1],vMin[2]);
          qglVertex3f(vMax[0],vMax[1],vMin[2]);
          qglVertex3f(vMin[0],vMax[1],vMin[2]);
		    qglEnd();
		    
		    qglBegin(GL_LINE_LOOP);
	        qglVertex3f(vMin[0],vMin[1],vMax[2]);
		      qglVertex3f(vMax[0],vMin[1],vMax[2]);
		      qglVertex3f(vMax[0],vMax[1],vMax[2]);
		      qglVertex3f(vMin[0],vMax[1],vMax[2]);
		    qglEnd();

		    qglBegin(GL_LINES);
  		    qglVertex3f(vMin[0],vMin[1],vMin[2]);
		      qglVertex3f(vMin[0],vMin[1],vMax[2]);
		      qglVertex3f(vMin[0],vMax[1],vMax[2]);
		      qglVertex3f(vMin[0],vMax[1],vMin[2]);
		      qglVertex3f(vMax[0],vMin[1],vMin[2]);
		      qglVertex3f(vMax[0],vMin[1],vMax[2]);
		      qglVertex3f(vMax[0],vMax[1],vMax[2]);
		      qglVertex3f(vMax[0],vMax[1],vMin[2]);
		    qglEnd();


	    if (g_PrefsDlg.m_nEntityShowState & ENTITY_BOXED)
	    {
		    qglColor3fv(pEclass->color);

        vec3_t mins, maxs;
        VectorCopy(b->mins, mins);
        VectorCopy(b->maxs, maxs);
/*
        if (a)
        {
          vec3_t vAngle;
          vAngle[0] = vAngle[1] = 0;
          vAngle[2] = a;
          VectorRotate(mins, vAngle, b->owner->origin, mins);
          VectorRotate(maxs, vAngle, b->owner->origin, maxs);
        }
*/
		    qglBegin(GL_LINE_LOOP);
          qglVertex3f(mins[0],mins[1],mins[2]);
          qglVertex3f(maxs[0],mins[1],mins[2]);
          qglVertex3f(maxs[0],maxs[1],mins[2]);
          qglVertex3f(mins[0],maxs[1],mins[2]);
		    qglEnd();
		    
		    qglBegin(GL_LINE_LOOP);
	        qglVertex3f(mins[0],mins[1],maxs[2]);
		      qglVertex3f(maxs[0],mins[1],maxs[2]);
		      qglVertex3f(maxs[0],maxs[1],maxs[2]);
		      qglVertex3f(mins[0],maxs[1],maxs[2]);
		    qglEnd();

		    qglBegin(GL_LINES);
  		    qglVertex3f(mins[0],mins[1],mins[2]);
		      qglVertex3f(mins[0],mins[1],maxs[2]);
		      qglVertex3f(mins[0],maxs[1],maxs[2]);
		      qglVertex3f(mins[0],maxs[1],mins[2]);
		      qglVertex3f(maxs[0],mins[1],mins[2]);
		      qglVertex3f(maxs[0],mins[1],maxs[2]);
		      qglVertex3f(maxs[0],maxs[1],maxs[2]);
		      qglVertex3f(maxs[0],maxs[1],mins[2]);
		    qglEnd();
      }
	    qglPopAttrib();
      bReturn = true;
    }
    else
    {
      b->bModelFailed = true;
    }

  g_bInPaintedModel = false;
  return bReturn;
}
/*
//++timo moved out to mahlib.h
//++timo remove
void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
{
	float		angle;
	static float		sr, sp, sy, cr, cp, cy;
	// static to help MS compiler fp bugs

	angle = angles[YAW] * Q_PI / 180;
	sy = sin(angle);
	cy = cos(angle);
	angle = angles[PITCH] * Q_PI / 180;
	sp = sin(angle);
	cp = cos(angle);
	angle = angles[ROLL] * Q_PI / 180;
	sr = sin(angle);
	cr = cos(angle);

	if (forward)
	{
		forward[0] = cp*cy;
		forward[1] = cp*sy;
		forward[2] = -sp;
	}
	if (right)
	{
		right[0] = (-1*sr*sp*cy+-1*cr*-sy);
		right[1] = (-1*sr*sp*sy+-1*cr*cy);
		right[2] = -1*sr*cp;
	}
	if (up)
	{
		up[0] = (cr*sp*cy+-sr*-sy);
		up[1] = (cr*sp*sy+-sr*cy);
		up[2] = cr*cp;
	}
}
*/
void FacingVectors (entity_t *e, vec3_t forward, vec3_t right, vec3_t up)
{
	int			angleVal;
	vec3_t		angles;

	angleVal = IntForKey(e, "angle");
	if (angleVal == -1)				// up
	{
		VectorSet(angles, 270, 0, 0);
	}
	else if(angleVal == -2)		// down
	{
		VectorSet(angles, 90, 0, 0);
	}
	else
	{
		VectorSet(angles, 0, angleVal, 0);
	}

	AngleVectors(angles, forward, right, up);
}

void Brush_DrawFacingAngle (brush_t *b, entity_t *e)
{
	vec3_t	forward, right, up;
	vec3_t	endpoint, tip1, tip2;
	vec3_t	start;
	float	dist;

	VectorAdd(e->brushes.onext->mins, e->brushes.onext->maxs, start);
	VectorScale(start, 0.5, start);
	dist = (b->maxs[0] - start[0]) * 2.5;

	FacingVectors (e, forward, right, up);
	VectorMA (start, dist, forward, endpoint);

	dist = (b->maxs[0] - start[0]) * 0.5;
	VectorMA (endpoint, -dist, forward, tip1);
	VectorMA (tip1, -dist, up, tip1);
	VectorMA (tip1, 2*dist, up, tip2);

	qglColor4f (1, 1, 1, 1);
	qglLineWidth (4);
	qglBegin (GL_LINES);
	qglVertex3fv (start);
	qglVertex3fv (endpoint);
	qglVertex3fv (endpoint);
	qglVertex3fv (tip1);
	qglVertex3fv (endpoint);
	qglVertex3fv (tip2);
	qglEnd ();
	qglLineWidth (1);
}

void DrawLight(brush_t *b)
{
	vec3_t vTriColor;
	bool bTriPaint = false;

  vTriColor[0] = vTriColor[2] = 1.0;
  vTriColor[1]  = 1.0;
  bTriPaint = true;
  CString strColor = ValueForKey(b->owner, "_color");
  if (strColor.GetLength() > 0)
  {
    float fR, fG, fB;
	  int n = sscanf(strColor,"%f %f %f", &fR, &fG, &fB);
    if (n == 3)
    {
      vTriColor[0] = fR;
      vTriColor[1] = fG;
      vTriColor[2] = fB;
    }
  }
  qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]);

  vec3_t vCorners[4];
  float fMid = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2;

  vCorners[0][0] = b->mins[0];
  vCorners[0][1] = b->mins[1];
  vCorners[0][2] = fMid;

  vCorners[1][0] = b->mins[0];
  vCorners[1][1] = b->maxs[1];
  vCorners[1][2] = fMid;

  vCorners[2][0] = b->maxs[0];
  vCorners[2][1] = b->maxs[1];
  vCorners[2][2] = fMid;

  vCorners[3][0] = b->maxs[0];
  vCorners[3][1] = b->mins[1];
  vCorners[3][2] = fMid;

  vec3_t vTop, vBottom;

  vTop[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) / 2);
  vTop[1] = b->mins[1] + ((b->maxs[1] - b->mins[1]) / 2);
  vTop[2] = b->maxs[2];

  VectorCopy(vTop, vBottom);
  vBottom[2] = b->mins[2];

  vec3_t vSave;
  VectorCopy(vTriColor, vSave);

  qglBegin(GL_TRIANGLE_FAN);
  qglVertex3fv(vTop);
  for (int i = 0; i <= 3; i++)
  {
    vTriColor[0] *= 0.95;
    vTriColor[1] *= 0.95;
    vTriColor[2] *= 0.95;
    qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]);
    qglVertex3fv(vCorners[i]);
  }
  qglVertex3fv(vCorners[0]);
  qglEnd();
  
  VectorCopy(vSave, vTriColor);
  vTriColor[0] *= 0.95;
  vTriColor[1] *= 0.95;
  vTriColor[2] *= 0.95;

  qglBegin(GL_TRIANGLE_FAN);
  qglVertex3fv(vBottom);
  qglVertex3fv(vCorners[0]);
  for (i = 3; i >= 0; i--)
  {
    vTriColor[0] *= 0.95;
    vTriColor[1] *= 0.95;
    vTriColor[2] *= 0.95;
    qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]);
    qglVertex3fv(vCorners[i]);
  }
  qglEnd();
}

void Brush_Draw( brush_t *b )
{
	face_t			*face;
	int				i, order;
	qtexture_t		*prev = 0;
	winding_t *w;

	if ( b->owner && ( b->owner->eclass->nShowFlags & ECLASS_PLUGINENTITY ) )
	{
		b->owner->pPlugEnt->CamRender();
		return;
	}
	
	// (TTimo) NOTE: added by build 173, I check after pPlugEnt so it doesn't interfere ?
  if (b->hiddenBrush)
  {
    return;
  }

	if (b->patchBrush)
	{
		Patch_DrawCam(b->pPatch);
		return;
	}
	
	int nDrawMode = g_pParentWnd->GetCamera()->Camera().draw_mode;
	
	if (b->owner->eclass->fixedsize)
	{
		
		if (!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ANGLES) && (b->owner->eclass->nShowFlags & ECLASS_ANGLE))
		{
			Brush_DrawFacingAngle(b, b->owner);
		}
		
		if (g_PrefsDlg.m_bNewLightDraw && (b->owner->eclass->nShowFlags & ECLASS_LIGHT))
		{
			DrawLight(b);
			return;
		}
		if (nDrawMode == cd_texture || nDrawMode == cd_light)
			qglDisable (GL_TEXTURE_2D);
		
		// if we are wireframing models
		bool bp = (b->bModelFailed) ? false : PaintedModel(b, true);
		
		if (nDrawMode == cd_texture || nDrawMode == cd_light)
			qglEnable (GL_TEXTURE_2D);
		
		if (bp)
			return;
	}
	
//	qglDisable( GL_TEXTURE_2D );

	// guarantee the texture will be set first
	prev = NULL;
	for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)
	{
		w = face->face_winding;
		if (!w)
		{
			continue;		// freed face
		}
		
		if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK)
		{
			if (strstr(face->texdef.name, "caulk"))
				continue;
		}

		if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP)
    {
			if (strstr(face->texdef.name, "clip"))
				continue;
		}
		
		if (face->d_texture->name[0] == '(')
		{
			prev = NULL;
			qglDisable( GL_TEXTURE_2D );
		}
		else if ((nDrawMode == cd_texture || nDrawMode == cd_light) && face->d_texture != prev)
		{
			// set the texture for this face
			prev = face->d_texture;
			qglBindTexture( GL_TEXTURE_2D, face->d_texture->texture_number );
		}
		
    // NOTE: alpha values when setting colors are only relevant if blending was enabled before calling Brush_Draw
    if (b->patchBrush)
      //++timo FIXME: find a use case for this??
      qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], 0.13 );
    else
      qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], face->pShader->getTrans() );

		// draw the polygon
		
		if (g_PrefsDlg.m_bGLLighting)
			qglNormal3fv(face->plane.normal);
		
		qglBegin(GL_POLYGON);
		
		for (i=0 ; i<w->numpoints ; i++)
		{
			if (nDrawMode == cd_texture || nDrawMode == cd_light)
				qglTexCoord2fv( &w->points[i][3] );
			qglVertex3fv(w->points[i]);
		}
		qglEnd();
	}
	
	if (b->owner->eclass->fixedsize && (nDrawMode == cd_texture || nDrawMode == cd_light))
		qglEnable (GL_TEXTURE_2D);
	
	qglBindTexture( GL_TEXTURE_2D, 0 );
}



void Face_Draw( face_t *f )
{
	int i;

	if ( f->face_winding == 0 )
		return;
	qglBegin( GL_POLYGON );
	for ( i = 0 ; i < f->face_winding->numpoints; i++)
		qglVertex3fv( f->face_winding->points[i] );
	qglEnd();
}

void Brush_DrawXY(brush_t *b, int nViewType)
{
	face_t *face;
	int     order;
	winding_t *w;
	int        i;

  if (b->hiddenBrush)
  {
    return;
  }

  if (b->patchBrush)
  {
    //Patch_DrawXY(b->nPatchID);
    Patch_DrawXY(b->pPatch);
    if (!g_bPatchShowBounds)
      return;
  }
                     

  if (b->owner->eclass->fixedsize)
  {
    if (g_PrefsDlg.m_bNewLightDraw && (b->owner->eclass->nShowFlags & ECLASS_LIGHT))
    {
      vec3_t vCorners[4];
      float fMid = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2;

      vCorners[0][0] = b->mins[0];
      vCorners[0][1] = b->mins[1];
      vCorners[0][2] = fMid;

      vCorners[1][0] = b->mins[0];
      vCorners[1][1] = b->maxs[1];
      vCorners[1][2] = fMid;

      vCorners[2][0] = b->maxs[0];
      vCorners[2][1] = b->maxs[1];
      vCorners[2][2] = fMid;

      vCorners[3][0] = b->maxs[0];
      vCorners[3][1] = b->mins[1];
      vCorners[3][2] = fMid;

      vec3_t vTop, vBottom;

      vTop[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) / 2);
      vTop[1] = b->mins[1] + ((b->maxs[1] - b->mins[1]) / 2);
      vTop[2] = b->maxs[2];

      VectorCopy(vTop, vBottom);
      vBottom[2] = b->mins[2];
	    
      qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
      qglBegin(GL_TRIANGLE_FAN);
      qglVertex3fv(vTop);
      qglVertex3fv(vCorners[0]);
      qglVertex3fv(vCorners[1]);
      qglVertex3fv(vCorners[2]);
      qglVertex3fv(vCorners[3]);
      qglVertex3fv(vCorners[0]);
      qglEnd();
      qglBegin(GL_TRIANGLE_FAN);
      qglVertex3fv(vBottom);
      qglVertex3fv(vCorners[0]);
      qglVertex3fv(vCorners[3]);
      qglVertex3fv(vCorners[2]);
      qglVertex3fv(vCorners[1]);
      qglVertex3fv(vCorners[0]);
      qglEnd();
		  DrawBrushEntityName (b);
      return;
    }
    else if (b->owner->eclass->nShowFlags & ECLASS_MISCMODEL)
    {
      if (PaintedModel(b, false))
        return;
    }
  }

	for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)
	{
		// only draw polygons facing in a direction we care about
    if (nViewType == XY)
    {
		  if (face->plane.normal[2] <= 0)
			  continue;
    }
    else
    {
      if (nViewType == XZ)
      {
        if (face->plane.normal[1] <= 0)
          continue;
      }
      else 
      {
        if (face->plane.normal[0] <= 0)
          continue;
      }
    }

		w = face->face_winding;
		if (!w)
			continue;

		// draw the polygon
		qglBegin(GL_LINE_LOOP);
    for (i=0 ; i<w->numpoints ; i++)
		  qglVertex3fv(w->points[i]);
		qglEnd();
	}

	DrawBrushEntityName (b);

}

/*
============
Brush_Move
============
*/
void Brush_Move (brush_t *b, const vec3_t move, bool bSnap)
{
  int i;
  face_t *f;

  for (f=b->brush_faces ; f ; f=f->next)
  {
    vec3_t vTemp;
    VectorCopy(move, vTemp);

    if (g_PrefsDlg.m_bTextureLock)
      Face_MoveTexture(f, vTemp);
    
    for (i=0 ; i<3 ; i++)
      VectorAdd (f->planepts[i], move, f->planepts[i]);
  }
  Brush_Build( b, bSnap );


  if (b->patchBrush)
  {
    //Patch_Move(b->nPatchID, move);
    Patch_Move(b->pPatch, move);
  }


  // PGM - keep the origin vector up to date on fixed size entities.
  if(b->owner->eclass->fixedsize)
  {
    VectorAdd(b->owner->origin, move, b->owner->origin);
	  //VectorAdd(b->maxs, b->mins, b->owner->origin);
	  //VectorScale(b->owner->origin, 0.5, b->owner->origin);
  }
}



void Brush_Print(brush_t* b)
{
  int nFace = 0;
  for (face_t* f = b->brush_faces ; f ; f=f->next)
  {
    Sys_Printf("Face %i\n", nFace++);
    Sys_Printf("%f %f %f\n", f->planepts[0][0], f->planepts[0][1], f->planepts[0][2]);
    Sys_Printf("%f %f %f\n", f->planepts[1][0], f->planepts[1][1], f->planepts[1][2]);
    Sys_Printf("%f %f %f\n", f->planepts[2][0], f->planepts[2][1], f->planepts[2][2]);
  }
 }



/*
=============
Brush_MakeSided

Makes the current brushhave the given number of 2d sides and turns it into a cone
=============
*/
void Brush_MakeSidedCone(int sides)
{
	int		i;
	vec3_t	mins, maxs;
	brush_t	*b;
	texdef_t	*texdef;
	face_t	*f;
	vec3_t	mid;
	float	width;
	float	sv, cv;

	if (sides < 3)
	{
		Sys_Status ("Bad sides number", 0);
		return;
	}

	if (!QE_SingleBrush ())
	{
		Sys_Status ("Must have a single brush selected", 0 );
		return;
	}

	b = selected_brushes.next;
	VectorCopy (b->mins, mins);
	VectorCopy (b->maxs, maxs);
	texdef = &g_qeglobals.d_texturewin.texdef;

	Brush_Free (b);

	// find center of brush
	width = 8;
	for (i=0 ; i<2 ; i++)
	{
		mid[i] = (maxs[i] + mins[i])*0.5;
		if (maxs[i] - mins[i] > width)
			width = maxs[i] - mins[i];
	}
	width /= 2;

	b = Brush_Alloc();

	// create bottom face
	f = Face_Alloc();
	f->texdef = *texdef;
	f->next = b->brush_faces;
	b->brush_faces = f;

	f->planepts[0][0] = mins[0];f->planepts[0][1] = mins[1];f->planepts[0][2] = mins[2];
	f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = mins[2];
	f->planepts[2][0] = maxs[0];f->planepts[2][1] = maxs[1];f->planepts[2][2] = mins[2];

	for (i=0 ; i<sides ; i++)
	{
		f = Face_Alloc();
		f->texdef = *texdef;
		f->next = b->brush_faces;
		b->brush_faces = f;

		sv = sin (i*3.14159265*2/sides);
		cv = cos (i*3.14159265*2/sides);


		f->planepts[0][0] = floor(mid[0]+width*cv+0.5);
		f->planepts[0][1] = floor(mid[1]+width*sv+0.5);
		f->planepts[0][2] = mins[2];

		f->planepts[1][0] = mid[0];
		f->planepts[1][1] = mid[1];
		f->planepts[1][2] = maxs[2];

		f->planepts[2][0] = floor(f->planepts[0][0] - width * sv + 0.5);
		f->planepts[2][1] = floor(f->planepts[0][1] + width * cv + 0.5);
		f->planepts[2][2] = maxs[2];

	}

	Brush_AddToList (b, &selected_brushes);

	Entity_LinkBrush (world_entity, b);

	Brush_Build( b );

	Sys_UpdateWindows (W_ALL);
}

/*
=============
Brush_MakeSided

Makes the current brushhave the given number of 2d sides and turns it into a sphere
=============

*/
void Brush_MakeSidedSphere(int sides)
{
	int		i,j;
	vec3_t	mins, maxs;
	brush_t	*b;
	texdef_t	*texdef;
	face_t	*f;
	vec3_t	mid;

	if (sides < 4)
	{
		Sys_Status ("Bad sides number", 0);
		return;
	}

	if (!QE_SingleBrush ())
	{
		Sys_Status ("Must have a single brush selected", 0 );
		return;
	}

	b = selected_brushes.next;
	VectorCopy (b->mins, mins);
	VectorCopy (b->maxs, maxs);
	texdef = &g_qeglobals.d_texturewin.texdef;

	Brush_Free (b);

	// find center of brush
	float radius = 8;
	for (i=0 ; i<2 ; i++)
	{
		mid[i] = (maxs[i] + mins[i])*0.5;
		if (maxs[i] - mins[i] > radius)
			radius = maxs[i] - mins[i];
	}
	radius /= 2;

	b = Brush_Alloc();

	float dt = float(2 * Q_PI / sides);
	float dp = float(Q_PI / sides);
  float t,p;
	for(i=0; i <= sides-1; i++)
  {
		for(j=0;j <= sides-2; j++)
		{
			t = i * dt;
			p = float(j * dp - Q_PI / 2);

      f = Face_Alloc();
	    f->texdef = *texdef;
	    f->next = b->brush_faces;
	    b->brush_faces = f;

      VectorPolar(f->planepts[0], radius, t, p);
      VectorPolar(f->planepts[1], radius, t, p + dp);
      VectorPolar(f->planepts[2], radius, t + dt, p + dp);

      for (int k = 0; k < 3; k++)
        VectorAdd(f->planepts[k], mid, f->planepts[k]);
		}
  }

  p = float((sides - 1) * dp - Q_PI / 2);
	for(i = 0; i <= sides-1; i++)
	{
		t = i * dt;

    f = Face_Alloc();
	  f->texdef = *texdef;
	  f->next = b->brush_faces;
	  b->brush_faces = f;

    VectorPolar(f->planepts[0], radius, t, p);
    VectorPolar(f->planepts[1], radius, t + dt, p + dp);
    VectorPolar(f->planepts[2], radius, t + dt, p);

    for (int k = 0; k < 3; k++)
      VectorAdd(f->planepts[k], mid, f->planepts[k]);
	}

	Brush_AddToList (b, &selected_brushes);

	Entity_LinkBrush (world_entity, b);

	Brush_Build( b );

	Sys_UpdateWindows (W_ALL);
}

void Face_FitTexture( face_t * face, int nHeight, int nWidth )
{
  winding_t *w;
  vec3_t   mins,maxs;
  int i;
  float width, height, temp;
  float rot_width, rot_height;
  float cosv,sinv,ang;
  float min_t, min_s, max_t, max_s;
  float s,t;
	vec3_t	vecs[2];
  vec3_t   coords[4];
	texdef_t	*td;

  if (nHeight < 1)
    nHeight = 1;
  if (nWidth < 1)
    nWidth = 1;

  ClearBounds (mins, maxs);

	w = face->face_winding;
	if (!w)
	{
    return;
	}
  for (i=0 ; i<w->numpoints ; i++)
  {
    AddPointToBounds( w->points[i], mins, maxs );
  }

	if (g_qeglobals.m_bBrushPrimitMode)
		Face_FitTexture_BrushPrimit( face, mins, maxs, nHeight, nWidth );
	else
	{
		
		td = &face->texdef;
		// 
		// get the current angle
		//
		ang = td->rotate / 180 * Q_PI;
		sinv = sin(ang);
		cosv = cos(ang);
		
		// get natural texture axis
		TextureAxisFromPlane(&face->plane, vecs[0], vecs[1]);
		
		min_s = DotProduct( mins, vecs[0] );
		min_t = DotProduct( mins, vecs[1] );
		max_s = DotProduct( maxs, vecs[0] );
		max_t = DotProduct( maxs, vecs[1] );
		width = max_s - min_s;
		height = max_t - min_t;
		coords[0][0] = min_s;
		coords[0][1] = min_t;
		coords[1][0] = max_s;
		coords[1][1] = min_t;
		coords[2][0] = min_s;
		coords[2][1] = max_t;
		coords[3][0] = max_s;
		coords[3][1] = max_t;
		min_s = min_t = 99999;
		max_s = max_t = -99999;
		for (i=0; i<4; i++)
		{
			s = cosv * coords[i][0] - sinv * coords[i][1];
			t = sinv * coords[i][0] + cosv * coords[i][1];
			if (i&1)
			{
				if (s > max_s) 
				{
					max_s = s;
				}
			}
			else
			{
				if (s < min_s) 
				{
					min_s = s;
				}
				if (i<2)
				{
					if (t < min_t) 
					{
						min_t = t;
					}
				}
				else
				{
					if (t > max_t) 
					{
						max_t = t;
					}
				}
			}
		}
		rot_width =  (max_s - min_s);
		rot_height = (max_t - min_t);
		td->scale[0] = -(rot_width/((float)(face->d_texture->width*nWidth)));
		td->scale[1] = -(rot_height/((float)(face->d_texture->height*nHeight)));
		
		td->shift[0] = min_s/td->scale[0];
		temp = (int)(td->shift[0] / (face->d_texture->width*nWidth));
		temp = (temp+1)*face->d_texture->width*nWidth;
		td->shift[0] = (int)(temp - td->shift[0])%(face->d_texture->width*nWidth);
		
		td->shift[1] = min_t/td->scale[1];
		temp = (int)(td->shift[1] / (face->d_texture->height*nHeight));
		temp = (temp+1)*(face->d_texture->height*nHeight);
		td->shift[1] = (int)(temp - td->shift[1])%(face->d_texture->height*nHeight);
		
	}
}

void Brush_FitTexture( brush_t *b, int nHeight, int nWidth )
{
	face_t *face;

	for (face = b->brush_faces ; face ; face=face->next)
  {
    Face_FitTexture( face, nHeight, nWidth );
  }
}


