/*
===========================================================================
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