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