/*
===========================================================================
Copyright (C) 2008 Daniel Örstadius
Copyright (C) 2009 Jared Prince
This file is part of bsp-renderer source code.
bsp-renderer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
bsp-renderer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with bsp-renderer.  If not, see .
*/
// Q3Map.cpp -- handles the map data
#include 
#include 
#include "Q3Map.h"
#include "Q3Map_misc.h"
//temp
//#include 
//#include       /* Needed only for _O_RDWR definition */
//#include 
Q3Map::Q3Map()
{
	m_nDebugA=0 ;
	m_nNewCount=0 ;
	
  m_pTexturesOrig=NULL ;
  m_pFaces=NULL ;
  m_pVertices=NULL ;
  m_pMeshVerts=NULL ;
  m_pLeafs=NULL ;
  m_pLeafFaces=NULL ;
  m_pPlanes=NULL ;
  m_pNodes=NULL ;
	//m_VisData->vecs=NULL ; // can't NULL this because m_VisData doesn't exist yet!
	m_VisData=NULL ;
   
  m_pBrushes=NULL ;
  m_pLeafBrushes=NULL ;
  m_pBrushSides=NULL ;
  m_pLightMaps=NULL ;
  m_pEntities=NULL ;
	m_pSubZones=NULL ;
	m_pPortals=NULL ;
	//////////////////////////////////////////////////////
	// initiallize our triangle memory management
	m_nTriangleSize=0 ;
	m_pTriangleMem=NULL ;
	m_pTriangle=NULL ;
	m_nTriangleMax=0 ;
	m_nTriangleLimit=0 ;
	m_nVertexSize=0 ;
	m_pVertexMem=NULL ;
	m_pVertex=NULL ;
	m_nVertexMax=0 ;
	m_nVertexLimit=0 ;
	m_nLightSize=0 ;
	m_pLightMem=NULL ;
	m_pLight=NULL ;
	m_nLightMax=0 ;
	m_nLightLimit=0 ;
	m_nMaxMultiZoneLight=0 ;
	m_nLampSize=0 ;
	m_pLampMem=NULL ;
	m_pLamp=NULL ;
	m_nLampMax=0 ;
	m_nLampLimit=0 ;
	m_nTextureSize=0 ;
	m_pTextureMem=NULL ;
	m_pTexture=NULL ;
	m_nTextureMax=0 ;
	m_nTextureLimit=0 ;
	m_nTexLampSize=0 ;
	m_pTexLampMem=NULL ;
	m_pTexLamp=NULL ;
	m_nTexLampMax=0 ;
	m_nTexLampLimit=0 ;
	m_BspFaces=NULL;
	m_pTransTexture=NULL ;
//	Q3Bug.LogInit() ;
}
Q3Map::~Q3Map()
{
//	Q3Bug.LogSave("Q3Bug.log") ;
	DELETE_ARRAY(m_pTransTexture) ;
	//DELETE_ARRAY(m_pSubZones) ;
	DELETE_ARRAY(mVisibleFaces) ;
	FreeLightMemory() ;
	FreeLampMemory() ;
	FreeVertexMemory() ;
	FreeTriangleMemory() ;
	FreeTextureMemory() ;
	FreeTexLampMemory() ;
	DestroyBspFacesMemory() ;	// free up face and patch data.  Normally already done if we're just loading a new level or quitting, but we might also be here due to a loading fail or something.
	// no need to delete any of the pointers to the lumps since their memory belongs to m_pRawBspFile over in OgreFramework.
	
}
void Q3Map::DestroyBspFacesMemory(void)
{
	if(m_BspFaces) 
	{
		// clean up any memory declared for patches
		for (int i=0; i < m_NumBspFaces; i++)
      if ((m_BspFaces[i].type == PATCH) && (m_BspFaces[i].patch != NULL))
			{
				DELETE_ARRAY( m_BspFaces[i].patch->bezier ) ;
				DELETE_POINTER( m_BspFaces[i].patch ) ;
			}
		// delete the faces memory
		DELETE_ARRAY( m_BspFaces ) ;
	}
}
int Q3Map::parseMap(const char* pMem, size_t Size)
{	
	// we check all lump info to make sure it isn't trying to go out of bounds,
	// in case some mangled bsp is trying to do something devious or is just corrupted
	
	
	
	m_BspHeader=*((Q3BspHeader_t*)pMem) ; // pointer to the header
	// run a check that the total size of all the iLengths plus the header isn't too large
	size_t TotalLength=sizeof(Q3BspHeader_t) ; // initialize to the size of the header
	int nLump=0 ;
	int nOther=0 ;
	int nLumpMin=0 ;
	int nLumpMax=0 ;
	int nOtherMin=0 ;
	int nOtherMax=0 ;
	for(nLump=0 ; nLumpMAX_LUMP_SIZE) return -2 ; // no lump has a right to be this big... FAIL!
		
		
		if( (m_BspHeader.Lumps[nLump].iLength>0) && (m_BspHeader.Lumps[nLump].iOffsetSize) return -5 ;// this file is messed up, the lumps add up to more than the file size.  FAIL!
		// make sure this lump doesn't overlap any other lumps
		nLumpMin=m_BspHeader.Lumps[nLump].iOffset ;
		nLumpMax=nLumpMin+m_BspHeader.Lumps[nLump].iLength-1 ;
		for(nOther=nLump+1 ; nOther0) && (m_BspHeader.Lumps[nOther].iLength>0)) // don't check zero sized lumps
			{
				nOtherMin=m_BspHeader.Lumps[nOther].iOffset ;
				nOtherMax=nOtherMin+m_BspHeader.Lumps[nOther].iLength-1 ;
				if((nLumpMax>=nOtherMin) && (nLumpMin<=nOtherMax)) 
				return -6 ; // lump overlaps another lump, FAIL!
			}
	}
	
	// setup pointers to the lumps
	if((m_BspHeader.Lumps[0].iOffset<0) || (m_BspHeader.Lumps[0].iOffset>=Size)) return -7 ; // fail if out of memory bounds
	m_pEntities=(char*)(pMem+m_BspHeader.Lumps[0].iOffset) ;
	if((m_BspHeader.Lumps[Faces].iOffset<0) || (m_BspHeader.Lumps[Faces].iOffset>=Size)) return -8 ; // out of bounds
	if(m_BspHeader.Lumps[Faces].iOffset+m_BspHeader.Lumps[Faces].iLength>Size) return -9 ; // out of bounds
  m_iNumFaces = m_BspHeader.Lumps[Faces].iLength / sizeof(Q3BspFace_t);
	m_pFaces=(Q3BspFace_t*)(pMem+m_BspHeader.Lumps[Faces].iOffset) ;
	if((m_BspHeader.Lumps[Vertices].iOffset<0) || (m_BspHeader.Lumps[Vertices].iOffset>=Size)) return -10 ; // out of bounds
	if(m_BspHeader.Lumps[Vertices].iOffset+m_BspHeader.Lumps[Vertices].iLength>Size) return -11 ; // out of bounds
  m_iNumVertices = m_BspHeader.Lumps[Vertices].iLength / sizeof(Q3BspVertex);
	m_pVertices=(Q3BspVertex*)(pMem+m_BspHeader.Lumps[Vertices].iOffset) ;
	if((m_BspHeader.Lumps[MeshVerts].iOffset<0) || (m_BspHeader.Lumps[MeshVerts].iOffset>=Size)) return -12 ; // out of bounds
	if(m_BspHeader.Lumps[MeshVerts].iOffset+m_BspHeader.Lumps[MeshVerts].iLength>Size) return -13 ; // out of bounds
  m_iNumMeshVerts = m_BspHeader.Lumps[MeshVerts].iLength / sizeof(int);
	m_pMeshVerts=(int*)(pMem+m_BspHeader.Lumps[MeshVerts].iOffset) ;
	if((m_BspHeader.Lumps[Leafs].iOffset<0) || (m_BspHeader.Lumps[Leafs].iOffset>=Size)) return -14 ; // out of bounds
	if(m_BspHeader.Lumps[Leafs].iOffset+m_BspHeader.Lumps[Leafs].iLength>Size) return -15 ; // out of bounds
  m_iNumLeafs = m_BspHeader.Lumps[Leafs].iLength / sizeof(Q3BspLeaf);
	m_pLeafs=(Q3BspLeaf*)(pMem+m_BspHeader.Lumps[Leafs].iOffset) ;
	if((m_BspHeader.Lumps[LeafFaces].iOffset<0) || (m_BspHeader.Lumps[LeafFaces].iOffset>=Size)) return -16 ; // out of bounds
	if(m_BspHeader.Lumps[LeafFaces].iOffset+m_BspHeader.Lumps[LeafFaces].iLength>Size) return -17 ; // out of bounds
  m_iNumLeafFaces = m_BspHeader.Lumps[LeafFaces].iLength / sizeof(int);
	m_pLeafFaces=(int*)(pMem+m_BspHeader.Lumps[LeafFaces].iOffset) ;
	if((m_BspHeader.Lumps[LeafBrushes].iOffset<0) || (m_BspHeader.Lumps[LeafBrushes].iOffset>=Size)) return -18 ; // out of bounds
	if(m_BspHeader.Lumps[LeafBrushes].iOffset+m_BspHeader.Lumps[LeafBrushes].iLength>Size) return -19 ; // out of bounds
  m_iNumLeafBrushes = m_BspHeader.Lumps[LeafBrushes].iLength / sizeof(int);
	m_pLeafBrushes=(int*)(pMem+m_BspHeader.Lumps[LeafBrushes].iOffset) ;
	if((m_BspHeader.Lumps[Textures].iOffset<0) || (m_BspHeader.Lumps[Textures].iOffset>=Size)) return -20 ; // out of bounds
	if(m_BspHeader.Lumps[Textures].iOffset+m_BspHeader.Lumps[Textures].iLength>Size) return -21 ; // out of bounds
  m_iNumTexs = m_BspHeader.Lumps[Textures].iLength / sizeof(Q3BspTexture);
	m_pTexturesOrig=(Q3BspTexture*)(pMem+m_BspHeader.Lumps[Textures].iOffset) ;
	if((m_BspHeader.Lumps[Planes].iOffset<0) || (m_BspHeader.Lumps[Planes].iOffset>=Size)) return -22 ; // out of bounds
	if(m_BspHeader.Lumps[Planes].iOffset+m_BspHeader.Lumps[Planes].iLength>Size) return -23 ; // out of bounds
  m_iNumPlanes = m_BspHeader.Lumps[Planes].iLength / sizeof(Q3BspPlane);
	m_pPlanes=(Q3BspPlane*)(pMem+m_BspHeader.Lumps[Planes].iOffset) ;
	if((m_BspHeader.Lumps[Nodes].iOffset<0) || (m_BspHeader.Lumps[Nodes].iOffset>=Size)) return -24 ; // out of bounds
	if(m_BspHeader.Lumps[Nodes].iOffset+m_BspHeader.Lumps[Nodes].iLength>Size) return -25 ; // out of bounds
  m_iNumNodes = m_BspHeader.Lumps[Nodes].iLength / sizeof(Q3BspNode);
	m_pNodes=(Q3BspNode*)(pMem+m_BspHeader.Lumps[Nodes].iOffset) ;
  //m_iNumModels = m_BspHeader.Lumps[Models].iLength / sizeof(Q3BspModel);
  //m_pModels = new Q3BspModel[m_iNumModels];
	// bzn doesn't use lightmaps
  //m_iNumLightMaps = m_BspHeader.Lumps[LightMaps].iLength / sizeof(Q3BspLightMap);
	//m_pLightMaps=(Q3BspLightMap*)(pMem+m_BspHeader.Lumps[LightMaps].iOffset) ;
	if((m_BspHeader.Lumps[Brushes].iOffset<0) || (m_BspHeader.Lumps[Brushes].iOffset>=Size)) return -26 ; // out of bounds
	if(m_BspHeader.Lumps[Brushes].iOffset+m_BspHeader.Lumps[Brushes].iLength>Size) return -27 ; // out of bounds
  m_iNumBrushes = m_BspHeader.Lumps[Brushes].iLength / sizeof(Q3BspBrush);
	m_pBrushes=(Q3BspBrush*)(pMem+m_BspHeader.Lumps[Brushes].iOffset) ;
	if((m_BspHeader.Lumps[BrushSides].iOffset<0) || (m_BspHeader.Lumps[BrushSides].iOffset>=Size)) return -28 ; // out of bounds
	if(m_BspHeader.Lumps[BrushSides].iOffset+m_BspHeader.Lumps[BrushSides].iLength>Size) return -29 ; // out of bounds
  m_iNumBrushSides = m_BspHeader.Lumps[BrushSides].iLength / sizeof(Q3BspBrushSide);
	m_pBrushSides=(Q3BspBrushSide*)(pMem+m_BspHeader.Lumps[BrushSides].iOffset) ;
  //m_iNumEffects = m_BspHeader.Lumps[Effects].iLength / sizeof(Q3BspEffect);
  //m_pEffects = new Q3BspEffect[m_iNumEffects];
  //
  //m_pImages = new BDTexture[m_iNumTexs];
	// bzn doesn't use visdata
	//m_VisData=(Q3BspVisData*)(pMem+m_BspHeader.Lumps[VisData].iOffset) ;
	//m_VisData->vecs=(unsigned char*)(pMem+m_BspHeader.Lumps[VisData].iOffset + 2*sizeof(int)) ;
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// bzn specific data
	if((m_BspHeader.Lumps[SubZoneData].iOffset<0) || (m_BspHeader.Lumps[SubZoneData].iOffset>=Size)) return -30 ; // out of bounds
	if(m_BspHeader.Lumps[SubZoneData].iOffset+m_BspHeader.Lumps[SubZoneData].iLength>Size) return -31 ; // out of bounds
	m_iNumSubZones = m_BspHeader.Lumps[SubZoneData].iLength / sizeof(BZN_SubZone_t);
  m_pSubZones=(BZN_SubZone_t*)(pMem+m_BspHeader.Lumps[SubZoneData].iOffset) ;
	if((m_BspHeader.Lumps[PortalData].iOffset<0) || (m_BspHeader.Lumps[PortalData].iOffset>=Size)) return -32 ; // out of bounds
	if(m_BspHeader.Lumps[PortalData].iOffset+m_BspHeader.Lumps[PortalData].iLength>Size) return -33 ; // out of bounds
	m_iNumPortals = m_BspHeader.Lumps[PortalData].iLength / sizeof(BZN_Portal_t);
	m_pPortals=(BZN_Portal_t*)(pMem+m_BspHeader.Lumps[PortalData].iOffset) ;
	
	// fix coords and setup face memory
	swizzleCoords();
  mVisibleFaces = new int[m_iNumFaces];
	// we need a new version of the textures, because when we parse the lights they will have textures to add to it,
	// and we can't expand the texture lump because it's in the middle of a block of memory containing all the lumps.
	// copy the texture lump
	int nTexture=0 ;
	for(nTexture=0 ; nTexturesize = num_bezier_patches;
  q3patch->bezier = new Bezier[q3patch->size];
  int patchIndex =  0;
  int ii, n, j, nn;
  for (ii = 0, n = 0; n < patch_size_x; n++, ii = 2*n)
  {				
    for (j=0, nn=0; nn < patch_size_y; nn++, j = 2*nn)
    {
      int index = 0;
      for (int ctr = 0; ctr < 3; ctr++)
      { 
        int pos = ctr * m_pFaces[faceIndex].size[0];
        q3patch->bezier[patchIndex].mControls[index++] = 
          BspVertex(
          // position
          m_pVertices[m_pFaces[faceIndex].vertex + 
          ii + 
          m_pFaces[faceIndex].size[0] * j + 
          pos].position,
          // texture coordinates
          m_pVertices[m_pFaces[faceIndex].vertex + 
          ii + 
          m_pFaces[faceIndex].size[0] * j +
          pos].texcoord,
          // normal
          m_pVertices[m_pFaces[faceIndex].vertex +
          ii +
          m_pFaces[faceIndex].size[0] * j +
          pos].normal);
        q3patch->bezier[patchIndex].mControls[index++] = 
                BspVertex(
                  m_pVertices[m_pFaces[faceIndex].vertex + ii + m_pFaces[faceIndex].size[0] * j + pos + 1].position,
                  m_pVertices[m_pFaces[faceIndex].vertex + ii + m_pFaces[faceIndex].size[0] * j + pos + 1].texcoord,
                  m_pVertices[m_pFaces[faceIndex].vertex + ii + m_pFaces[faceIndex].size[0] * j + pos + 1].normal);
        q3patch->bezier[patchIndex].mControls[index++] = 
                BspVertex(
                  m_pVertices[m_pFaces[faceIndex].vertex + ii + m_pFaces[faceIndex].size[0] * j + pos + 2].position,
                  m_pVertices[m_pFaces[faceIndex].vertex + ii + m_pFaces[faceIndex].size[0] * j + pos + 2].texcoord,
                  m_pVertices[m_pFaces[faceIndex].vertex + ii + m_pFaces[faceIndex].size[0] * j + pos + 2].normal);						
      }      
      q3patch->bezier[patchIndex].tessellate(5);
      patchIndex++;
    }
  }
  return q3patch;
}
int Q3Map::findVisibleFaces(const QVECTOR *camPos, int *facesToRender)
{
  int leaf;
  int visCluster;	
  leaf = findLeaf(camPos);	
  visCluster = m_pLeafs[leaf].cluster;
  memset(mVisibleFaces, 0, sizeof(int) * m_iNumFaces);	
  int faceindex;
  int renderindex=0;
  m_ClusterCount=0 ;
  
  for (int i=0; i < m_iNumLeafs; i++)
  {
    if (isClusterVisible(visCluster, m_pLeafs[i].cluster))
    {				
			m_ClusterCount++ ;
      bool vis=true ;//bool vis = mViewFrustum->checkIfBoxInside(m_pLeafs[i].mins, m_pLeafs[i].maxs);						
      
	  if (vis)
      {
        for (int k=0; k < m_pLeafs[i].n_leaffaces; k++)
        {					
          faceindex =	m_pLeafFaces[m_pLeafs[i].leafface + k];				
          if (mVisibleFaces[faceindex] == 0)
          {
            mVisibleFaces[faceindex] = 1;						
            facesToRender[renderindex++] = faceindex;
          }
        }
      }			
    }
  }
  
  facesToRender[renderindex] = -1;	
  return renderindex;
}
int Q3Map::findLeaf(const QVECTOR *camPos) const
{
  int index = 0;
  while (index >= 0)
  {
    const Q3BspNode *node = &m_pNodes[index];
    const Q3BspPlane *plane = &m_pPlanes[node->plane];
    // distance from point to plane
    //QVECTOR normal = QVECTOR(plane->normal);		 
    QVECTOR normal ;
	normal[0]=plane->normal[0] ;
	normal[1]=plane->normal[1] ;
	normal[2]=plane->normal[2] ;
	
	
	//const float distance = D3DXVec3Dot(&normal,camPos) - plane->dist;
	const float distance=(normal[0]* *camPos[0] + normal[1]* *camPos[1] + normal[2]* *camPos[2]) - plane->dist ;
    if(distance >= 0)
      index = node->children[0];
    else
      index = node->children[1];
  }
  return -index - 1;
}
bool Q3Map::isClusterVisible(int visCluster, int testCluster) const
{
  if (m_VisData == NULL)
    return true;
  if ((m_VisData->vecs == NULL) || (visCluster < 0)) 	
    return true;    
  int i = (visCluster * m_VisData->sz_vecs) + (testCluster >> 3);
  unsigned char visSet = m_VisData->vecs[i];
  return (visSet & (1 << (testCluster & 7))) != 0;
}
Q3BspFace_t *Q3Map::getFaces(void)
{
  return m_pFaces;
}
 /***********************************************************************************************************\
                                               
                                   New Parsing and Triangulation Functions 
                                                
 \***********************************************************************************************************/
