/*============================================================================= Name : btg.c Purpose : BTG - Background Tool of the Geeks This file contains everything (?) necessary to load and render the BTG backgrounds Created 4/27/1998 by khent Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #ifndef SW_Render #include #endif #include #include #include #include "btg.h" #include "memory.h" #include "vector.h" #include "matrix.h" #include "glinc.h" #include "color.h" #include "universe.h" #include "file.h" #include "render.h" #include "clipper.h" #include "main.h" #include "glcaps.h" #include "mainrgn.h" #include "texreg.h" // ----- // data // ----- #define M_PI_F 3.1415926535f static ubyte lastbg[4] = {255,255,255,0}; char btgLastBackground[128] = ""; static real32 btgThetaOffset; static real32 btgPhiOffset; static GLfloat _bgColor[4]; static GLubyte _bgByte[4]; static sdword btgFade = 255; typedef struct btgTransVertex { GLubyte red, green, blue, alpha; vector position; } btgTransVertex; typedef struct btgTransStar { vector ul, ll, lr, ur; vector mid; } btgTransStar; static btgHeader* btgHead = NULL; static btgVertex* btgVerts = NULL; static btgStar* btgStars = NULL; static btgPolygon* btgPolys = NULL; static btgTransVertex* btgTransVerts = NULL; static btgTransStar* btgTransStars = NULL; static udword* btgIndices = NULL; typedef struct starTex { char filename[48]; udword glhandle; sdword width, height; struct starTex* next; } starTex; static starTex* texList = NULL; typedef struct tagTGAFileHeader { unsigned char idLength; unsigned char colorMapType; unsigned char imageType; // Color Map information. unsigned short colorMapStartIndex; unsigned short colorMapNumEntries; unsigned char colorMapBitsPerEntry; // Image Map information. signed short imageOffsetX; signed short imageOffsetY; unsigned short imageWidth; unsigned short imageHeight; unsigned char pixelDepth; unsigned char imageDescriptor; } TGAFileHeader; // ----- // code // ----- #define fsin(A) (real32)sin((real64)(A)) #define fcos(A) (real32)cos((real64)(A)) /*----------------------------------------------------------------------------- Name : btgStartup Description : initialize the btg subsystem Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void btgStartup() { btgReset(); } /*----------------------------------------------------------------------------- Name : btgReset Description : reset the btg subsystem Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void btgReset() { sdword numStars; lastbg[0] = 255; lastbg[1] = 255; lastbg[2] = 255; lastbg[3] = 0; btgFade = 255; btgThetaOffset = 0.0f; btgPhiOffset = 0.0f; universe.backgroundColor = 0; rndSetClearColor(colRGBA(0,0,0,255)); if (texList != NULL) { starTex* ptex = texList; starTex* temptex; while (ptex != NULL) { if (ptex->glhandle != 0) { glDeleteTextures(1, &ptex->glhandle); } temptex = ptex; ptex = ptex->next; memFree(temptex); } texList = NULL; } if (btgHead != NULL) { numStars = btgHead->numStars; memFree(btgHead); btgHead = NULL; } else { numStars = 0; } if (btgVerts != NULL) { memFree(btgVerts); btgVerts = NULL; } if (btgStars != NULL) { memFree(btgStars); btgStars = NULL; } if (btgPolys != NULL) { memFree(btgPolys); btgPolys = NULL; } if (btgTransVerts != NULL) { memFree(btgTransVerts); btgTransVerts = NULL; } if (btgTransStars != NULL) { memFree(btgTransStars); btgTransStars = NULL; } if (btgIndices != NULL) { memFree(btgIndices); btgIndices = NULL; } } /*----------------------------------------------------------------------------- Name : btgShutdown Description : shut down the btg subsystem Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void btgShutdown() { btgReset(); } /*----------------------------------------------------------------------------- Name : btgSetTheta/btgGetTheta/ Description : set/get the x theta offset of the entire background Inputs : theta - amount, in degrees, to offset Outputs : Return : ----------------------------------------------------------------------------*/ void btgSetTheta(real32 theta) { btgThetaOffset = -theta; } real32 btgGetTheta(void) { return(-btgThetaOffset); } /*----------------------------------------------------------------------------- Name : btgSetPhi/btgGetPhi Description : set/get the y phi offset of the entire background Inputs : phi - amount, in degrees, to offset Outputs : Return : ----------------------------------------------------------------------------*/ void btgSetPhi(real32 phi) { btgPhiOffset = -phi; } real32 btgGetPhi(void) { return(-btgPhiOffset); } /*----------------------------------------------------------------------------- Name : btgGetTexture Description : creates a GL texture object with the contents of the given .TGA file Inputs : filename - the name of the bitmap, in btg\bitmaps thandle - place where the texture name goes width, height - dimensions of the bitmap Outputs : thandle, width, height are modified Return : ----------------------------------------------------------------------------*/ void btgGetTexture(char* filename, udword* thandle, sdword* width, sdword* height) { char fullname[64]; ubyte* data; ubyte* pdata; ubyte* bp; unsigned short* psdata; TGAFileHeader head; sdword i; strcpy(fullname, "btg\\bitmaps\\"); strcat(fullname, filename); if (fileExists(fullname, 0)) { fileLoadAlloc(fullname, (void**)&data, 0); //this pointer wackiness gets around alignment strangeness pdata = data; head.idLength = *pdata++; head.colorMapType = *pdata++; head.imageType = *pdata++; psdata = (unsigned short*)pdata; head.colorMapStartIndex = *psdata; pdata += 2; psdata = (unsigned short*)pdata; head.colorMapNumEntries = *psdata; pdata += 2; head.colorMapBitsPerEntry = *pdata++; psdata = (unsigned short*)pdata; head.imageOffsetX = (signed short)*psdata; pdata += 2; psdata = (unsigned short*)pdata; head.imageOffsetY = (signed short)*psdata; pdata += 2; psdata = (unsigned short*)pdata; head.imageWidth = *psdata; pdata += 2; psdata = (unsigned short*)pdata; head.imageHeight = *psdata; pdata += 2; head.pixelDepth = *pdata++; head.imageDescriptor = *pdata++; //only 32bit TGAs dbgAssert(head.pixelDepth == 32); pdata += head.idLength; *width = (sdword)head.imageWidth; *height = (sdword)head.imageHeight; //convert to GL_RGBA for (i = 0, bp = pdata; i < 4*(*width)*(*height); i += 4, bp += 4) { ubyte r, b; r = bp[0]; b = bp[2]; bp[0] = b; bp[2] = r; bp[3] = (ubyte)(255 - (int)bp[3]); } //create the GL texture object glGenTextures(1, thandle); glBindTexture(GL_TEXTURE_2D, *thandle); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, *width, *height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pdata); memFree(data); } else { *thandle = 0; *width = 0; *height = 0; } } /*----------------------------------------------------------------------------- Name : btgTexInList Description : determines whether a given bitmap has already been loaded Inputs : filename - the name of the bitmap Outputs : Return : TRUE or FALSE (in or not in list) ----------------------------------------------------------------------------*/ bool btgTexInList(char* filename) { starTex* ptex; ptex = texList; while (ptex != NULL) { if (!strcmp(filename, ptex->filename)) { return TRUE; } ptex = ptex->next; } return FALSE; } /*----------------------------------------------------------------------------- Name : btgFindTexture Description : searches the star bitmap list to find a GL texture object Inputs : filename - name of the bitmap star - btg star structure to fill in Outputs : star is modified accordingly Return : ----------------------------------------------------------------------------*/ void btgFindTexture(char* filename, btgStar* star) { starTex* ptex; ptex = texList; while (ptex != NULL) { if (!strcmp(filename, ptex->filename)) { star->glhandle = ptex->glhandle; star->width = ptex->width; star->height = ptex->height; return; } ptex = ptex->next; } star->glhandle = 0; star->width = 0; star->height = 0; } /*----------------------------------------------------------------------------- Name : btgAddTexToList Description : adds a starTex struct for a supplied gl texture name Inputs : filename - name of the bitmap glhandle - handle of the GL's texture object width, height - dimensions of the bitmap Outputs : texList is readjusted to contain a newly allocated starTex struct Return : ----------------------------------------------------------------------------*/ void btgAddTexToList(char *filename, udword glhandle, sdword width, sdword height) { starTex* newtex; newtex = (starTex*)memAlloc(sizeof(starTex), "btg star bitmap", 0); newtex->glhandle = glhandle; newtex->width = width; newtex->height = height; strcpy(newtex->filename, filename); newtex->next = NULL; if (texList == NULL) { //first bitmap loaded texList = newtex; } else { //other bitmaps exist newtex->next = texList; texList = newtex; } } /*----------------------------------------------------------------------------- Name : btgCloseTextures Description : free texture handles Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void btgCloseTextures(void) { sdword i; btgStar* pStar; if (btgStars != NULL) { for (i = 0, pStar = btgStars; i < btgHead->numStars; i++, pStar++) { if (pStar->glhandle != 0) { glDeleteTextures(1, &pStar->glhandle); } } } } /*----------------------------------------------------------------------------- Name : btgLoadTextures Description : reload the background Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void btgLoadTextures(void) { if (btgLastBackground[0] != '\0') { btgLoad(btgLastBackground); btgFade = -1; } } /*----------------------------------------------------------------------------- Name : btgLoad Description : load a btg scene into our structures Inputs : filename - the name of the btg file to load Outputs : Return : ----------------------------------------------------------------------------*/ void btgLoad(char* filename) { ubyte* btgData; udword headSize; udword vertSize; udword starSize, fileStarSize; udword polySize; real32 thetaSave, phiSave; fileLoadAlloc(filename, (void**)&btgData, 0); memStrncpy(btgLastBackground, filename, 127); //reset / free any previous structures thetaSave = btgThetaOffset; phiSave = btgPhiOffset; btgReset(); btgThetaOffset = thetaSave; btgPhiOffset = phiSave; //header. trivial copy headSize = sizeof(btgHeader); btgHead = (btgHeader*)memAlloc(headSize, "btg header", 0); memcpy(btgHead, btgData, headSize); //set background colour universe.backgroundColor = colRGB(btgHead->mBGRed, btgHead->mBGGreen, btgHead->mBGBlue); #if 0 // set at end of gameStart to prevent color problems rndSetClearColor(colRGBA(btgHead->mBGRed, btgHead->mBGGreen, btgHead->mBGBlue, 255)); #endif //version check dbgAssert(btgHead->btgFileVersion == BTG_FILE_VERSION); //vertices. trivial copy vertSize = btgHead->numVerts * sizeof(btgVertex); if (vertSize) { btgVerts = (btgVertex*)memAlloc(vertSize, "btg verts", 0); memcpy(btgVerts, btgData + headSize, vertSize); } //stars. non-trivial munging around starSize = btgHead->numStars * sizeof(btgStar); fileStarSize = 0; if (starSize != 0) { btgStar* outstarp; ubyte* instarp; udword* udp; sdword i, j, tempSize, count, length; char filename[48]; btgStars = (btgStar*)memAlloc(starSize, "btg stars", 0); instarp = btgData + headSize + vertSize; outstarp = btgStars; for (i = 0; i < btgHead->numStars; i++, outstarp++) { //extract constant-sized header tempSize = sizeof(udword) + 2*sizeof(real64) + 4*sizeof(sdword); memcpy(outstarp, instarp, tempSize); instarp += tempSize; fileStarSize += tempSize; //extract variable-sized filename count = 0; memset(filename, 0, 48); udp = (udword*)instarp; length = (sdword)*udp; instarp += 4; fileStarSize += 4; for (j = 0; j < length; j++) { filename[count++] = *instarp++; fileStarSize++; } memcpy(outstarp->filename, filename, 48); //create a GL texture object if (!btgTexInList(filename)) { btgGetTexture(filename, &outstarp->glhandle, &outstarp->width, &outstarp->height); btgAddTexToList(filename, outstarp->glhandle, outstarp->width, outstarp->height); } else { btgFindTexture(filename, outstarp); } } } //reset the game's current texture, which now differs from the GL's trClearCurrent(); //polys. trivial copy polySize = btgHead->numPolys * sizeof(btgPolygon); if (polySize != 0) { btgPolys = (btgPolygon*)memAlloc(polySize, "btg polys", 0); memcpy(btgPolys, btgData + headSize + vertSize + fileStarSize, polySize); } memFree(btgData); btgIndices = (udword*)memAlloc(3 * btgHead->numPolys * sizeof(udword), "btg indices", NonVolatile); //spherically project things, blend colours, &c btgConvertVerts(); } /*----------------------------------------------------------------------------- Name : btgSortCompare Description : callback from qsort to sort btg vertices by y coordinate Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ int btgSortCompare(const void* p0, const void* p1) { btgVertex* va; btgVertex* vb; va = btgVerts + *((sdword*)p0); vb = btgVerts + *((sdword*)p1); if (va->y > vb->y) { return 1; } else if (va->y < vb->y) { return -1; } else { return 0; } } /*----------------------------------------------------------------------------- Name : btgSortByY Description : sorts a list of vertex indices by y coordinate Inputs : listToSort - the list of indices to sort n - number of elements Outputs : listToSort is sorted Return : ----------------------------------------------------------------------------*/ void btgSortByY(sdword* listToSort, sdword n) { qsort(listToSort, n, sizeof(sdword), btgSortCompare); } /*----------------------------------------------------------------------------- Name : btgZip Description : fixup seaming Inputs : Outputs : btgPolys are adjusted to eliminate seaming Return : ----------------------------------------------------------------------------*/ void btgZip(void) { sdword i, iPoly; sdword numLeft, numRight; sdword lefts[64], rights[64]; btgVertex* vert; btgPolygon* poly; //make lists of left, right verts numLeft = numRight = 0; for (i = 0, vert = btgVerts; i < btgHead->numVerts; i++, vert++) { if (vert->x < 0.5) { lefts[numLeft++] = i; } else if (vert->x > (btgHead->pageWidth - 0.5)) { rights[numRight++] = i; } } //continue only if approximately equal number of edge verts if (abs(numLeft - numRight) > 1) { return; } //sort lists btgSortByY(lefts, numLeft); btgSortByY(rights, numRight); //minimum of edge verts if (numLeft < numRight) { numRight = numLeft; } else if (numRight < numLeft) { numLeft = numRight; } //remove right refs for (i = 0; i < numLeft; i++) { for (iPoly = 0, poly = btgPolys; iPoly < btgHead->numPolys; iPoly++, poly++) { if (poly->v0 == rights[i]) { poly->v0 = lefts[i]; } if (poly->v1 == rights[i]) { poly->v1 = lefts[i]; } if (poly->v2 == rights[i]) { poly->v2 = lefts[i]; } } } } /*----------------------------------------------------------------------------- Name : btgConvertAVert Description : spherically unprojects a vert Inputs : out - target output vector x, y - 2D position Outputs : out is modified Return : ----------------------------------------------------------------------------*/ void btgConvertAVert(vector* out, real64 x, real64 y) { real32 xFrac, yFrac; real32 theta, phi; real32 sinTheta, cosTheta; real32 sinPhi, cosPhi; real32 pageWidth, pageHeight; real32 radius; pageWidth = (real32)btgHead->pageWidth; pageHeight = (real32)btgHead->pageHeight; x = ((real64)pageWidth - 1.0) - x; xFrac = (real32)x / pageWidth; yFrac = (real32)y / pageHeight; theta = 2.0f * M_PI_F * xFrac; phi = 1.0f * M_PI_F * yFrac; theta += DEG_TO_RAD(btgThetaOffset) + DEG_TO_RAD(90.0f); phi += DEG_TO_RAD(btgPhiOffset); sinTheta = fsin(theta); cosTheta = fcos(theta); sinPhi = fsin(phi); cosPhi = fcos(phi); radius = CAMERA_CLIP_FAR - 500.0f; out->x = radius * cosTheta * sinPhi; out->y = radius * sinTheta * sinPhi; out->z = radius * cosPhi; } /*----------------------------------------------------------------------------- Name : btgConvertVert Description : do appropriate things to spherically unproject a vert Inputs : out - target output btgTransVert nVert - index of 2D btg vertex Outputs : out will contain the appropriate thing Return : ----------------------------------------------------------------------------*/ void btgConvertVert(btgTransVertex* out, udword nVert) { btgVertex* in; in = btgVerts + nVert; btgConvertAVert(&out->position, in->x, in->y); out->red = (GLubyte)((in->red * in->alpha) >> 8); out->green = (GLubyte)((in->green * in->alpha) >> 8); out->blue = (GLubyte)((in->blue * in->alpha) >> 8); out->alpha = 255; } /*----------------------------------------------------------------------------- Name : btgConvertStar Description : do appropriate things to spherically unproject a star Inputs : out - target output btgTransVert nVert - index of 2D btg star Outputs : out will contain the appropriate thing Return : ----------------------------------------------------------------------------*/ void btgConvertStar(btgTransStar* out, udword nVert) { btgStar* in; real64 x, y; real64 halfWidth, halfHeight; in = btgStars + nVert; halfWidth = (real64)in->width / 2.0; halfHeight = (real64)in->height / 2.0; btgConvertAVert(&out->mid, in->x, in->y); x = in->x - halfWidth; y = in->y - halfHeight; btgConvertAVert(&out->ul, x, y); x = in->x - halfWidth; y = in->y + halfHeight; btgConvertAVert(&out->ll, x, y); x = in->x + halfWidth; y = in->y + halfHeight; btgConvertAVert(&out->lr, x, y); x = in->x + halfWidth; y = in->y - halfHeight; btgConvertAVert(&out->ur, x, y); } /*----------------------------------------------------------------------------- Name : btgConvertVerts Description : transforms 2D BTG vertices into 3D Inputs : Outputs : btgTransVerts, btgTransStars are allocated and initialized Return : ----------------------------------------------------------------------------*/ void btgConvertVerts() { udword nVert, nPoly, index; btgTransVertex* pTransVert; btgTransStar* pTransStar; btgPolygon* pPoly; btgZip(); btgTransVerts = memAlloc(btgHead->numVerts * sizeof(btgTransVertex), "btg trans verts", NonVolatile); if (btgHead->numStars > 0) { btgTransStars = memAlloc(btgHead->numStars * sizeof(btgTransStar), "btg trans stars", NonVolatile); } else { btgTransStars = NULL; } for (nVert = 0, pTransVert = btgTransVerts; nVert < btgHead->numVerts; nVert++, pTransVert++) { btgConvertVert(pTransVert, nVert); } for (nVert = 0, pTransStar = btgTransStars; nVert < btgHead->numStars; nVert++, pTransStar++) { btgConvertStar(pTransStar, nVert); } for (nPoly = 0, index = 0, pPoly = btgPolys; nPoly < btgHead->numPolys; nPoly++, pPoly++, index += 3) { btgIndices[index + 0] = pPoly->v0; btgIndices[index + 1] = pPoly->v1; btgIndices[index + 2] = pPoly->v2; } } /*----------------------------------------------------------------------------- Name : btgBlendColour Description : blend a colour to the current background colour Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void btgBlendColour( GLubyte* dr, GLubyte* dg, GLubyte* db, sdword sr, sdword sg, sdword sb, sdword sa) { *dr = (GLubyte)((sr * sa) >> 8); *dg = (GLubyte)((sg * sa) >> 8); *db = (GLubyte)((sb * sa) >> 8); if (sa < 252) { if (*dr < _bgByte[0]) *dr = _bgByte[0]; if (*dg < _bgByte[1]) *dg = _bgByte[1]; if (*db < _bgByte[2]) *db = _bgByte[2]; } } /*----------------------------------------------------------------------------- Name : btgVertexColor Description : sets the GL's colour from a btg vertex Inputs : nVert - index of vertex in the big array fastBlends - TRUE if alpha blending is fast Outputs : Return : ----------------------------------------------------------------------------*/ void btgVertexColor(udword nVert, bool fastBlends) { if (fastBlends) { btgVertex* pVert; sdword alpha; pVert = btgVerts + nVert; alpha = (pVert->alpha * pVert->brightness) >> 8; glColor4ub((GLubyte)pVert->red, (GLubyte)pVert->green, (GLubyte)pVert->blue, (GLubyte)alpha); } else { btgVertex* pVert; GLubyte r, g, b, a; pVert = btgVerts + nVert; a = (pVert->alpha * pVert->brightness) >> 8; btgBlendColour(&r, &g, &b, pVert->red, pVert->green, pVert->blue, a); glColor3ub(r, g, b); } } /*----------------------------------------------------------------------------- Name : btgColorVertices Description : apply colouring to the interleaved color+vertex array Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void btgColorVertices(bool fastBlends) { btgVertex* pVert; btgTransVertex* transVert; sdword nVert; GLubyte r, g, b, a; for (nVert = 0, pVert = btgVerts, transVert = btgTransVerts; nVert < btgHead->numVerts; nVert++, pVert++, transVert++) { if (fastBlends) { a = (pVert->alpha * pVert->brightness * btgFade) >> 16; transVert->red = pVert->red; transVert->green = pVert->green; transVert->blue = pVert->blue; transVert->alpha = a; } else { a = (pVert->alpha * pVert->brightness * btgFade) >> 16; btgBlendColour(&r, &g, &b, pVert->red, pVert->green, pVert->blue, a); transVert->red = r; transVert->green = g; transVert->blue = b; transVert->alpha = 255; } } } /*----------------------------------------------------------------------------- Name : btgSetColourMultiplier Description : set colour multiplier to allow fading of the entire background Inputs : t - [0..1], the parameter of fade-ness, default 1.0 Outputs : btgFade global == t * 255 Return : ----------------------------------------------------------------------------*/ void btgSetColourMultiplier(real32 t) { // dbgAssert(t >= 0.0f && t <= 1.0f); btgFade = (sdword)(t * 255.0f); } /*----------------------------------------------------------------------------- Name : btgRender Description : renders the background. assumes modelview already set Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void btgRender() { udword nStar; sdword lightOn, index; GLboolean texOn, blendOn, depthOn; bool fastBlends, textureStars, compiledArrays; real32 modelview[16], projection[16]; udword* dlast; udword* dnext; extern bool gMosaic; static sdword lastFade = 255; if (btgHead == NULL) { btgLoad("BTG\\default.btg"); } #if MR_KEYBOARD_CHEATS glShadeModel(gMosaic ? GL_FLAT : GL_SMOOTH); #else glShadeModel(GL_SMOOTH); #endif glGetFloatv(GL_COLOR_CLEAR_VALUE, _bgColor); for (index = 0; index < 4; index++) { _bgByte[index] = (GLubyte)(_bgColor[index] * 255.0f); } dnext = (udword*)_bgByte; dlast = (udword*)lastbg; fastBlends = FALSE;//glCapFastFeature(GL_BLEND); textureStars = (RGLtype == SWtype) ? FALSE : TRUE; depthOn = glIsEnabled(GL_DEPTH_TEST); lightOn = rndLightingEnable(FALSE); texOn = glIsEnabled(GL_TEXTURE_2D); blendOn = glIsEnabled(GL_BLEND); if (RGLtype == SWtype) { glDisable(GL_DEPTH_TEST); } else { if (glCapFeatureExists(RGL_BROKEN_MIXED_DEPTHTEST)) { glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); } else { glDisable(GL_DEPTH_TEST); } } glDisable(GL_CULL_FACE); glDisable(GL_TEXTURE_2D); if (fastBlends) { glEnable(GL_BLEND); rndAdditiveBlends(FALSE); } else { glDisable(GL_BLEND); } //polys if (btgFade != lastFade || *dnext != *dlast) { if (btgFade < 0) { btgFade = 255; } btgColorVertices(fastBlends); *dlast = *dnext; lastFade = btgFade; } if (glCapFeatureExists(GL_VERTEX_ARRAY) && (RGLtype != GLtype)) { //use DrawElements to render the bg polys glInterleavedArrays(GL_C4UB_V3F, 0, (void*)btgTransVerts); if (glCapFeatureExists(GL_COMPILED_ARRAYS_EXT)) { compiledArrays = TRUE; glLockArraysEXT(0, btgHead->numVerts); } else { compiledArrays = FALSE; } glDrawElements(GL_TRIANGLES, 3 * btgHead->numPolys, GL_UNSIGNED_INT, btgIndices); if (compiledArrays) { glUnlockArraysEXT(); } } else { //simulate DrawElements for buggy GLs glBegin(GL_TRIANGLES); for (index = 0; index < (3*btgHead->numPolys); index++) { btgTransVertex* pVert = &btgTransVerts[btgIndices[index]]; glColor4ub(pVert->red, pVert->green, pVert->blue, pVert->alpha); glVertex3fv((GLfloat*)&pVert->position); } glEnd(); } //stars rndPerspectiveCorrection(FALSE); glGetFloatv(GL_MODELVIEW_MATRIX, modelview); glGetFloatv(GL_PROJECTION_MATRIX, projection); if (textureStars) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); rgluOrtho2D(0.0f, (GLfloat)MAIN_WindowWidth, 0.0f, (GLfloat)MAIN_WindowHeight); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } rndAdditiveBlends(TRUE); glEnable(GL_BLEND); trClearCurrent(); for (nStar = 0; nStar < btgHead->numStars; nStar++) { btgTransStar* pStar; pStar = &btgTransStars[nStar]; glColor4ub((GLubyte)btgStars[nStar].red, (GLubyte)btgStars[nStar].green, (GLubyte)btgStars[nStar].blue, (GLubyte)btgStars[nStar].alpha); if (btgStars[nStar].glhandle != 0) { vector pscreen; bool billboard = TRUE; glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, btgStars[nStar].glhandle); rndTextureEnvironment(RTE_Modulate); if (billboard) { if (clipPointToScreenWithMatrices(&pStar->ul, &pscreen, modelview, projection, 0) || clipPointToScreenWithMatrices(&pStar->ll, &pscreen, modelview, projection, 0) || clipPointToScreenWithMatrices(&pStar->lr, &pscreen, modelview, projection, 0) || clipPointToScreenWithMatrices(&pStar->ur, &pscreen, modelview, projection, 0)) { sdword width, height; width = btgStars[nStar].width >> 1; height = btgStars[nStar].height >> 1; clipPointToScreenWithMatrices(&pStar->mid, &pscreen, modelview, projection, 1); if (textureStars) { glColor3ub((ubyte)((btgStars[nStar].red * btgFade) >> 8), (ubyte)((btgStars[nStar].green * btgFade) >> 8), (ubyte)((btgStars[nStar].blue * btgFade) >> 8)); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex2f(pscreen.x - width, pscreen.y - height); glTexCoord2f(0.0f, 1.0f); glVertex2f(pscreen.x - width, pscreen.y + height); glTexCoord2f(1.0f, 1.0f); glVertex2f(pscreen.x + width, pscreen.y + height); glTexCoord2f(1.0f, 0.0f); glVertex2f(pscreen.x + width, pscreen.y - height); glEnd(); } else { glTexCoord2f(pscreen.x - width, pscreen.y - height); glDrawPixels(btgStars[nStar].width, btgStars[nStar].height, GL_RGBA, GL_UNSIGNED_BYTE, NULL); } } } else { glBegin(GL_QUADS); glTexCoord2f(0.0f, 1.0f); glVertex3fv((GLfloat*)&pStar->ul); glTexCoord2f(0.0f, 0.0f); glVertex3fv((GLfloat*)&pStar->ll); glTexCoord2f(1.0f, 0.0f); glVertex3fv((GLfloat*)&pStar->lr); glTexCoord2f(1.0f, 1.0f); glVertex3fv((GLfloat*)&pStar->ur); glEnd(); } } } if (textureStars) { glMatrixMode(GL_PROJECTION); glLoadMatrixf(projection); glMatrixMode(GL_MODELVIEW); glLoadMatrixf(modelview); } glDisable(GL_BLEND); rndAdditiveBlends(FALSE); rndLightingEnable(lightOn); if (texOn) glEnable(GL_TEXTURE_2D); if (blendOn) { glEnable(GL_BLEND); } else { glDisable(GL_BLEND); } glEnable(GL_CULL_FACE); if (depthOn) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } if (glCapFeatureExists(RGL_BROKEN_MIXED_DEPTHTEST)) { glDepthMask(GL_TRUE); } }