// This routine is basically an overview of the entire process that converts the BSP
// into something our Ogre code can use to construct the map's mesh and level data.
// In essence, it converts the map geometry into a list of triangles sorted by zone and material,
// as well as extracting other map info like zone and portal bounding boxes, lights, entities etc.
int Q3Map::ParseAndTriangulateMap(const char* pData, size_t Size)
{
	
//	char chMessage[1024] ;
	int nError=0 ;
		// setup pointers to the various lumps and get their quantities
		nError=parseMap( pData, Size ) ;
		if(nError<0)
		{
			//sprintf(chMessage, "Parse Map Error: %i", nError) ; 
			//Q3Bug.LogAddCR(chMessage) ;
			return ERROR_ParseMap ;
		}
		// extract entities such as lights, monsters, etc
		if(!ParseEntities())										return ERROR_ParseEntities ;
		// initial memory allocation for triangles
		m_nVertexMax=0 ;
		if(!AllocateVertexMemory(m_iNumVertices))	return ERROR_AllocateVertex ;
		if(!AllocateTriangleMemory())						return ERROR_AllocateTriangle ;
		if(!initFaces())												return ERROR_InitializeFaces ;
		// no new map textures should be added after here, or else SetupTransTextures won't work
		if(!SetupTransTextures())								return ERROR_SetupTransTextures ;
		// work out the zones
		SetupZones() ;
		// convert faces to triangles
		if(!ConvertFacesToTriangles())					return ERROR_ConvertFaces ;
		if(!ConvertPatchesToTriangles())				return ERROR_ConvertPatches ;
		if(!ConvertLampsToTriangles())					return ERROR_ConvertLamps ;
		if(!ConvertLampsToGlowTriangles())			return ERROR_ConvertLampGlow ;
		if(!ConvertLightsToGlowTriangles())			return ERROR_ConvertLightGlow ;
		GetTexLampTextureNumbers() ; // find out which textures, if any, are textures/common/bzn_lightnode0 to textures/common/bzn_lightnode3
		// assign triangles to zones, splitting them where necessary
		if(!AssignTrianglesToZones())						return ERROR_AssignTriangles ;
		if(!ConvertTexLampsToLampTriangles())		return ERROR_ConvertTexLamp ;
		// sort by group and re-range the group numbers
		if(!SortTrianglesIntoGroups())					return ERROR_SortGroups ;
		// sort the triangles in order of zone and texture.  This will also get rid of any unsubzoned triangles.
		if(!SortTrianglesIntoBatches())					return ERROR_SortTriangles ;
		
		// Setup the portals, lights and various bits of map connectivity
		AssignPortalsToZones() ;		// what portals each zone touches
		AssignLightsToZones() ;			// what lights each zone touches
		AssignLightsToPortals() ;		// what lights each portal touches
		AssignZonesToZones() ;			// what zones each zone touches
		return NOERROR ;
}
void Q3Map::FreeParseMem(void)
{
	FreeVertexMemory() ;
	FreeTriangleMemory() ;
	DestroyBspFacesMemory() ;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// memory management
int Q3Map::AllocateTriangleMemory(void)
{
	// memory for the Triangle
	m_nTriangleSize=MEMADD ; // starting memory size
	m_pTriangleMem=malloc(m_nTriangleSize) ; // allocate starting memory space
	m_pTriangle=(triangle_t*)m_pTriangleMem ; // a pointer to the memory cast as a triangle_t
	m_nTriangleMax=0 ;
	m_nTriangleLimit=m_nTriangleSize/sizeof(triangle_t) ; // if pos reaches this memory must expand
	if(m_pTriangleMem==NULL)
		return 0 ;
	return 1 ;
}
void Q3Map::FreeTriangleMemory(void)
{
	if(m_pTriangleMem) free(m_pTriangleMem) ;
	m_pTriangleMem=NULL ;
	m_pTriangle=NULL ;
	m_nTriangleMax=0 ;
	m_nTriangleLimit=0 ;
}
// increase size of Triangle memory, return 0 if failed
int Q3Map::ExpandTriangleMemory(void)
{
	m_nTriangleSize+=MEMADD ; // increase size
	m_pTriangleMem=realloc(m_pTriangleMem, m_nTriangleSize) ; // reallocate the memory
	if(m_pTriangleMem==NULL) return 0 ; // failed to allocate memory, get out and return false
	// if here then memory allocation succeeded
	m_pTriangle=(triangle_t*)m_pTriangleMem ; // pointer to the memory cast as a triangle_t
	m_nTriangleLimit=m_nTriangleSize/sizeof(triangle_t) ; // if pos reaches this memory must expand
	return 1 ; // ok
}
int Q3Map::AddTriangle(triangle_t Triangle)
{
	if(m_nTriangleMax>=m_nTriangleLimit)
		if( !ExpandTriangleMemory() )
			return 0 ;
	m_pTriangle[m_nTriangleMax++]=Triangle ;
	return 1 ;
}
int Q3Map::AllocateVertexMemory(int nVertNum)
{
	// memory for the Vertex
	m_nVertexSize=nVertNum*sizeof(Q3BspVertex)+MEMADD ; // starting memory size
	m_pVertexMem=malloc(m_nVertexSize) ; // allocate starting memory space
	m_pVertex=(Q3BspVertex*)m_pVertexMem ; // a pointer to the memory cast as a triangle_t
	m_nVertexLimit=m_nVertexSize/sizeof(Q3BspVertex) ; // if pos reaches this memory must expand
	if(m_pVertexMem==NULL)
		return 0 ;
	return 1 ;
}
void Q3Map::FreeVertexMemory(void)
{
	if(m_pVertexMem) free(m_pVertexMem) ;
	m_pVertexMem=NULL ;
	m_pVertex=NULL ;
	m_nVertexMax=0 ;
	m_nVertexLimit=0 ;
}
// increase size of Vertex memory, return 0 if failed
int Q3Map::ExpandVertexMemory(void)
{
	m_nVertexSize+=MEMADD ; // increase size
	m_pVertexMem=realloc(m_pVertexMem, m_nVertexSize) ; // reallocate the memory
	if(m_pVertexMem==NULL) return 0 ; // failed to allocate memory, get out and return false
	// if here then memory allocation succeeded
	m_pVertex=(Q3BspVertex*)m_pVertexMem ; // pointer to the memory cast as a triangle_t
	m_nVertexLimit=m_nVertexSize/sizeof(Q3BspVertex) ; // if pos reaches this memory must expand
	return 1 ; // ok
}
int Q3Map::AddVertex(Q3BspVertex Vertex)
{
	if(m_nVertexMax>=m_nVertexLimit)
		if( !ExpandVertexMemory() )
			return 0 ;
	m_pVertex[m_nVertexMax++]=Vertex ;
	return 1 ;
}
int Q3Map::AllocateLightMemory(void)
{
	// memory for the Light
	m_nLightSize=MEMADD ; // starting memory size
	m_pLightMem=malloc(m_nLightSize) ; // allocate starting memory space
	m_pLight=(light_t*)m_pLightMem ; // a pointer to the memory cast as a light_t
	m_nLightMax=0 ;
	m_nLightLimit=m_nLightSize/sizeof(light_t) ; // if pos reaches this memory must expand
	if(m_pLightMem==NULL)
		return 0 ;
	return 1 ;
}
void Q3Map::FreeLightMemory(void)
{
	if(m_pLightMem) free(m_pLightMem) ;
	m_pLightMem=NULL ;
	m_pLight=NULL ;
	m_nLightMax=0 ;
	m_nLightLimit=0 ;
}
// increase size of Light memory, return 0 if failed
int Q3Map::ExpandLightMemory(void)
{
	m_nLightSize+=MEMADD ; // increase size
	m_pLightMem=realloc(m_pLightMem, m_nLightSize) ; // reallocate the memory
	if(m_pLightMem==NULL) return 0 ; // failed to allocate memory, get out and return false
	// if here then memory allocation succeeded
	m_pLight=(light_t*)m_pLightMem ; // pointer to the memory cast as a light_t
	m_nLightLimit=m_nLightSize/sizeof(light_t) ; // if pos reaches this memory must expand
	return 1 ; // ok
}
int Q3Map::AddLight(light_t Light)
{
	if(m_nLightLimit==0) // light memory hasn't been allocated yet
	{
		if( !AllocateLightMemory() )
			return 0 ;
	}
	else
		if(m_nLightMax>=m_nLightLimit)
			if( !ExpandLightMemory() )
				return 0 ;
	m_pLight[m_nLightMax++]=Light ;
	return 1 ;
}
// lamps are deferred shading, non-shadowing point lights
int Q3Map::AllocateLampMemory(void)
{
	// memory for the Lamp
	m_nLampSize=MEMADD ; // starting memory size
	m_pLampMem=malloc(m_nLampSize) ; // allocate starting memory space
	m_pLamp=(lamp_t*)m_pLampMem ; // a pointer to the memory cast as a lamp_t
	m_nLampMax=0 ;
	m_nLampLimit=m_nLampSize/sizeof(lamp_t) ; // if pos reaches this memory must expand
	if(m_pLampMem==NULL)
		return 0 ;
	return 1 ;
}
void Q3Map::FreeLampMemory(void)
{
	if(m_pLampMem) free(m_pLampMem) ;
	m_pLampMem=NULL ;
	m_pLamp=NULL ;
	m_nLampMax=0 ;
	m_nLampLimit=0 ;
}
// increase size of Lamp memory, return 0 if failed
int Q3Map::ExpandLampMemory(void)
{
	m_nLampSize+=MEMADD ; // increase size
	m_pLampMem=realloc(m_pLampMem, m_nLampSize) ; // reallocate the memory
	if(m_pLampMem==NULL) return 0 ; // failed to allocate memory, get out and return false
	// if here then memory allocation succeeded
	m_pLamp=(lamp_t*)m_pLampMem ; // pointer to the memory cast as a lamp_t
	m_nLampLimit=m_nLampSize/sizeof(lamp_t) ; // if pos reaches this memory must expand
	return 1 ; // ok
}
int Q3Map::AddLamp(lamp_t Lamp)
{
	if(m_nLampLimit==0) // Lamp memory hasn't been allocated yet
	{
		if( !AllocateLampMemory() )
			return 0 ;
	}
	else
		if(m_nLampMax>=m_nLampLimit)
			if( !ExpandLampMemory() )
				return 0 ;
	m_pLamp[m_nLampMax++]=Lamp ;
	return 1 ;
}
//////////////
// Q3BspTexture textures.  We duplicate the loaded texture mem and then add lighting textures to it.
int Q3Map::AllocateTextureMemory(void)
{
	// memory for the Texture
	m_nTextureSize=MEMADD ; // starting memory size
	m_pTextureMem=malloc(m_nTextureSize) ; // allocate starting memory space
	m_pTexture=(Q3BspTexture*)m_pTextureMem ; // a pointer to the memory cast as a Q3BspTexture
	m_nTextureMax=0 ;
	m_nTextureLimit=m_nTextureSize/sizeof(Q3BspTexture) ; // if pos reaches this memory must expand
	if(m_pTextureMem==NULL)
		return 0 ;
	return 1 ;
}
void Q3Map::FreeTextureMemory(void)
{
	if(m_pTextureMem) free(m_pTextureMem) ;
	m_pTextureMem=NULL ;
	m_pTexture=NULL ;
	m_nTextureMax=0 ;
	m_nTextureLimit=0 ;
}
// increase size of Texture memory, return 0 if failed
int Q3Map::ExpandTextureMemory(void)
{
	m_nTextureSize+=MEMADD ; // increase size
	m_pTextureMem=realloc(m_pTextureMem, m_nTextureSize) ; // reallocate the memory
	if(m_pTextureMem==NULL) return 0 ; // failed to allocate memory, get out and return false
	// if here then memory allocation succeeded
	m_pTexture=(Q3BspTexture*)m_pTextureMem ; // pointer to the memory cast as a Q3BspTexture
	m_nTextureLimit=m_nTextureSize/sizeof(Q3BspTexture) ; // if pos reaches this memory must expand
	return 1 ; // ok
}
int Q3Map::AddTexture(Q3BspTexture Texture)
{
	if(m_nTextureLimit==0) // Texture memory hasn't been allocated yet
	{
		if( !AllocateTextureMemory() )
			return 0 ;
	}
	else
		if(m_nTextureMax>=m_nTextureLimit)
			if( !ExpandTextureMemory() )
				return 0 ;
	m_pTexture[m_nTextureMax++]=Texture ;
	return 1 ;
}
// special version of the Add function, will not add if the texture name already exist.  
// Will succeed even if the texture is already on the list, but fails if it can't add a new texture
// returns texture index, or -1 on fail
// Q3 texture names can be tricky, I think I've had cases where they ended in spaces instead of nulls,
// and they might go all the way to the end without either.
int Q3Map::AddTextureUnique(Q3BspTexture Texture)
{
	if(m_nTextureLimit==0) // Texture memory hasn't been allocated yet
		if( !AllocateTextureMemory() )
			return ADDTEXTUREUNIQUE_FAIL ; // fail
	// scan through all the newly added textures so far and see if this one already exists.
	int nTexture=0 ;
	int nPos=0 ;
	bool bMatch=false ;
	for(nTexture=0 ; nTexture=m_nTextureLimit)
		if( !ExpandTextureMemory() )
			return ADDTEXTUREUNIQUE_FAIL ; // fail
	m_pTexture[m_nTextureMax++]=Texture ;
	return m_nTextureMax-1 ; // return this new texture's index
}
////////////////////////////////////////
int Q3Map::AllocateTexLampMemory(void)
{
	// memory for the TexLamp
	m_nTexLampSize=MEMADD ; // starting memory size
	m_pTexLampMem=malloc(m_nTexLampSize) ; // allocate starting memory space
	m_pTexLamp=(int*)m_pTexLampMem ; // a pointer to the memory cast as an int
	m_nTexLampMax=0 ;
	m_nTexLampLimit=m_nTexLampSize/sizeof(int) ; // if pos reaches this memory must expand
	if(m_pTexLampMem==NULL)
		return 0 ;
	return 1 ;
}
void Q3Map::FreeTexLampMemory(void)
{
	if(m_pTexLampMem) free(m_pTexLampMem) ;
	m_pTexLampMem=NULL ;
	m_pTexLamp=NULL ;
	m_nTexLampMax=0 ;
	m_nTexLampLimit=0 ;
}
// increase size of TexLamp memory, return 0 if failed
int Q3Map::ExpandTexLampMemory(void)
{
	m_nTexLampSize+=MEMADD ; // increase size
	m_pTexLampMem=realloc(m_pTexLampMem, m_nTexLampSize) ; // reallocate the memory
	if(m_pTexLampMem==NULL) return 0 ; // failed to allocate memory, get out and return false
	// if here then memory allocation succeeded
	m_pTexLamp=(int*)m_pTexLampMem ; // pointer to the memory cast as an int
	m_nTexLampLimit=m_nTexLampSize/sizeof(int) ; // if pos reaches this memory must expand
	return 1 ; // ok
}
int Q3Map::AddTexLamp(int TexLamp)
{
	if(m_nTexLampMax>=m_nTexLampLimit)
		if( !ExpandTexLampMemory() )
			return 0 ;
	m_pTexLamp[m_nTexLampMax++]=TexLamp ;
	return 1 ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// extract entities from bsp entities lump
int Q3Map::ParseEntities(void)
{
//	char chKey[MAX_TOKENSIZE+1] ;   // +1 to leave room for null terminator
//	char chValue[MAX_TOKENSIZE+1] ; // +1 to leave room for null terminator
	int nPos=0 ;
	int nMaxPos=m_BspHeader.Lumps[0].iLength ;
	int nEntityType=0 ;
	// reset the spotlight textures
	m_nMaxSpotlightTexture=0 ;
	for(nPos=0 ; nPos='0') && (pValue[nPos]<='9')) // found another digit
				||
				(pValue[nPos]=='.')
				||
				(pValue[nPos]=='-')
			)
		{
			chTemp[nTempPos++]=pValue[nPos] ;
			if(nTempPos==MAX_TOKENSIZE) return 0 ; // number too big
		}
		else // anything else means the end of the number
		{
			
			chTemp[nTempPos]='\0' ;
			pNumber[nCount++]=atof(chTemp) ;
			nTempPos=0 ;
		}
		
	}while((nPos='0') && (pValue[nPos]<='9')) // found another digit
				||
				(pValue[nPos]=='.')
				||
				(pValue[nPos]=='-')
			)
		{
			chTemp[nTempPos++]=pValue[nPos] ;
			if(nTempPos==MAX_TOKENSIZE) return 0 ; // number too big
		}
		else // anything else means the end of the number
		{
			
			chTemp[nTempPos]='\0' ;
			pNumber[nCount++]=atoi(chTemp) ;
			nTempPos=0 ;
		}
		
	}while((nPos3)) return 0 ; // something dodgy about the lightnode number
			}
		}// end if key ok
		
	}while(nKeyReturn==KEY_OK) ;  // end do looping through keys
	// return a fail if there was a problem with the keys
	if(nKeyReturn==KEY_ERROR) return 0 ; 
	if(bSpotLight)// found a light_target so this must be a spotlight
	{
		// light settings.
		NewLight.Position[0]=flOrigin[0]+flCentre[0] ;
		NewLight.Position[1]=flOrigin[1]+flCentre[1] ;
		NewLight.Position[2]=flOrigin[2]+flCentre[2] ;
		NewLight.Min[0]=flOrigin[0]-flRadius[0] ;
		NewLight.Min[1]=flOrigin[1]-flRadius[1] ;
		NewLight.Min[2]=flOrigin[2]-flRadius[2] ;
		NewLight.Max[0]=flOrigin[0]+flRadius[0] ;
		NewLight.Max[1]=flOrigin[1]+flRadius[1] ;
		NewLight.Max[2]=flOrigin[2]+flRadius[2] ;
		NewLight.Colour[0]=flColour[0] ;
		NewLight.Colour[1]=flColour[1] ;
		NewLight.Colour[2]=flColour[2] ;
		NewLight.Angle=flAngle ;
		NewLight.Cutoff=flCutoff ;
		NewLight.Brightness=flBrightness * BRIGHTNESSTWEAK ;
		// direction light points, as a normal
		flLength=sqrt( flAimvec[0]*flAimvec[0] + flAimvec[1]*flAimvec[1] + flAimvec[2]*flAimvec[2] ) ;
		if(flLength>0.0f)
		{
			NewLight.Direction[0]=flAimvec[0]/flLength ;
			NewLight.Direction[1]=flAimvec[1]/flLength ;
			NewLight.Direction[2]=flAimvec[2]/flLength ;
		}
		else
		{ // default to pointing down
			NewLight.Direction[0]=0.0f ;
			NewLight.Direction[1]=1.0f ;
			NewLight.Direction[2]=0.0f ;
		}
		NewLight.ZoneCount=0 ;
		NewLight.CentreZone=0 ;
	
		
		if(Q3Texture.name[0]==0)	
			strcpy(Q3Texture.name, "spotlight.dds") ;
		
		NewLight.Texture=AddSpolightTexture(Q3Texture.name) ;
		if(NewLight.Texture==ADDSPOTLIGHTTEXTURE_FAIL)
			return 0 ; // failure	
	
		//AddTextureUnique(Q3Texture) ;
		/*
		// add the light's texture index
		if(Q3Texture.name[0]==0)
			NewLight.Texture=m_nDefaultTextureIndexSpotlight ;
		else
		{
			NewLight.Texture=AddTextureUnique(Q3Texture) ; // this will add the texture name to the list if it is unique, as well as setting the index
			if(NewLight.Texture==ADDTEXTUREUNIQUE_FAIL) 
				return 0 ;
		}
		*/
		return AddLight(NewLight) ;
	}
	else // add a non-shadowing deferred shading point light
	{
		// light settings.
		NewLamp.Position[0]=flOrigin[0]+flCentre[0] ;
		NewLamp.Position[1]=flOrigin[1]+flCentre[1] ;
		NewLamp.Position[2]=flOrigin[2]+flCentre[2] ;
		NewLamp.Min[0]=flOrigin[0]-flRadius[0] ;
		NewLamp.Min[1]=flOrigin[1]-flRadius[1] ;
		NewLamp.Min[2]=flOrigin[2]-flRadius[2] ;
		NewLamp.Max[0]=flOrigin[0]+flRadius[0] ;
		NewLamp.Max[1]=flOrigin[1]+flRadius[1] ;
		NewLamp.Max[2]=flOrigin[2]+flRadius[2] ;
		NewLamp.Colour[0]=flColour[0] ;
		NewLamp.Colour[1]=flColour[1] ;
		NewLamp.Colour[2]=flColour[2] ;
		NewLamp.Brightness=flBrightness * BRIGHTNESSTWEAK ;
		NewLamp.LightNode=nLightNode ; // typically -1, but may be 0 to 3 if this lamp is the node for some TexLamp freeform deferred shading geometry.
		// Note that m_pLamp.Zone will be set after we first convert lamps into triangles and then 
		// assign those triangles to zones.  At that point, if the assigned triangle is also flagged as
		// coming from a lamp, then the lamp's list of zones will be updated.
				// add the light's texture index
		if(Q3Texture.name[0]==0)
		{
			if(nLightNode==-1)
				NewLamp.Texture=m_nDefaultTextureIndexLamp ; // normal 1 pass deferred shading
			else
				NewLamp.Texture=m_nDefaultTextureIndexLamp2Pass ; // special 2 pass deferred shading to texlamps
		}
		else
		{
			NewLamp.Texture=AddTextureUnique(Q3Texture) ; // this will add the texture name to the list if it is unique, as well as setting the index
			if(NewLamp.Texture==ADDTEXTUREUNIQUE_FAIL) 
				return 0 ;
		}	
		return AddLamp(NewLamp) ;
	}
}
// adds a spotlight texture name if it is unique, returns the index to that texture name either way.
// returns ADDSPOTLIGHTTEXTURE_FAIL on a fail
int Q3Map::AddSpolightTexture(char TEXNAME[])
{
	if((strlen(TEXNAME)>Q3NAMESIZE) || (m_nMaxSpotlightTexture>=MAX_PROJECTORTEX))
		return ADDSPOTLIGHTTEXTURE_FAIL ;
	// scan through all the newly added textures so far and see if this one already exists.
	int nTexture=0 ;
	int nPos=0 ;
	bool bMatch ;
	for(nTexture=0 ; nTexturesize; j++)
          {
            numIndex += m_BspFaces[i].patch->bezier[j].mNumIndex;
            numVertex += m_BspFaces[i].patch->bezier[j].mNumVertex;
          }
					if((numIndex==0) || (numVertex==0))
					{
						DELETE_ARRAY( m_BspFaces[i].patch->bezier ) ;
						DELETE_POINTER( m_BspFaces[i].patch ) ;
					}
      }// end if patch
    }// end for 
	// copy the vertices over.  
	// We need to work on a copy because we need to create new verts when splitting triangles that cross subzones, and for patches
	for(int i=0 ; im_nMaxZone)
			m_nMaxZone=m_pSubZones[nSubZone].Zone ;
	m_nMaxZone++ ; // our limit
	
	//char chMessage[1024] ;
	// fill in what subzones are in each zone
	for(nSubZone=0 ; nSubZonem_ZoneBoundary[nZone].Max[0]) 
				m_ZoneBoundary[nZone].Max[0]= m_pSubZones[nSubZone].Max[0] ;
			if(m_pSubZones[nSubZone].Max[1]>m_ZoneBoundary[nZone].Max[1]) 
				m_ZoneBoundary[nZone].Max[1]= m_pSubZones[nSubZone].Max[1] ;
			if(m_pSubZones[nSubZone].Max[2]>m_ZoneBoundary[nZone].Max[2]) 
				m_ZoneBoundary[nZone].Max[2]= m_pSubZones[nSubZone].Max[2] ;
			m_nZone[nZone][INDEX_SUBZONECOUNT]++ ;
		}
	}
}
// work out what zone each triangle is in.
// if it is in more than one, cut it up into smaller triangles that are only in one zone each.
int Q3Map::AssignTrianglesToZones(void)
{
	int nCurrentTriangle=0 ;
//	int nZone=0 ;
	/*
	char chMessage[1024] ;
	float flPos[3] ;
	int nTri=0 ;
	float flVert[3][3] ;
	
	for(nTri=0 ; nTri-1) // if we have a lamp
		{
			int nSlot=m_pLamp[nLamp].Zone[MAX_ZONEPERLIGHT] ;
			if(nSlotflCutPos+SUBZONE_EPSILON))
					||
					((VertA.position[0]>flCutPos+SUBZONE_EPSILON) && (VertB.position[0]flCutPos+SUBZONE_EPSILON))
					||
					((VertB.position[0]>flCutPos+SUBZONE_EPSILON) && (VertC.position[0]flCutPos))
					||
					((VertC.position[0]>flCutPos) && (VertA.position[0]flCutPos+SUBZONE_EPSILON))
					||
					((VertA.position[1]>flCutPos+SUBZONE_EPSILON) && (VertB.position[1]flCutPos+SUBZONE_EPSILON))
					||
					((VertB.position[1]>flCutPos+SUBZONE_EPSILON) && (VertC.position[1]flCutPos))
					||
					((VertC.position[1]>flCutPos) && (VertA.position[1]flCutPos+SUBZONE_EPSILON))
					||
					((VertA.position[2]>flCutPos+SUBZONE_EPSILON) && (VertB.position[2]flCutPos+SUBZONE_EPSILON))
					||
					((VertB.position[2]>flCutPos+SUBZONE_EPSILON) && (VertC.position[2]flCutPos))
					||
					((VertC.position[2]>flCutPos) && (VertA.position[2]color[0]=(unsigned char)(flPercent0*pVertA->color[0] + flPercent1*pVertB->color[0]) ;
	pVertexAB->color[1]=(unsigned char)(flPercent0*pVertA->color[1] + flPercent1*pVertB->color[1]) ;
	pVertexAB->color[2]=(unsigned char)(flPercent0*pVertA->color[2] + flPercent1*pVertB->color[2]) ;
	pVertexAB->color[3]=(unsigned char)(flPercent0*pVertA->color[3] + flPercent1*pVertB->color[3]) ;
	pVertexAB->position[0]=flPercent0*pVertA->position[0] + flPercent1*pVertB->position[0] ;
	pVertexAB->position[1]=flPercent0*pVertA->position[1] + flPercent1*pVertB->position[1] ;
	pVertexAB->position[2]=flPercent0*pVertA->position[2] + flPercent1*pVertB->position[2] ;
				
	pVertexAB->texcoord[0][0]=flPercent0*pVertA->texcoord[0][0] + flPercent1*pVertB->texcoord[0][0] ;
	pVertexAB->texcoord[0][1]=flPercent0*pVertA->texcoord[0][1] + flPercent1*pVertB->texcoord[0][1] ;
	pVertexAB->texcoord[1][0]=flPercent0*pVertA->texcoord[1][0] + flPercent1*pVertB->texcoord[1][0] ;
	pVertexAB->texcoord[1][1]=flPercent0*pVertA->texcoord[1][1] + flPercent1*pVertB->texcoord[1][1] ;
	pVertexAB->normal[0]=flPercent0*pVertA->normal[0] + flPercent1*pVertB->normal[0] ;
	pVertexAB->normal[1]=flPercent0*pVertA->normal[1] + flPercent1*pVertB->normal[1] ;
	pVertexAB->normal[2]=flPercent0*pVertA->normal[2] + flPercent1*pVertB->normal[2] ;
	// normalize
	float flLen=sqrt(pVertexAB->normal[0]*pVertexAB->normal[0] + pVertexAB->normal[1]*pVertexAB->normal[1] + pVertexAB->normal[2]*pVertexAB->normal[2]) ;
	if(flLen!=0.0f) // don't divide by zero... but normal is messed up.
	{
		pVertexAB->normal[0]/=flLen ;
		pVertexAB->normal[1]/=flLen ;
		pVertexAB->normal[2]/=flLen ;
	}
	else
	{
		// default a messed up normal to point upward
		pVertexAB->normal[0]=0.0f ;
		pVertexAB->normal[1]=1.0f ;
		pVertexAB->normal[2]=0.0f ;
	}
}
// returns the next subzone a point is in after the start subzone, or -1 if there are no more subzones
int Q3Map::GetNextSubZone(float *flPoint, int nStart, int nMax)
{
	while(++nStart=m_pSubZones[nSubZone].Min[0]-SUBZONE_EPSILON) && (flPoint[0]<=m_pSubZones[nSubZone].Max[0]+SUBZONE_EPSILON)
			&&(flPoint[1]>=m_pSubZones[nSubZone].Min[1]-SUBZONE_EPSILON) && (flPoint[1]<=m_pSubZones[nSubZone].Max[1]+SUBZONE_EPSILON)
			&&(flPoint[2]>=m_pSubZones[nSubZone].Min[2]-SUBZONE_EPSILON) && (flPoint[2]<=m_pSubZones[nSubZone].Max[2]+SUBZONE_EPSILON)
		)
		return true ;
	return false ;
}
// returns true if a point is in a zone.
bool Q3Map::PointInZone(float *flPos, int nZone)
{
	int nMaxSubZone=m_nZone[nZone][INDEX_SUBZONECOUNT] ;
	for(int nSubZoneIndex=0 ; nSubZoneIndexflPointMin[0])
			&& (m_pSubZones[nSubZone].Min[1]flPointMin[1])
			&& (m_pSubZones[nSubZone].Min[2]flPointMin[2])
		)
		return true ;
	return false ;
}
// returns true if an axis aligned bounding box touches a zone.
bool Q3Map::AABBTouchesZone(float *flPosMin, float *flPosMax, int nZone)
{
	int nMaxSubZone=m_nZone[nZone][INDEX_SUBZONECOUNT] ;
	for(int nSubZoneIndex=0 ; nSubZoneIndex=m_nTriangleLimit)
						if(!ExpandTriangleMemory())
							return 0 ;
					
					m_pTriangle[ m_nTriangleMax ].Texture=		m_BspFaces[nFaceIndex].texture ;
					//m_pTriangle[ m_nTriangleMax ].Lightmap=		m_BspFaces[nFaceIndex].lm_index ; // bzn doesn't use lightmaps
					m_pTriangle[ m_nTriangleMax ].VIndex[0]=	meshverts[ nMeshVert++ ]+m_BspFaces[nFaceIndex].vertex ;
					m_pTriangle[ m_nTriangleMax ].VIndex[1]=	meshverts[ nMeshVert++ ]+m_BspFaces[nFaceIndex].vertex ;
					m_pTriangle[ m_nTriangleMax ].VIndex[2]=	meshverts[ nMeshVert++ ]+m_BspFaces[nFaceIndex].vertex ;
					m_pTriangle[ m_nTriangleMax ].Lamp=-1 ; // assume it didn't come from a lamp, this will be updated later
					
					m_pTriangle[ m_nTriangleMax ].Group=m_nGroup ; // increment group number.  
					m_nTriangleMax++ ;
				}// end for nTriangle
				m_nGroup++ ; // increment group.  Every face is a new group.
				
			break ;
		}// end switch
		
		nFaceIndex++;
	} // end while	
	return 1 ;
}
// convert the patch info from the BSP into bezier curved triangle meshes and add to our triangle list.
int Q3Map::ConvertPatchesToTriangles(void)
{
//	float flPosX=0.0f ;
//	float flPosY=0.0f ;
//	float flPosZ=0.0f ;
//	float	flNormX=0.0f ;
//	float	flNormY=0.0f ;
//	float	flNormZ=0.0f ;
//	float flTexU=0.0f ;
//	float flTexV=0.0f ;
//	int nMeshVert=0 ;
	int nMeshVertA=0 ;
	int nMeshVertB=0 ;
	int nMeshVertC=0 ;
	int nTriPerRow=0 ;
	int nRow=0 ;
	int nFirstVertex=m_nVertexMax ;
	
	int nVertCount=nFirstVertex ;
//	int nPatchCount=0 ;
	int* pIndexBuffer=NULL ;
	Q3BspVertex NewVert ;
		
	int nCount=0 ;
	int nCountB=0 ;
		
    int indexBufferindex = 0;
	
    int vertexBufferindex = 0;
    for (int faceIndex=0; faceIndex < m_iNumFaces; faceIndex++)
    {		
			nCount++ ;
			if(nCount==1)
			{
				nCountB+=nCount ;
				nCount=0 ;
			}
      if (m_BspFaces[faceIndex].type == PATCH)
      {
        Q3BspPatch *patch = m_BspFaces[faceIndex].patch;
        if (patch != NULL)
        {
					
          for (int bezierIndex=0; bezierIndex < patch->size; bezierIndex++)
          {
						indexBufferindex = 0;
						pIndexBuffer = new int[patch->bezier[bezierIndex].mNumIndex] ;
						if(pIndexBuffer==NULL) return 0 ; // ran out of memory
            for (int index=0; index < patch->bezier[bezierIndex].mNumIndex; index++)
            {	
              pIndexBuffer[indexBufferindex] = patch->bezier[bezierIndex].mIndex[index];
              indexBufferindex++;
            }
            for (int vertex=0; vertex < patch->bezier[bezierIndex].mNumVertex; vertex++)
            {
              BspVertex *bspVertex = &patch->bezier[bezierIndex].mVertex[vertex];
							NewVert.position[0]=bspVertex->mPosition[0] ;
							NewVert.position[1]=bspVertex->mPosition[1] ;
							NewVert.position[2]=bspVertex->mPosition[2] ;
							NewVert.normal[0]=bspVertex->mNormal[0] ;
							NewVert.normal[1]=bspVertex->mNormal[1] ;
							NewVert.normal[2]=bspVertex->mNormal[2] ;
							NewVert.texcoord[0][0]=bspVertex->mTexcoord[0][0] ;
							NewVert.texcoord[0][1]=bspVertex->mTexcoord[0][1] ;
							
							// if we are out of memory, grow it.  If we can't grow it, fail
							if(m_nVertexMax>=m_nVertexLimit)
								if(!ExpandVertexMemory())
								{
									if(pIndexBuffer) DELETE_ARRAY( pIndexBuffer ) ;
									return 0 ;
								}
							if(!AddVertex(NewVert)) 
							{
								if(pIndexBuffer) DELETE_ARRAY( pIndexBuffer ) ;
								return 0 ;
							}
							nVertCount++ ;
              vertexBufferindex++;
            }// end for vertex
						for (int j=0; j < 5; j++)
						{
							nRow=m_BspFaces[faceIndex].patch->bezier[bezierIndex].mRowIndex[j] ;
							nTriPerRow=m_BspFaces[faceIndex].patch->bezier[bezierIndex].mTrianglesPerRow[j] ;
							nMeshVertA=pIndexBuffer[nRow+0]+nFirstVertex ;
							nMeshVertB=pIndexBuffer[nRow+1]+nFirstVertex ;
							
							for(int nVert=2 ; nVert=m_nTriangleLimit)
									if(!ExpandTriangleMemory())
									{
										if(pIndexBuffer) DELETE_ARRAY( pIndexBuffer ) ;
										return 0 ;
									}
								m_pTriangle[ m_nTriangleMax ].Texture=		m_BspFaces[faceIndex].texture ;
								//m_pTriangle[ m_nTriangleMax ].Lightmap=		m_BspFaces[faceIndex].lm_index ; // bzn doesn't use lightmaps
								
								
								nMeshVertC=pIndexBuffer[nRow+nVert]+nFirstVertex ;
								if(nVert&1)
								{
									m_pTriangle[ m_nTriangleMax ].VIndex[0]=	nMeshVertB ;
									m_pTriangle[ m_nTriangleMax ].VIndex[1]=	nMeshVertA ;
									m_pTriangle[ m_nTriangleMax ].VIndex[2]=	nMeshVertC ;
								}
								else
								{
									m_pTriangle[ m_nTriangleMax ].VIndex[0]=	nMeshVertA ;
									m_pTriangle[ m_nTriangleMax ].VIndex[1]=	nMeshVertB ;
									m_pTriangle[ m_nTriangleMax ].VIndex[2]=	nMeshVertC ;
								}
								m_pTriangle[ m_nTriangleMax ].Lamp=-1 ; // assume it didn't come from a lamp, this will be updated later
								m_pTriangle[ m_nTriangleMax ].Group=m_nGroup ;
								m_nTriangleMax++ ;
								nMeshVertA=nMeshVertB ;
								nMeshVertB=nMeshVertC ;
							}
				
						}
						
						// finished with the index buffer
						if(pIndexBuffer) 
							DELETE_ARRAY( pIndexBuffer ) ; 
						nFirstVertex=nVertCount ;
          }// end for bezier index
					m_nGroup++ ; // increment the group number.  Each patch is treated as a single group.
        }// end if patch not null
      }// end if patch
    }// end for faceIndex
	return 1 ;
}
//''
// some triangles might be designed to be deferred shading shapes inside of Lamp entities.
// If so, change their material to the deferred shading configuration for that lamp entity.
int Q3Map::ConvertTexLampsToLampTriangles(void)
{
	float flCentreX=0.0f ;
	float flCentreY=0.0f ;
	float flCentreZ=0.0f ;
//	float flMinX=0.0f ;
//	float flMinY=0.0f ;
//	float flMinZ=0.0f ;
//	float flMaxX=0.0f ;
//	float flMaxY=0.0f ;
//	float flMaxZ=0.0f ;
//	float	flNormX=0.0f ;
//	float	flNormY=0.0f ;
//	float	flNormZ=0.0f ;
//	float flTexU=0.0f ;
//	float flTexV=0.0f ;
	float flColR=0.0f ;
	float flColG=0.0f ;
	float flColB=0.0f ;
	float flBrightness=0.0f ;
	int nLamp=0 ;
	int nTriangle=0 ;
	int nTexLampListPos=0 ;
	int nLightNode=0 ;
	int nTexture=0 ;
	int nZone=0 ;
//	int nLampZone=0 ;
//	int nMaxLampZone=0 ;
//	int nZoneMatch=0 ;
	for(nTexLampListPos=0 ; nTexLampListPos=m_pLamp[nLamp].Min[0])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[0]<=m_pLamp[nLamp].Max[0])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[1]>=m_pLamp[nLamp].Min[1])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[1]<=m_pLamp[nLamp].Max[1])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[2]>=m_pLamp[nLamp].Min[2])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[2]<=m_pLamp[nLamp].Max[2])	
					&&
						// second vert
						(m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[0]>=m_pLamp[nLamp].Min[0])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[0]<=m_pLamp[nLamp].Max[0])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[1]>=m_pLamp[nLamp].Min[1])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[1]<=m_pLamp[nLamp].Max[1])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[2]>=m_pLamp[nLamp].Min[2])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[2]<=m_pLamp[nLamp].Max[2])	
					&&
						// third vert
						(m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[0]>=m_pLamp[nLamp].Min[0])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[0]<=m_pLamp[nLamp].Max[0])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[1]>=m_pLamp[nLamp].Min[1])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[1]<=m_pLamp[nLamp].Max[1])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[2]>=m_pLamp[nLamp].Min[2])
					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[2]<=m_pLamp[nLamp].Max[2])	
	
					)
			{
				m_pTriangle[nTriangle].Texture=m_pLamp[nLamp].Texture ;
				m_pTriangle[nTriangle].Lamp=nLamp ;
				flCentreX =		m_pLamp[nLamp].Position[0] ;
				flCentreY =		m_pLamp[nLamp].Position[1] ;
				flCentreZ =		m_pLamp[nLamp].Position[2] ;
				flColR =			m_pLamp[nLamp].Colour[0]*255.0f ;
				flColG =			m_pLamp[nLamp].Colour[1]*255.0f ;
				flColB =			m_pLamp[nLamp].Colour[2]*255.0f ;
				flBrightness=	m_pLamp[nLamp].Brightness ; 
				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].texcoord[0][0]=flCentreX ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].texcoord[0][1]=flCentreY ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].texcoord[1][0]=flCentreZ ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].texcoord[1][1]=flBrightness ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].color[0]=(unsigned char)flColR ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].color[1]=(unsigned char)flColG ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].color[2]=(unsigned char)flColB ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].texcoord[0][0]=flCentreX ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].texcoord[0][1]=flCentreY ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].texcoord[1][0]=flCentreZ ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].texcoord[1][1]=flBrightness ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].color[0]=(unsigned char)flColR ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].color[1]=(unsigned char)flColG ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].color[2]=(unsigned char)flColB ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].texcoord[0][0]=flCentreX ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].texcoord[0][1]=flCentreY ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].texcoord[1][0]=flCentreZ ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].texcoord[1][1]=flBrightness ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].color[0]=(unsigned char)flColR ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].color[1]=(unsigned char)flColG ;
				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].color[2]=(unsigned char)flColB ;
				
				m_pTriangle[ m_nTriangleMax ].Group=m_nGroup ;
			}// end triangle is in bounds of lamp
			m_nGroup++ ; // increment group number.  Every texture lamp is a group.
		}// end for nLamp
	}
	return 1 ;
}
// convert lamps into boxes.  Lamps are deferred shading non-shadowing lights, and are rendered as triangles
// The texture coords are actually the light centre point, the spot the shaders calculate as the source of the lighting, 
// and also the brightness
// The triangles created will remember the lamp they came from, via Triangle.Lamp
int Q3Map::ConvertLampsToTriangles(void)
{
	float flCentreX=0.0f ;
	float flCentreY=0.0f ;
	float flCentreZ=0.0f ;
	float flMinX=0.0f ;
	float flMinY=0.0f ;
	float flMinZ=0.0f ;
	float flMaxX=0.0f ;
	float flMaxY=0.0f ;
	float flMaxZ=0.0f ;
//	float	flNormX=0.0f ;
//	float	flNormY=0.0f ;
//	float	flNormZ=0.0f ;
//	float flTexU=0.0f ;
//	float flTexV=0.0f ;
	float flColR=0.0f ;
	float flColG=0.0f ;
	float flColB=0.0f ;
	float flBrightness=0.0f ;
	int nLamp=0 ;
	// lower case = min, upper case = max
	Q3BspVertex Vert_xyz ;
	Q3BspVertex Vert_Xyz ;
	Q3BspVertex Vert_xYz ;
	Q3BspVertex Vert_XYz ;
	Q3BspVertex Vert_xyZ ;
	Q3BspVertex Vert_XyZ ;
	Q3BspVertex Vert_xYZ ;
	Q3BspVertex Vert_XYZ ;
	int n_xyz=0 ;
	int n_Xyz=0 ;
	int n_xYz=0 ;
	int n_XYz=0 ;
	int n_xyZ=0 ;
	int n_XyZ=0 ;
	int n_xYZ=0 ;
	int n_XYZ=0 ;
	int nFirstVertex=0 ;
	triangle_t Triangle ;
	ZeroMemory((void*)&Triangle, sizeof(triangle_t)) ;
	
	for(nLamp=0 ; nLamp-1) continue ; // lamps that are lightnodes don't add their own triangles.  They just exist as information for TexLamps.
		flCentreX =		m_pLamp[nLamp].Position[0] ;
		flCentreY =		m_pLamp[nLamp].Position[1] ;
		flCentreZ =		m_pLamp[nLamp].Position[2] ;
		flMinX =			m_pLamp[nLamp].Min[0] ;
		flMinY =			m_pLamp[nLamp].Min[1] ;
		flMinZ =			m_pLamp[nLamp].Min[2] ;
		flMaxX =			m_pLamp[nLamp].Max[0] ;
		flMaxY =			m_pLamp[nLamp].Max[1] ;
		flMaxZ =			m_pLamp[nLamp].Max[2] ;
		flColR =			m_pLamp[nLamp].Colour[0]*255.0f ;
		flColG =			m_pLamp[nLamp].Colour[1]*255.0f ;
		flColB =			m_pLamp[nLamp].Colour[2]*255.0f ;
		
		flBrightness=	m_pLamp[nLamp].Brightness ; 
		
		//////////////////////////////////////
		// setup our 8 vertices.  Normal isn't that important, I just approximate regardless of actual box shape
		nFirstVertex=m_nVertexMax ; // we need to remember which vertex is which for defining the triangles
		// vertex numbers
		n_xyz=nFirstVertex+0 ;
		n_Xyz=nFirstVertex+1 ;
		n_xYz=nFirstVertex+2 ;
		n_XYz=nFirstVertex+3 ;
		n_xyZ=nFirstVertex+4 ;
		n_XyZ=nFirstVertex+5 ;
		n_xYZ=nFirstVertex+6 ;
		n_XYZ=nFirstVertex+7 ;
		Vert_xyz.position[0]=flMinX ;
		Vert_xyz.position[1]=flMinY ;
		Vert_xyz.position[2]=flMinZ ;
		Vert_xyz.normal[0]=-0.5773502691896 ;
		Vert_xyz.normal[1]=-0.5773502691896 ;
		Vert_xyz.normal[2]=-0.5773502691896 ;
		Vert_xyz.texcoord[0][0]=flCentreX ;
		Vert_xyz.texcoord[0][1]=flCentreY ;
		Vert_xyz.texcoord[1][0]=flCentreZ ;
		Vert_xyz.texcoord[1][1]=flBrightness ;
		Vert_xyz.color[0]=(unsigned char)flColR ;
		Vert_xyz.color[1]=(unsigned char)flColG ;
		Vert_xyz.color[2]=(unsigned char)flColB ;
		if(!AddVertex(Vert_xyz)) return 0 ;
			
		Vert_Xyz.position[0]=flMaxX ;
		Vert_Xyz.position[1]=flMinY ;
		Vert_Xyz.position[2]=flMinZ ;
		Vert_Xyz.normal[0]= 0.5773502691896 ;
		Vert_Xyz.normal[1]=-0.5773502691896 ;
		Vert_Xyz.normal[2]=-0.5773502691896 ;
		Vert_Xyz.texcoord[0][0]=flCentreX ;
		Vert_Xyz.texcoord[0][1]=flCentreY ;
		Vert_Xyz.texcoord[1][0]=flCentreZ ;
		Vert_Xyz.texcoord[1][1]=flBrightness ;
		Vert_Xyz.color[0]=(unsigned char)flColR ;
		Vert_Xyz.color[1]=(unsigned char)flColG ;
		Vert_Xyz.color[2]=(unsigned char)flColB ;
		if(!AddVertex(Vert_Xyz)) return 0 ;
		Vert_xYz.position[0]=flMinX ;
		Vert_xYz.position[1]=flMaxY ;
		Vert_xYz.position[2]=flMinZ ;
		Vert_xYz.normal[0]=-0.5773502691896 ;
		Vert_xYz.normal[1]= 0.5773502691896 ;
		Vert_xYz.normal[2]=-0.5773502691896 ;
		Vert_xYz.texcoord[0][0]=flCentreX ;
		Vert_xYz.texcoord[0][1]=flCentreY ;
		Vert_xYz.texcoord[1][0]=flCentreZ ;
		Vert_xYz.texcoord[1][1]=flBrightness ;
		Vert_xYz.color[0]=(unsigned char)flColR ;
		Vert_xYz.color[1]=(unsigned char)flColG ;
		Vert_xYz.color[2]=(unsigned char)flColB ;
		if(!AddVertex(Vert_xYz)) return 0 ;
		Vert_XYz.position[0]=flMaxX ;
		Vert_XYz.position[1]=flMaxY ;
		Vert_XYz.position[2]=flMinZ ;
		Vert_XYz.normal[0]= 0.5773502691896 ;
		Vert_XYz.normal[1]= 0.5773502691896 ;
		Vert_XYz.normal[2]=-0.5773502691896 ;
		Vert_XYz.texcoord[0][0]=flCentreX ;
		Vert_XYz.texcoord[0][1]=flCentreY ;
		Vert_XYz.texcoord[1][0]=flCentreZ ;
		Vert_XYz.texcoord[1][1]=flBrightness ;
		Vert_XYz.color[0]=(unsigned char)flColR ;
		Vert_XYz.color[1]=(unsigned char)flColG ;
		Vert_XYz.color[2]=(unsigned char)flColB ;
		if(!AddVertex(Vert_XYz)) return 0 ;
		//////////////////////////////////////
		Vert_xyZ.position[0]=flMinX ;
		Vert_xyZ.position[1]=flMinY ;
		Vert_xyZ.position[2]=flMaxZ ;
		Vert_xyZ.normal[0]=-0.5773502691896 ;
		Vert_xyZ.normal[1]=-0.5773502691896 ;
		Vert_xyZ.normal[2]= 0.5773502691896 ;
		Vert_xyZ.texcoord[0][0]=flCentreX ;
		Vert_xyZ.texcoord[0][1]=flCentreY ;
		Vert_xyZ.texcoord[1][0]=flCentreZ ;
		Vert_xyZ.texcoord[1][1]=flBrightness ;
		Vert_xyZ.color[0]=(unsigned char)flColR ;
		Vert_xyZ.color[1]=(unsigned char)flColG ;
		Vert_xyZ.color[2]=(unsigned char)flColB ;
		if(!AddVertex(Vert_xyZ)) return 0 ;
		Vert_XyZ.position[0]=flMaxX ;
		Vert_XyZ.position[1]=flMinY ;
		Vert_XyZ.position[2]=flMaxZ ;
		Vert_XyZ.normal[0]= 0.5773502691896 ;
		Vert_XyZ.normal[1]=-0.5773502691896 ;
		Vert_XyZ.normal[2]= 0.5773502691896 ;
		Vert_XyZ.texcoord[0][0]=flCentreX ;
		Vert_XyZ.texcoord[0][1]=flCentreY ;
		Vert_XyZ.texcoord[1][0]=flCentreZ ;
		Vert_XyZ.texcoord[1][1]=flBrightness ;
		Vert_XyZ.color[0]=(unsigned char)flColR ;
		Vert_XyZ.color[1]=(unsigned char)flColG ;
		Vert_XyZ.color[2]=(unsigned char)flColB ;
		if(!AddVertex(Vert_XyZ)) return 0 ;
		Vert_xYZ.position[0]=flMinX ;
		Vert_xYZ.position[1]=flMaxY ;
		Vert_xYZ.position[2]=flMaxZ ;
		Vert_xYZ.normal[0]=-0.5773502691896 ;
		Vert_xYZ.normal[1]= 0.5773502691896 ;
		Vert_xYZ.normal[2]= 0.5773502691896 ;
		Vert_xYZ.texcoord[0][0]=flCentreX ;
		Vert_xYZ.texcoord[0][1]=flCentreY ;
		Vert_xYZ.texcoord[1][0]=flCentreZ ;
		Vert_xYZ.texcoord[1][1]=flBrightness ;
		Vert_xYZ.color[0]=(unsigned char)flColR ;
		Vert_xYZ.color[1]=(unsigned char)flColG ;
		Vert_xYZ.color[2]=(unsigned char)flColB ;
		if(!AddVertex(Vert_xYZ)) return 0 ;
		Vert_XYZ.position[0]=flMaxX ;
		Vert_XYZ.position[1]=flMaxY ;
		Vert_XYZ.position[2]=flMaxZ ;
		Vert_XYZ.normal[0]= 0.5773502691896 ;
		Vert_XYZ.normal[1]= 0.5773502691896 ;
		Vert_XYZ.normal[2]= 0.5773502691896 ;
		Vert_XYZ.texcoord[0][0]=flCentreX ;
		Vert_XYZ.texcoord[0][1]=flCentreY ;
		Vert_XYZ.texcoord[1][0]=flCentreZ ;
		Vert_XYZ.texcoord[1][1]=flBrightness ;
		Vert_XYZ.color[0]=(unsigned char)flColR ;
		Vert_XYZ.color[1]=(unsigned char)flColG ;
		Vert_XYZ.color[2]=(unsigned char)flColB ;
		if(!AddVertex(Vert_XYZ)) return 0 ;
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		Triangle.Texture=m_pLamp[nLamp].Texture ;
		Triangle.Lamp=nLamp ;
		Triangle.Group=m_nGroup ;
		/////////////////////////////////////
		Triangle.VIndex[2]=n_xyz ;
		Triangle.VIndex[1]=n_xyZ ;
		Triangle.VIndex[0]=n_xYZ ;
		if(!AddTriangle(Triangle)) return 0 ;
		Triangle.VIndex[2]=n_xYZ ;
		Triangle.VIndex[1]=n_xYz ;
		Triangle.VIndex[0]=n_xyz ;
		if(!AddTriangle(Triangle)) return 0 ;
	
		/////////////////////////////////////
		Triangle.VIndex[0]=n_Xyz ;
		Triangle.VIndex[1]=n_XyZ ;
		Triangle.VIndex[2]=n_XYZ ;
		if(!AddTriangle(Triangle)) return 0 ;
		Triangle.VIndex[0]=n_XYZ ;
		Triangle.VIndex[1]=n_XYz ;
		Triangle.VIndex[2]=n_Xyz ;
		if(!AddTriangle(Triangle)) return 0 ;
		/////////////////////////////////////
		Triangle.VIndex[2]=n_xyz ;
		Triangle.VIndex[1]=n_xYz ;
		Triangle.VIndex[0]=n_XYz ;
		if(!AddTriangle(Triangle)) return 0 ;
		Triangle.VIndex[2]=n_XYz ;
		Triangle.VIndex[1]=n_Xyz ;
		Triangle.VIndex[0]=n_xyz ;
		if(!AddTriangle(Triangle)) return 0 ;
	
		/////////////////////////////////////
		Triangle.VIndex[0]=n_xyZ ;
		Triangle.VIndex[1]=n_xYZ ;
		Triangle.VIndex[2]=n_XYZ ;
		if(!AddTriangle(Triangle)) return 0 ;
		Triangle.VIndex[0]=n_XYZ ;
		Triangle.VIndex[1]=n_XyZ ;
		Triangle.VIndex[2]=n_xyZ ;
		if(!AddTriangle(Triangle)) return 0 ;
	
		/////////////////////////////////////
		Triangle.VIndex[0]=n_xyz ;
		Triangle.VIndex[1]=n_xyZ ;
		Triangle.VIndex[2]=n_XyZ ;
		if(!AddTriangle(Triangle)) return 0 ;
		Triangle.VIndex[0]=n_XyZ ;
		Triangle.VIndex[1]=n_Xyz ;
		Triangle.VIndex[2]=n_xyz ;
		if(!AddTriangle(Triangle)) return 0 ;
		/////////////////////////////////////
		Triangle.VIndex[2]=n_xYz ;
		Triangle.VIndex[1]=n_xYZ ;
		Triangle.VIndex[0]=n_XYZ ;
		if(!AddTriangle(Triangle)) return 0 ;
		Triangle.VIndex[2]=n_XYZ ;
		Triangle.VIndex[1]=n_XYz ;
		Triangle.VIndex[0]=n_xYz ;
		if(!AddTriangle(Triangle)) return 0 ;
	
		m_nGroup++ ; // increment group once for every lamp
	}
	
	return 1 ;
}
int Q3Map::ConvertLampsToGlowTriangles(void)
{
	float flCentreX=0.0f ;
	float flCentreY=0.0f ;
	float flCentreZ=0.0f ;
	float flMinX=0.0f ;
	float flMinY=0.0f ;
	float flMinZ=0.0f ;
	float flMaxX=0.0f ;
	float flMaxY=0.0f ;
	float flMaxZ=0.0f ;
//	float	flNormX=0.0f ;
//	float	flNormY=0.0f ;
//	float	flNormZ=0.0f ;
//	float flTexU=0.0f ;
//	float flTexV=0.0f ;
	float flColR=0.0f ;
	float flColG=0.0f ;
	float flColB=0.0f ;
	float flBrightness=0.0f ;
	int nLamp=0 ;
	Q3BspVertex Vert_L ;
	Q3BspVertex Vert_R ;
	Q3BspVertex Vert_F ;
	Q3BspVertex Vert_B ;
	Q3BspVertex Vert_U ;
	Q3BspVertex Vert_D ;
	int n_L=0 ;
	int n_R=0 ;
	int n_F=0 ;
	int n_B=0 ;
	int n_U=0 ;
	int n_D=0 ;
	
	int nFirstVertex=0 ;
	float flBaseGlowSize=0.15f ;//0.2f;//0.001f ;
	float flGlowSize=0.0f ;
	triangle_t Triangle ;
	ZeroMemory((void*)&Triangle, sizeof(triangle_t)) ;
	
	for(nLamp=0 ; nLamp-1) continue ; // Lights that are lightnodes don't add their own triangles.  They just exist as information for TexLights.
		flGlowSize=flBaseGlowSize*m_pLight[nLight].Brightness ;
		flCentreX =		m_pLight[nLight].Position[0] ;
		flCentreY =		m_pLight[nLight].Position[1] ;
		flCentreZ =		m_pLight[nLight].Position[2] ;
		flMinX =			flCentreX-flGlowSize ;
		flMinY =			flCentreY-flGlowSize ;
		flMinZ =			flCentreZ-flGlowSize ;
		flMaxX =			flCentreX+flGlowSize ;
		flMaxY =			flCentreY+flGlowSize ;
		flMaxZ =			flCentreZ+flGlowSize ;
		flColR =			m_pLight[nLight].Colour[0]*255.0f ;
		flColG =			m_pLight[nLight].Colour[1]*255.0f ;
		flColB =			m_pLight[nLight].Colour[2]*255.0f ;
		
		flBrightness=	m_pLight[nLight].Brightness * 0.75 ;//*2.0 ; 
		
		//////////////////////////////////////
		// setup our 5 vertices.
		nFirstVertex=m_nVertexMax ; // we need to remember which vertex is which for defining the triangles
		// vertex numbers
		n_Or=nFirstVertex+0 ;
		n_A0=nFirstVertex+1 ;
		n_A1=nFirstVertex+2 ;
		n_B0=nFirstVertex+3 ;
		n_B1=nFirstVertex+4 ;
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		
	
	float flHALFPI =	1.5707963 ;
	float flTWOPI =		6.2831853 ;
	float flPI =			3.1415926 ;
	float flAngle=(m_pLight[nLight].Angle/360.0f*flTWOPI)/2.0 ;
	float flCutoff=32.0f ;//m_pLight[nLight].Brightness ;
	
	Q3BspVertex color ;
	Q3BspVertex NormalVert ;
	SetVertex(&NormalVert, m_pLight[nLight].Direction[0], m_pLight[nLight].Direction[1], m_pLight[nLight].Direction[2]) ;
	NormalVert=GetNormalised(&NormalVert) ;
	Q3BspVertex normal ;
	SetVertex(&normal, m_pLight[nLight].Direction[0]*flCutoff, m_pLight[nLight].Direction[1]*flCutoff, m_pLight[nLight].Direction[2]*flCutoff) ;
	Q3BspVertex adjust=normal ;
	VertexScale(&adjust, 0.5) ;
	Q3BspVertex start ;		SetVertex(&start, flCentreX, flCentreY, flCentreZ) ;
	Q3BspVertex end ;			SetVertex(&end, flCentreX+normal.position[0], flCentreY+normal.position[1], flCentreZ+normal.position[2]) ;
	Q3BspVertex xaxis ; SetVertex(&xaxis, 1,0,0) ;
	Q3BspVertex xaxisneg ; SetVertex(&xaxisneg, -1,0,0) ;
	Q3BspVertex yaxis ; SetVertex(&yaxis, 0,1,0) ;
	Q3BspVertex Origin ; SetVertex(&Origin, 0,0,0) ;
	Q3BspVertex tangentA ;
	Q3BspVertex tangentB ;
	Q3BspVertex tangentStart ;
	Q3BspVertex tangentEnd ;
	Q3BspVertex tangentEndA0 ;
	Q3BspVertex tangentEndA1 ;
	Q3BspVertex tangentEndB0 ;
	Q3BspVertex tangentEndB1 ;
	Q3BspVertex tangentANorm ;
	Q3BspVertex tangentBNorm ;
	Q3BspVertex raytangent ;
	Q3BspVertex ray ;
	float theta=0.0f ;
	float adjacent=0.0f ;
	SetVertex(&color, 0.66, 0.66, 0.66) ;
	if(flAngle<0.0001) return 0 ;
	if(flAngle>flPI-0.01) // near on 180 degrees
		flAngle=flPI-0.01 ;
	Q3BspVertex backshift ;
	backshift=normal ;
	VertexScale(&backshift, 0.95) ;
	
	if( !VectorsAreEqual(&NormalVert, &xaxis) && !VectorsAreEqual(&NormalVert, &xaxisneg) )
		tangentA=NormalizedCrossProduct(Origin, normal, xaxis) ;
	else
		tangentA=NormalizedCrossProduct(Origin, normal, yaxis) ;
	
	tangentB=NormalizedCrossProduct(Origin, normal, tangentA) ;
	tangentANorm=tangentA ;
	tangentBNorm=tangentB ;
	
	theta=flHALFPI-flAngle ; // angle between adjacent and hypotenuse (the normal is the "opposite" side, and we know there's a right angle)
	adjacent=VertexDistance(&Origin, &normal)/tan(theta) ; 
	//////////////////////////////////////////////////////////////////////
	
	Vert_Or.position[0]=end.position[0]-backshift.position[0] ;
	Vert_Or.position[1]=end.position[1]-backshift.position[1] ;
	Vert_Or.position[2]=end.position[2]-backshift.position[2] ;
	Vert_Or.normal[0]= NormalVert.position[0] ;
	Vert_Or.normal[1]= NormalVert.position[1] ;
	Vert_Or.normal[2]= NormalVert.position[2] ;
	Vert_Or.texcoord[0][0]=flCentreX ;
	Vert_Or.texcoord[0][1]=flCentreY ;
	Vert_Or.texcoord[1][0]=flCentreZ ;
	Vert_Or.texcoord[1][1]=flBrightness ;
	Vert_Or.color[0]=(unsigned char)flColR ;
	Vert_Or.color[1]=(unsigned char)flColG ;
	Vert_Or.color[2]=(unsigned char)flColB ;
	if(!AddVertex(Vert_Or)) return 0 ;
	//////////////////////////////////////////////////////////////////////
	flColR=0.0f ;
	flColG=0.0f ;
	flColB=0.0f ;
	tangentA=GetNormalised(&tangentA) ;
	VertexScale(&tangentA,  adjacent) ; 
	tangentStart=start ;
	tangentEnd=VectorSubtract(&end, &tangentA) ;
	ray=VectorSubtract(&tangentEnd, &tangentStart) ;
	ray=GetNormalised(&ray) ;
	VertexScale(&ray, flCutoff) ;
	tangentStart=start ;
	tangentEndA0=VectorAdd(&start, &ray) ;
	raytangent=VectorSubtract(&end, &tangentEndA0) ;
	raytangent=VectorAdd(&raytangent, &adjust) ;
	raytangent=GetNormalised(&raytangent) ;
	tangentEndA0=VectorSubtract(&tangentEndA0, &backshift) ;
	Vert_A0.position[0]=tangentEndA0.position[0] ;
	Vert_A0.position[1]=tangentEndA0.position[1] ;
	Vert_A0.position[2]=tangentEndA0.position[2] ;
	Vert_A0.normal[0]= -raytangent.position[0] ;
	Vert_A0.normal[1]= -raytangent.position[1] ;
	Vert_A0.normal[2]= -raytangent.position[2] ;
	Vert_A0.texcoord[0][0]=flCentreX ;
	Vert_A0.texcoord[0][1]=flCentreY ;
	Vert_A0.texcoord[1][0]=flCentreZ ;
	Vert_A0.texcoord[1][1]=flBrightness ;
	Vert_A0.color[0]=(unsigned char)flColR ;//abs(Vert_A0.normal[0])*255 ;//0.0f ;
	Vert_A0.color[1]=(unsigned char)flColG ;//abs(Vert_A0.normal[1])*255 ;//0.0f ;
	Vert_A0.color[2]=(unsigned char)flColB ;//abs(Vert_A0.normal[2])*255 ;//0.0f ;
	if(!AddVertex(Vert_A0)) return 0 ;
	tangentStart=start ;
	tangentEnd=VectorAdd(&end, &tangentA) ;
	ray=VectorSubtract(&tangentEnd, &tangentStart) ;
	ray=GetNormalised(&ray) ;
	VertexScale(&ray, flCutoff) ; //ray.getScaledBy(cutoff) ;
	tangentStart=start ;
	tangentEndA1=VectorAdd(&start, &ray) ;
	raytangent=VectorSubtract(&end, &tangentEndA1) ;
	raytangent=VectorAdd(&raytangent, &adjust) ;
	raytangent=GetNormalised(&raytangent) ;
	tangentEndA1=VectorSubtract(&tangentEndA1, &backshift) ;
	Vert_A1.position[0]=tangentEndA1.position[0] ;
	Vert_A1.position[1]=tangentEndA1.position[1] ;
	Vert_A1.position[2]=tangentEndA1.position[2] ;
	Vert_A1.normal[0]= -raytangent.position[0] ;
	Vert_A1.normal[1]= -raytangent.position[1] ;
	Vert_A1.normal[2]= -raytangent.position[2] ;
	Vert_A1.texcoord[0][0]=flCentreX ;
	Vert_A1.texcoord[0][1]=flCentreY ;
	Vert_A1.texcoord[1][0]=flCentreZ ;
	Vert_A1.texcoord[1][1]=flBrightness ;
	Vert_A1.color[0]=(unsigned char)flColR ;//abs(Vert_A1.normal[0])*255 ;//0.0f ;
	Vert_A1.color[1]=(unsigned char)flColG ;//abs(Vert_A1.normal[1])*255 ;//0.0f ;
	Vert_A1.color[2]=(unsigned char)flColB ;//abs(Vert_A1.normal[2])*255 ;//0.0f ;
	if(!AddVertex(Vert_A1)) return 0 ;
	//////////////////////////////////////////////////////////////////////
	tangentB=GetNormalised(&tangentB) ;
	VertexScale(&tangentB, adjacent) ; //tangentB.getScaledBy(adjacent) ;
	tangentStart=start ;
	tangentEnd=VectorSubtract(&end, &tangentB) ;
	ray=VectorSubtract(&tangentEnd, &tangentStart) ;
	ray=GetNormalised(&ray) ;
	VertexScale(&ray, flCutoff) ; //ray.getScaledBy(cutoff) ;
	tangentStart=start ;
	tangentEndB0=VectorAdd(&start, &ray) ;
	raytangent=VectorSubtract(&end, &tangentEndB0) ;
	raytangent=VectorAdd(&raytangent, &adjust) ;
	raytangent=GetNormalised(&raytangent) ;
	tangentEndB0=VectorSubtract(&tangentEndB0, &backshift) ;
	Vert_B0.position[0]=tangentEndB0.position[0] ;
	Vert_B0.position[1]=tangentEndB0.position[1] ;
	Vert_B0.position[2]=tangentEndB0.position[2] ;
	Vert_B0.normal[0]= -raytangent.position[0] ;
	Vert_B0.normal[1]= -raytangent.position[1] ;
	Vert_B0.normal[2]= -raytangent.position[2] ;
	Vert_B0.texcoord[0][0]=flCentreX ;
	Vert_B0.texcoord[0][1]=flCentreY ;
	Vert_B0.texcoord[1][0]=flCentreZ ;
	Vert_B0.texcoord[1][1]=flBrightness ;
	Vert_B0.color[0]=(unsigned char)flColR ;//abs(Vert_B0.normal[0])*255 ;//0.0f ;
	Vert_B0.color[1]=(unsigned char)flColG ;//abs(Vert_B0.normal[1])*255 ;//0.0f ;
	Vert_B0.color[2]=(unsigned char)flColB ;//abs(Vert_B0.normal[2])*255 ;//0.0f ;
	if(!AddVertex(Vert_B0)) return 0 ;
	tangentStart=start ;
	tangentEnd=VectorAdd(&end, &tangentB) ;
	ray=VectorSubtract(&tangentEnd, &tangentStart) ;
	ray=GetNormalised(&ray) ;
	VertexScale(&ray, flCutoff) ; //ray.getScaledBy(cutoff) ;
	tangentStart=start ;
	tangentEndB1=VectorAdd(&start, &ray) ;
	raytangent=VectorSubtract(&end, &tangentEndB1) ;
	raytangent=VectorAdd(&raytangent, &adjust) ;
	raytangent=GetNormalised(&raytangent) ;
	tangentEndB1=VectorSubtract(&tangentEndB1, &backshift) ;
	Vert_B1.position[0]=tangentEndB1.position[0] ;
	Vert_B1.position[1]=tangentEndB1.position[1] ;
	Vert_B1.position[2]=tangentEndB1.position[2] ;
	Vert_B1.normal[0]= -raytangent.position[0] ;
	Vert_B1.normal[1]= -raytangent.position[1] ;
	Vert_B1.normal[2]= -raytangent.position[2] ;
	Vert_B1.texcoord[0][0]=flCentreX ;
	Vert_B1.texcoord[0][1]=flCentreY ;
	Vert_B1.texcoord[1][0]=flCentreZ ;
	Vert_B1.texcoord[1][1]=flBrightness ;
	Vert_B1.color[0]=(unsigned char)flColR ;//abs(Vert_B1.normal[0])*255 ;//0.0f ;
	Vert_B1.color[1]=(unsigned char)flColG ;//abs(Vert_B1.normal[1])*255 ;//0.0f ;
	Vert_B1.color[2]=(unsigned char)flColB ;//abs(Vert_B1.normal[2])*255 ;//0.0f ;
	if(!AddVertex(Vert_B1)) return 0 ;
	/////////////////////////////////////////////////////////////////////
	// the four verts are position correctly to make the large end of a cone (or rather, pyramid) that would be consistent
	// with the angle and their distance from the origin.  However we cheat and move them back on top of the origin
	// so that map lights come out looking better.
	
	/*
	SetVertex(&normal, m_pLight[nLight].Direction[0]*flCutoff, m_pLight[nLight].Direction[1]*flCutoff, m_pLight[nLight].Direction[2]*flCutoff) ;
	tangentEndA0=VectorSubtract(&tangentEndA0, &normal) ;
	Vert_A0.position[0]=tangentEndA0.position[0] ;
	Vert_A0.position[1]=tangentEndA0.position[1] ;
	Vert_A0.position[2]=tangentEndA0.position[2] ;
	tangentEndA1=VectorSubtract(&tangentEndA1, &normal) ;
	Vert_A1.position[0]=tangentEndA1.position[0] ;
	Vert_A1.position[1]=tangentEndA1.position[1] ;
	Vert_A1.position[2]=tangentEndA1.position[2] ;
	tangentEndB0=VectorSubtract(&tangentEndB0, &normal) ;
	Vert_A0.position[0]=tangentEndB0.position[0] ;
	Vert_A0.position[1]=tangentEndB0.position[1] ;
	Vert_A0.position[2]=tangentEndB0.position[2] ;
	tangentEndB1=VectorSubtract(&tangentEndB1, &normal) ;
	Vert_A1.position[0]=tangentEndB1.position[0] ;
	Vert_A1.position[1]=tangentEndB1.position[1] ;
	Vert_A1.position[2]=tangentEndB1.position[2] ;
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		Triangle.Texture=m_nDefaultTextureIndexGlowLight ;
		Triangle.Lamp=-1 ;
		Triangle.Group=m_nGroup ;
		/////////////////////////////////////
		Triangle.VIndex[0]=n_Or ;
		Triangle.VIndex[2]=n_A0 ;
		Triangle.VIndex[1]=n_B1 ;
		if(!AddTriangle(Triangle)) return 0 ;
		Triangle.VIndex[0]=n_Or ;
		Triangle.VIndex[2]=n_B1 ;
		Triangle.VIndex[1]=n_A1 ;
		if(!AddTriangle(Triangle)) return 0 ;
		Triangle.VIndex[0]=n_Or ;
		Triangle.VIndex[2]=n_A1 ;
		Triangle.VIndex[1]=n_B0 ;
		if(!AddTriangle(Triangle)) return 0 ;
		
		Triangle.VIndex[0]=n_Or ;
		Triangle.VIndex[2]=n_B0 ;
		Triangle.VIndex[1]=n_A0 ;
		if(!AddTriangle(Triangle)) return 0 ;
		m_nGroup++ ; // increment group once for each glow
		
	}
	
	return 1 ;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// some vertex manipulation functions used for setting up the light disk.
// They only work on the position, so other parts of the struct need to be setup seperately
void Q3Map::SetVertex(Q3BspVertex *pVert, float flXPos, float flYPos, float flZPos)
{
	pVert->position[0]=flXPos ;
	pVert->position[1]=flYPos ;
	pVert->position[2]=flZPos ;
}
// this doesn't test exact equivalence, but rather within the range of an epsilon VERYSMALL
bool Q3Map::VectorsAreEqual(Q3BspVertex* pVecA, Q3BspVertex* pVecB)
{
	if( fabs(pVecA->position[0] - pVecB->position[0])>VERYSMALL ) return false ;
	if( fabs(pVecA->position[1] - pVecB->position[1])>VERYSMALL ) return false ;
	if( fabs(pVecA->position[2] - pVecB->position[2])>VERYSMALL ) return false ;
	return true ;
}
// VertA is the origin of VertB and VertC
Q3BspVertex Q3Map::NormalizedCrossProduct(Q3BspVertex VertA, Q3BspVertex VertB, Q3BspVertex VertC)
{
	Q3BspVertex Cross ;
	// edge vectors
	float flVecXA_CP = VertA.position[0] - VertB.position[0] ;
	float flVecYA_CP = VertA.position[1] - VertB.position[1] ;
	float flVecZA_CP = VertA.position[2] - VertB.position[2] ;
	float flVecXB_CP = VertA.position[0] - VertC.position[0] ;
	float flVecYB_CP = VertA.position[1] - VertC.position[1] ;
	float flVecZB_CP = VertA.position[2] - VertC.position[2] ;
	// cross product
	float flCpx_CP = (flVecZA_CP * flVecYB_CP) - (flVecYA_CP * flVecZB_CP);
	float flCpy_CP = (flVecXA_CP * flVecZB_CP) - (flVecZA_CP * flVecXB_CP);
	float flCpz_CP = (flVecYA_CP * flVecXB_CP) - (flVecXA_CP * flVecYB_CP);
	// Normalize 
	float flR_CP = sqrt(flCpx_CP * flCpx_CP + flCpy_CP * flCpy_CP + flCpz_CP * flCpz_CP);
	Cross.position[0] = flCpx_CP / flR_CP;
	Cross.position[1] = flCpy_CP / flR_CP;
	Cross.position[2] = flCpz_CP / flR_CP;
	return Cross ;
}
float Q3Map::VertexDistance(Q3BspVertex* VertA, Q3BspVertex* VertB)
{
	float flXDis=VertA->position[0]-VertB->position[0] ;
	float flYDis=VertA->position[1]-VertB->position[1] ;
	float flZDis=VertA->position[2]-VertB->position[2] ;
	return sqrt(flXDis*flXDis+flYDis*flYDis+flZDis*flZDis) ;
}
void Q3Map::VertexScale(Q3BspVertex* pVert, float flScale)
{
	pVert->position[0]*=flScale ;
	pVert->position[1]*=flScale ;
	pVert->position[2]*=flScale ;
}
Q3BspVertex Q3Map::GetNormalised(Q3BspVertex* pVector)
{
	float flLength=sqrt((pVector->position[0]*pVector->position[0])+(pVector->position[1]*pVector->position[1])+(pVector->position[2]*pVector->position[2])) ;
	Q3BspVertex Vector=*pVector ;
	Vector.position[0]/=flLength ;
	Vector.position[1]/=flLength ;
	Vector.position[2]/=flLength ;
	return Vector ;
}
Q3BspVertex Q3Map::VectorAdd(Q3BspVertex* pVecA, Q3BspVertex* pVecB)
{
	Q3BspVertex Vector ;
	Vector.position[0]=pVecA->position[0] + pVecB->position[0] ;
	Vector.position[1]=pVecA->position[1] + pVecB->position[1] ;
	Vector.position[2]=pVecA->position[2] + pVecB->position[2] ;
	return Vector ;
}
Q3BspVertex Q3Map::VectorSubtract(Q3BspVertex* pVecA, Q3BspVertex* pVecB)
{
	Q3BspVertex Vector ;
	Vector.position[0]=pVecA->position[0] - pVecB->position[0] ;
	Vector.position[1]=pVecA->position[1] - pVecB->position[1] ;
	Vector.position[2]=pVecA->position[2] - pVecB->position[2] ;
	return Vector ;
}
Q3BspVertex Q3Map::VectorMultiply(Q3BspVertex* pVecA, Q3BspVertex* pVecB)
{
	Q3BspVertex Vector ;
	Vector.position[0]=pVecA->position[0] * pVecB->position[0] ;
	Vector.position[1]=pVecA->position[1] * pVecB->position[1] ;
	Vector.position[2]=pVecA->position[2] * pVecB->position[2] ;
	return Vector ;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Q3Map::GetTexLampTextureNumbers()
{
	m_nBZN_LightNode0=-1 ;
	m_nBZN_LightNode1=-1 ;
	m_nBZN_LightNode2=-1 ;
	m_nBZN_LightNode3=-1 ;
	int nTex=0;
	char chTexName[1024] ;
	int nLen=0 ;
	int nPos=0 ;
	int nMatch=0 ;
	strcpy(chTexName, "textures/common/bzn_lightnode") ;
	nLen=strlen(chTexName) ;
	for(nTex=0 ; nTex=0 ; nPos--) // faster to check backwards since lots of textures will start with "textures/common/"
			if(m_pTexture[nTex].name[nPos]!=chTexName[nPos])
			{
				nMatch=0 ;
				break ;
			}
		if(nMatch)
		{
			// what is the last character?
			if(m_pTexture[nTex].name[nLen]=='0')
				m_nBZN_LightNode0=nTex ;
			else
			if(m_pTexture[nTex].name[nLen]=='1')
				m_nBZN_LightNode1=nTex ;
			else
			if(m_pTexture[nTex].name[nLen]=='2')
				m_nBZN_LightNode2=nTex ;
			else
			if(m_pTexture[nTex].name[nLen]=='3')
				m_nBZN_LightNode3=nTex ;
			
		}// end if match
	}// end for nTex
}
int Q3Map::SortTrianglesIntoGroups(void)
{
	int nNewSize=0 ; // we will drop non-subzoned (-1 zone) triangles here, so new number of triangles may be less than old number.
	ULONGLONG *pFaceOrder = new ULONGLONG[m_nTriangleMax] ;
	if(pFaceOrder==NULL) return 0 ; // out of memory.
	// temporary copy of m_pTriangles to make sorting easier
	triangle_t *TempTriangle = new triangle_t[m_nTriangleMax];
	if(TempTriangle==NULL) { DELETE_ARRAY( pFaceOrder ) ; return 0 ; } // out of memory
	memcpy((void*)TempTriangle, (void*)m_pTriangle, m_nTriangleMax*sizeof(triangle_t)) ;
	// create the initial face "value" by setting the most significant bits to it's criteria number
	for (int i=0; i < m_nTriangleMax; i++)
		if(m_pTriangle[i].Zone!=-1) // drop unsubzoned triangles
			pFaceOrder[nNewSize++] = ( ((ULONGLONG)m_pTriangle[i].Zone)<=FACESORT_GROUP_LIMIT) return 0 ; // too many groups in a zone.
			}
			nGroup=m_pTriangle[nTri].Group ;
		}
		if(m_pTransTexture[ m_pTriangle[nTri].Texture ]==0)
			m_pTriangle[nTri].Group=0 ;
		else
			m_pTriangle[nTri].Group=nNewGroup ;
	}// end for tri
	return 1 ;
}
// static function for sorting groups, required by qsort
int Q3Map::compareGroups( const void *arg1, const void *arg2 )
{
	ULONGLONG FaceA= *(ULONGLONG*)arg1 ;
	ULONGLONG FaceB= *(ULONGLONG*)arg2 ;
	if(FaceA < FaceB)
		return -1 ;
	else
		if(FaceA > FaceB)
			return 1 ;
		
	return 0 ;
}
// sort faces so that we can batch effectively when constructing our manualobjects
// Currently grouped according to zone and texture.
// Triangles not in any subzone will be dropped at this stage.
int Q3Map::SortTrianglesIntoBatches(void)
{
	int nNewSize=0 ; // we will drop non-subzoned (-1 zone) triangles here, so new number of triangles may be less than old number.
	ULONGLONG *pFaceOrder = new ULONGLONG[m_nTriangleMax] ;
	if(pFaceOrder==NULL) return 0 ; // out of memory.
	// temporary copy of m_pTriangles to make sorting easier
	triangle_t *TempTriangle = new triangle_t[m_nTriangleMax];
	if(TempTriangle==NULL) { DELETE_ARRAY( pFaceOrder ) ; return 0 ; } // out of memory
	memcpy((void*)TempTriangle, (void*)m_pTriangle, m_nTriangleMax*sizeof(triangle_t)) ;
	// create the initial face "value" by setting the most significant bits to it's criteria number
	for (int i=0; i < m_nTriangleMax; i++)
		if(m_pTriangle[i].Zone!=-1) // drop unsubzoned triangles
			pFaceOrder[nNewSize++] = ( ((ULONGLONG)m_pTriangle[i].Zone)< FaceB)
			return 1 ;
		
	return 0 ;
}
// note which texture numbers correspond to textures that have transparency.
// needed for when we work out transparency groups and related stuff
int Q3Map::SetupTransTextures(void)
{
	int nMaterial=0 ;
	int nPos=0 ;
	char chMaterial[1024] ;
	// create the memory for the transtextures
	m_pTransTexture = new int[m_nTextureMax] ;
	if(m_pTransTexture==NULL) return 0 ; // out of memory.
	for(nMaterial=0 ; nMaterialm_pPortals[nPortal].Min[0])
					&& (flMinYm_pPortals[nPortal].Min[1])
					&& (flMinZm_pPortals[nPortal].Min[2])
				)
			{
				// add this portal to the zone's portal list
				nIndex=m_nZoneTouchesPortal[nZone][INDEX_PORTALCOUNT] ;
				if(nIndex=flMinX) && (m_pLight[nLight].Position[0]<=flMaxX)
					&& (m_pLight[nLight].Position[1]>=flMinY) && (m_pLight[nLight].Position[1]<=flMaxY)
					&& (m_pLight[nLight].Position[2]>=flMinZ) && (m_pLight[nLight].Position[2]<=flMaxZ)
				)
			{
				// add this light to the zone's light list
				nIndex=m_nZoneContainsLightCentre[nZone][INDEX_LIGHTCOUNT] ;
				if(nIndexm_pLight[nLight].Min[0])
					&& (flMinYm_pLight[nLight].Min[1])
					&& (flMinZm_pLight[nLight].Min[2])
				)
			{
				// add this light to the zone's light list
				nIndex=m_nZoneTouchesSubLight[nZone][INDEX_LIGHTCOUNT] ;
				if(nIndex1) && (m_nMaxMultiZoneLightm_ZoneBoundary[nZone].Max[0] ? m_ZoneBoundary[nZone].Max[0] : m_pLight[nLight].Max[0] ; 
			flMaxY= m_pLight[nLight].Max[1]>m_ZoneBoundary[nZone].Max[1] ? m_ZoneBoundary[nZone].Max[1] : m_pLight[nLight].Max[1] ; 
			flMaxZ= m_pLight[nLight].Max[2]>m_ZoneBoundary[nZone].Max[2] ? m_ZoneBoundary[nZone].Max[2] : m_pLight[nLight].Max[2] ; 
			// add the cut down light as a sublight
			m_SubLight[m_nSubLightMax].Light=nLight ;
			m_SubLight[m_nSubLightMax].Zone=nZone ;
			m_SubLight[m_nSubLightMax].Min[0]=flMinX ;
			m_SubLight[m_nSubLightMax].Min[1]=flMinY ;
			m_SubLight[m_nSubLightMax].Min[2]=flMinZ ;
			m_SubLight[m_nSubLightMax].Max[0]=flMaxX ;
			m_SubLight[m_nSubLightMax].Max[1]=flMaxY ;
			m_SubLight[m_nSubLightMax].Max[2]=flMaxZ ;
			// remember which sublight is the centre
			if(
						 (m_pLight[nLight].Position[0]>=flMinX) && (m_pLight[nLight].Position[0]<=flMaxX)
					&& (m_pLight[nLight].Position[1]>=flMinY) && (m_pLight[nLight].Position[1]<=flMaxY)
					&& (m_pLight[nLight].Position[2]>=flMinZ) && (m_pLight[nLight].Position[2]<=flMaxZ)
				)
				nSubLightCentre=m_nSubLightMax ;
					
			m_nSubLightMax++ ; // we don't have to worry about bound checking this, because we've already checked there aren't too many lights.
		}// end for zoneindex			
		
		// move the sublight that contains the centre to the beginning of the sublights for this light.
		// We always want the first sublight to contain the centre to make the culling algos work better.
		TempSubLight=m_SubLight[ m_pLight[nLight].SubLightStart ] ;
		m_SubLight[ m_pLight[nLight].SubLightStart ] = m_SubLight[ nSubLightCentre ] ;
		m_SubLight[ nSubLightCentre ] = TempSubLight ;
		
	}// end for light
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//
	// recalculate m_nZoneTouchesSubLight using the newly created sublights 
	// (instead of the complete lights that were originally used)
	//
	int nSubLight=0 ;
	// clear the light settings.
	for(nZone=0 ; nZonem_SubLight[nSubLight].Min[0])
					&& (flMinYm_SubLight[nSubLight].Min[1])
					&& (flMinZm_SubLight[nSubLight].Min[2])
				)
			{
				// add this light to the zone's light list
				nIndex=m_nZoneTouchesSubLight[nZone][INDEX_LIGHTCOUNT] ;
				if(nIndexm_pLight[nLight].Min[0])
					&& (flMinYm_pLight[nLight].Min[1])
					&& (flMinZm_pLight[nLight].Min[2])
				)
			{
				// add this light to the portal's light list
				nIndex=m_nPortalTouchesLight[nPortal][INDEX_PORTALLIGHTCOUNT] ;
				if(nIndex=MAX_ZONEPERZONE) 
						nMaxTouchZone=MAX_ZONEPERZONE-1 ;
					
				}
			}// end for portal zone index
		}// end for portal index
		
		// set the maximum
		m_nZoneTouchesZone[nCentralZone][INDEX_ZONEPERZONECOUNT]=nMaxTouchZone ;
		/*
		sprintf(m_chBug, "CentralZone %i, TouchedZoneCount %i", nCentralZone, m_nZoneTouchesPortal[nCentralZone][INDEX_ZONEPERZONECOUNT]) ;
		Q3Bug.LogAddCR(m_chBug) ;
		for(nTouchZoneIndex=0 ; nTouchZoneIndex