/*============================================================================= Name : mesh.c Purpose : Functions to load, manipulate and render polygon meshes. Created 6/21/1997 by lmoloney Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #define WIN32_LEAN_AND_MEAN #include #include "glinc.h" #include #include #include #include "types.h" #include "debug.h" #include "file.h" #include "memory.h" #include "vector.h" #include "texreg.h" #include "render.h" #include "utility.h" #include "teams.h" #include "objtypes.h" #include "spaceobj.h" #include "universe.h" #include "mesh.h" #include "shader.h" #include "glcaps.h" #include "autolod.h" #include "crc32.h" #include "hash.h" #include "prim3d.h" #include "transformer.h" #define GLVERTEX(x) glVertex4fv((GLfloat*)(x)) #define VERTEXLIST clipVertexList #define meshColour(NORMAL) shColourSet0((vector*)NORMAL); #define _MESH_OBJECT_RENDER(O, M, C)\ if (g_Output) meshObjectOutput(O, M, C);\ else if (g_Points) meshObjectPoints(O, M, C);\ else meshObjectRender(O, M, C); #define MESH_OBJECT_RENDER(O, M, C)\ if (g_SpecHack)\ {\ meshSpecObjectRender(O, M, C);\ }\ else if (usingShader && rndLightingEnabled && !g_WireframeHack)\ {\ if (RGL)\ {\ if (glIsEnabled(GL_CLIP_PLANE0)) meshObjectRenderLitRGL(O, M, C);\ else meshObjectRenderLitRGLvx(O, M, C);\ }\ else\ {\ meshObjectRenderLit(O, M, C);\ }\ }\ else\ {\ meshObjectRender(O, M, C);\ } /*============================================================================= Data: =============================================================================*/ sdword scaledWidth, scaledHeight; sdword visiblePoly = -1; sdword visibleDirection = 0; sdword visibleWhich = 0; sdword visibleSegment = 0; real32 visibleUV[3][2]; bool g_SpecificPoly = FALSE; void meshSpecObjectRender(polygonobject *object, materialentry *materials, sdword iColorScheme); void meshObjectRenderLitRGLvx(polygonobject *object, materialentry *materials, sdword iColorScheme); bool gSelfIllum; bool usingShader = TRUE; bool bFade = FALSE; real32 meshFadeAlpha = 1.0f; bool8 g_NoMatSwitch = FALSE; bool8 g_ReplaceHack = FALSE; bool8 g_WireframeHack = FALSE; bool8 g_SpecHack = FALSE; bool8 g_HiddenRemoval = TRUE; bool g_Points = FALSE; bool g_Output = FALSE; static sdword specIndex; static ubyte specColour[4]; #if MESH_LOAD_DUMMY_TEXTURE texhandle meshTestHandle = TEX_Invalid; #endif //current mesh poly mode sdword meshPolyMode = MPM_Flat; #if MESH_MATERIAL_STATS sdword meshTotalMaterials = 0; sdword meshMaterialChanges = 0; char meshMaterialStatsString[100] = ""; #endif //MESH_MATERIAL_STATS #if MESH_HACK_TEAM_COLORS trcolorinfo meshRace1Colors = {colRGB(129, 152, 97), colRGB(139, 152, 30)}; trcolorinfo meshRace2Colors = {colRGB(17, 17, 145), colRGB(209, 28, 139)}; #endif //funtion-pointer for changing the current material void (*meshCurrentMaterial)(materialentry *material, sdword iColorScheme) = meshCurrentMaterialDefault; #if MESH_SURFACE_NAME_DEBUG bool gTestTexture = FALSE; #endif #if MESH_MORPH_DEBUG bool meshMorphDebug = FALSE; struct { color from, to; } meshMorphLineColors[] = { {colRGB(255, 0, 0), colRGB(128, 0, 0)}, {colRGB(0, 255, 0), colRGB(0, 128, 0)}, {colRGB(0, 0, 255), colRGB(0, 0, 128)}, {colRGB(255, 255, 0), colRGB(128, 128, 0)}, {colRGB(0, 255, 255), colRGB(0, 128, 128)}, {colRGB(255, 0, 255), colRGB(128, 0, 128)}, {colBlack, colBlack} }; #endif; /*============================================================================= Functions: =============================================================================*/ /*----------------------------------------------------------------------------- Name : meshStartup Description : startup the mesh module Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void meshStartup() { usingShader = TRUE; } /*----------------------------------------------------------------------------- Name : meshShutdown Description : shutdown the mesh module Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void meshShutdown() { //nothing here } /*----------------------------------------------------------------------------- Name : meshSetFade Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void meshSetFade(real32 fade) { if (!usingShader) { bFade = FALSE; } else { meshFadeAlpha = 1.0f - fade; if (meshFadeAlpha == 1.0f) { bFade = FALSE; } else { bFade = TRUE; } } } /*----------------------------------------------------------------------------- Name : meshSetSpecular Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void meshSetSpecular(sdword mode, ubyte red, ubyte green, ubyte blue, ubyte alpha) { specIndex = mode; if (mode == -1) { g_SpecHack = 0; } else { specColour[0] = red; specColour[1] = green; specColour[2] = blue; specColour[3] = alpha; g_SpecHack = 1; } } /*----------------------------------------------------------------------------- Name : meshNameToRace Description : Convert a mesh file name to a race Inputs : fileName - name of mesh file Outputs : Return : pointer to either a ship or a missile static info for that race and type or NULL if unknown race ----------------------------------------------------------------------------*/ StaticInfo *meshNameToStaticInfo(char *fileName) { char raceString[16]; char typeString[64]; sdword length; StaticInfo *info; ShipRace race; ShipType type; dbgAssert(strchr(fileName, '\\')); length = strchr(fileName, '\\') - fileName; memcpy(raceString, fileName, length); raceString[length] = 0; //make a race name fileName += length + 1; // dbgAssert(strchr(fileName, '\\')); //make a type name if (strchr(fileName, '\\') == NULL) { return(NULL); } length = strchr(fileName, '\\') - fileName; memcpy(typeString, fileName, length); typeString[length] = 0; if (_stricmp(raceString, "Derelicts") == 0) { //if it's a derelict type = StrToDerelictType(typeString); dbgAssert(type != 0xffffffff); return((StaticInfo *)&derelictStaticInfos[type]); } //else it's a race-specific texture, process normally race = StrToShipRace(raceString); //get the race if (race == 0xffffffff) { race = NUM_RACES; return(NULL); } type = StrToShipType(typeString); if (type == 0xffffffff) { if (_stricmp(typeString, "MISSILE") == 0) { info = (StaticInfo *)&missileStaticInfos[race]; } else if (_stricmp(typeString, "MINE") == 0) { info = (StaticInfo *)&mineStaticInfos[race]; } else { dbgWarningf(DBG_Loc, "Don't know how to team colorize '%s' for '%s'", typeString, raceString); return(NULL); } } else { info = (StaticInfo *)GetShipStaticInfo(type, race); } return(info); } /*----------------------------------------------------------------------------- Name : meshTextureRegisterAllPlayers Description : Register the named texture for each player who is of a certain race. Inputs : fullName - name of texture. race - race of players to compare to. If NUM_RACES, there is no race and only one handle is allocated (cookie wast can you say?) meshReference - mesh reference to pass to the texture registry Outputs : allocates a list of texture handles TE_NumberPlayers in length. Return : pointer to this new list of handles cast to a handle. ----------------------------------------------------------------------------*/ trhandle meshTextureRegisterAllPlayers(char *fullName, void *meshReference) { trhandle handleList[MAX_MULTIPLAYER_PLAYERS], *allocedList; sdword index, nRegistered = 0; StaticInfo *info; memClearDword(handleList, TR_Invalid, TE_NumberPlayers);//allocate and clear handle list info = meshNameToStaticInfo(fullName); if (info == NULL) { handleList[0] = trTextureRegister(fullName, NULL, meshReference); nRegistered = 1; } else { for (index = 0; index < MAX_MULTIPLAYER_PLAYERS; index++) { dbgAssert(index < TE_NumberPlayers); handleList[index] = TR_Invalid; if (((ShipStaticInfo *)info)->teamColor[index]) { //if this player can build this ship handleList[index] = trTextureRegister(fullName, //register in this players's colors &teColorSchemes[index].textureColor, meshReference); nRegistered++; } } } if (nRegistered > 0) { //if some were registered allocedList = memAlloc(TE_NumberPlayers * sizeof(trhandle), "trhandle*TE_NumberPlayers", NonVolatile); memcpy(allocedList, handleList, TE_NumberPlayers * sizeof(trhandle)); return((trhandle)(allocedList)); //return memAlloced copy } return(TR_Invalid); } /*----------------------------------------------------------------------------- Name : meshTextureNameToPath Description : Create a path for a texture from a texture name as it comes from the .geo file. Inputs : mesh - name of mesh file tex - name of texture Outputs : out - path to texture Return : void ----------------------------------------------------------------------------*/ void meshTextureNameToPath(char *out, char *mesh, char *tex) { char *string; //make path of mesh path and \\texture\\ strcpy(out, mesh); string = out; while (strchr(string, '\\')) { //find last backslash string = strchr(string, '\\') + 1; } dbgAssert(string != out); dbgAssert(*string); *string = 0; //NULL terminate and append texture name strcat(out, tex); _strupr(out); } bool meshStringContainsPeriod(char* s) { sdword i; for (i = 0; i < strlen(s); i++) { if (s[i] == '.') { return TRUE; } } return FALSE; } void meshPagedName(char* outFileName, char* inFileName) { char fileName[1024]; char* token; outFileName[0] = '\0'; strcpy(fileName, inFileName); token = strtok(fileName, "\\/"); while (token != NULL) { if (meshStringContainsPeriod(token)) { token[strlen(token) - 3] = 'p'; strcat(outFileName, token); } else { strcat(outFileName, token); strcat(outFileName, "\\"); } token = strtok(NULL, "\\/"); } } /*----------------------------------------------------------------------------- Name : meshPagedVersionExists Description : See if the paged version (.peo) of a file exists. Inputs : fileName - file to check existence of paged version Outputs : Return : ----------------------------------------------------------------------------*/ bool meshPagedVersionExists(char* fileName) { char pagedName[1024]; meshPagedName(pagedName, fileName); return fileExists(pagedName, 0); } /*----------------------------------------------------------------------------- Name : meshObjectFixupPacoUV Description : fixup UV seaming on a particular polygon object Inputs : object - the polygonobject to fixup or un-fixup on - to fixup or not fixup Outputs : Return : ----------------------------------------------------------------------------*/ void meshObjectFixupPacoUV(polygonobject* object, sdword on) { polyentry* polygon; sdword iPoly; sdword width, height; real32 s, t; for (iPoly = 0, polygon = object->pPolygonList; iPoly < object->nPolygons; iPoly++, polygon++) { if (polygon->reserved[0] > 1) { //will be 0 or 1 if adjust data exists continue; } //isolate width, height nibbles width = (polygon->reserved[1] & 0xf0) >> 4; height = polygon->reserved[1] & 0x0f; //convert exp2 to number width = 1 << width; height = 1 << height; //define correction factors s = 0.6f / (real32)width; t = 0.6f / (real32)height; if (on) { //want to fixup if (bitTest(polygon->flags, PAC_FIXED)) { //already fixed up continue; } else { //not fixed up yet bitSet(polygon->flags, PAC_FIXED); } } else { //want to unfixup if (bitTest(polygon->flags, PAC_FIXED)) { //currently fixed up bitClear(polygon->flags, PAC_FIXED); s = -s; t = -t; } else { //already unfixed continue; } } //convert uv coords that need converting //0 if (bitTest(polygon->flags, PAC_S0_0)) { polygon->s0 += s; } if (bitTest(polygon->flags, PAC_S0_1)) { polygon->s0 -= s; } if (bitTest(polygon->flags, PAC_T0_0)) { polygon->t0 += t; } if (bitTest(polygon->flags, PAC_T0_1)) { polygon->t0 -= t; } //1 if (bitTest(polygon->flags, PAC_S1_0)) { polygon->s1 += s; } if (bitTest(polygon->flags, PAC_S1_1)) { polygon->s1 -= s; } if (bitTest(polygon->flags, PAC_T1_0)) { polygon->t1 += t; } if (bitTest(polygon->flags, PAC_T1_1)) { polygon->t1 -= t; } //2 if (bitTest(polygon->flags, PAC_S2_0)) { polygon->s2 += s; } if (bitTest(polygon->flags, PAC_S2_1)) { polygon->s2 -= s; } if (bitTest(polygon->flags, PAC_T2_0)) { polygon->t2 += t; } if (bitTest(polygon->flags, PAC_T2_1)) { polygon->t2 -= t; } } } /*----------------------------------------------------------------------------- Name : meshFixupPacoUV Description : apply UV munging to fixup texture seaming to a particular mesh Inputs : mesh - the mesh on - to fixup or not fixup Outputs : Return : ----------------------------------------------------------------------------*/ void meshFixupPacoUV(meshdata* mesh, sdword on) { sdword index; polygonobject* object; if (mesh->localSize == 0xFFFFFFFF) { //old-style mesh, no fixup info return; } if (on) { if (mesh->localSize > 0) { //already fixed up return; } else { mesh->localSize = 1; } } else { if (mesh->localSize == 0) { //already un-fixed return; } else { mesh->localSize = 0; } } for (index = 0, object = &mesh->object[0]; index < mesh->nPolygonObjects; index++, object++) { meshObjectFixupPacoUV(object, on); } } /*----------------------------------------------------------------------------- Name : meshLoad Description : Loads in a mesh file. Inputs : fileName - name of file to load Outputs : Allocates and loads data returned in meshdata structure. Return : Pointer to allocated and fixed-up meshdata structure. ----------------------------------------------------------------------------*/ meshdata *meshLoad(char *inFileName) { GeoFileHeader header; filehandle file; meshdata *mesh; sdword index; udword offset; polygonobject *object; char fullName[FL_Path]; sdword fileLength; trhandle handle; bool allowPacking; bool newFormat; char pagedName[1024]; char* fileName; allowPacking = mainAllowPacking; if (allowPacking && meshPagedVersionExists(inFileName)) { meshPagedName(pagedName, inFileName); fileName = pagedName; } else { fileName = inFileName; } fileLength = fileSizeGet(fileName, 0); file = fileOpen(fileName, 0); //open file fileBlockRead(file, &header, sizeof(GeoFileHeader)); //read in header #if MESH_ERROR_CHECKING if (strncmp(header.identifier, MESH_FileID, MESH_FileIDLength))//verify ident. string { if (strncmp(header.identifier, MESH_FileID2, MESH_FileIDLength)) { dbgFatalf(DBG_Loc, "meshLoad: Invalid ident string in %s. Expected %s, found %s", fileName, MESH_FileID, header.identifier); } } if (header.version != MESH_Version) //verify correct version { dbgFatalf(DBG_Loc, "meshLoad: Invalid version # %s. Expected 0x%x, found 0x%x", fileName, MESH_Version, header.version); } #endif if (strncmp(header.identifier, MESH_FileID2, MESH_FileIDLength) == 0) { newFormat = TRUE; } else { newFormat = FALSE; } mesh = memAlloc(fileLength - sizeof(header)/*header.fileSize*/ + sizeof(meshdata) - //allocate the memory for file sizeof(polygonobject), fileName, NonVolatile); memNameSetLong((memcookie*)((ubyte *)mesh - sizeof(memcookie)), fileName); //now that it is all loaded in, all pointers need to be fixed up offset = (udword)mesh - sizeof(GeoFileHeader) + sizeof(meshdata) - sizeof(polygonobject); //!!! add size of materials when they're in??? // mesh->localSize = header.localSize; if (newFormat) { mesh->localSize = 0; } else { mesh->localSize = 0xFFFFFFFF; } mesh->nPublicMaterials = header.nPublicMaterials; //set other header members mesh->nLocalMaterials = header.nLocalMaterials; mesh->publicMaterial = (materialentry *)(header.oPublicMaterial + offset); mesh->localMaterial = (materialentry *)(header.oLocalMaterial + offset); mesh->nPolygonObjects = header.nPolygonObjects; //read remainder of the file #if MESH_RETAIN_FILENAMES mesh->fileName = memStringDupe(fileName); #endif fileBlockRead(file, &mesh->object[0], fileLength - sizeof(header)/*header.fileSize*/); fileClose(file); //close the file mesh->name = header.pName + offset; //set name pointer //fix up the polgon objects for (index = 0; index < mesh->nPolygonObjects; index++) { object = &mesh->object[index]; //get pointer to poly object //object->pName += offset; //!!! is there a name (ubyte *)object->pVertexList += offset; //fixup vertex list pointer (ubyte *)object->pNormalList += offset; //fixup vertex list pointer (ubyte *)object->pPolygonList += offset; //fixup vertex list pointer if (object->pMother != NULL) { (ubyte *)object->pMother += offset; } if (object->pDaughter != NULL) { (ubyte *)object->pDaughter += offset; } if (object->pSister != NULL) { (ubyte *)object->pSister += offset; } if (object->pName != NULL) { (ubyte *)object->pName += offset; //fix up name } object->iObject = index; object->nameCRC = crc16Compute((ubyte*)object->pName, strlen(object->pName)); } if (allowPacking && mainOnlyPacking && (fileName != pagedName)) { for (index = 0; index < mesh->nLocalMaterials + mesh->nPublicMaterials; index++) { if (mesh->localMaterial[index].pName != NULL) { mesh->localMaterial[index].pName += offset; } mesh->localMaterial[index].bTexturesRegistered = FALSE; mesh->localMaterial[index].texture = 0; } return(mesh); } for (index = 0; index < mesh->nLocalMaterials + mesh->nPublicMaterials; index++) { //for all materials if (mesh->localMaterial[index].pName != NULL) { mesh->localMaterial[index].pName += offset; } mesh->localMaterial[index].bTexturesRegistered = FALSE; if (mesh->localMaterial[index].texture != 0) { //if there is a texture (ubyte *)mesh->localMaterial[index].texture += offset;//fix up texture name pointer mesh->localMaterial[index].textureNameSave = (char *)mesh->localMaterial[index].texture; meshTextureNameToPath(fullName, fileName, (char *)mesh->localMaterial[index].texture); handle = meshTextureRegisterAllPlayers(fullName, mesh);//register the texture if (handle != TR_Invalid) { //if anything was registered mesh->localMaterial[index].texture = handle; mesh->localMaterial[index].bTexturesRegistered = TRUE; } } /* else { mesh->localMaterial[index].texture = TR_Invalid; } */ } return(mesh); } /*----------------------------------------------------------------------------- Name : meshObjectFixupUV Description : nudges UV coordinates inwards to eliminate filtered oversampling errors Inputs : object - polygonobject whose UVs are to be modified materials - list of materials on the object Outputs : Return : ----------------------------------------------------------------------------*/ void meshObjectFixupUV(polygonobject* object, materialentry* materials) { sdword iPoly; polyentry* polygon; sdword currentMaterial; materialentry* material; trhandle handle; texreg* reg; real32 zero, one; for (iPoly = 0, polygon = object->pPolygonList; iPoly < object->nPolygons; iPoly++, polygon++) { currentMaterial = polygon->iMaterial; material = &materials[currentMaterial]; if (material->bTexturesRegistered && (material->texture != 0)) { handle = ((trhandle*)material->texture)[0/*iColorScheme*/]; //!!! dbgAssert(handle != TR_InvalidHandle); if (handle == TR_InvalidHandle) { continue; } reg = trStructureGet(handle); zero = 0.51f / reg->scaledWidth; one = 1.0f - zero; if (polygon->s0 == 0.0f) polygon->s0 = zero; if (polygon->s0 == 1.0f) polygon->s0 = one; if (polygon->t0 == 0.0f) polygon->t0 = zero; if (polygon->t0 == 1.0f) polygon->t0 = one; if (polygon->s1 == 0.0f) polygon->s1 = zero; if (polygon->s1 == 1.0f) polygon->s1 = one; if (polygon->t1 == 0.0f) polygon->t1 = zero; if (polygon->t1 == 1.0f) polygon->t1 = one; if (polygon->s2 == 0.0f) polygon->s2 = zero; if (polygon->s2 == 1.0f) polygon->s2 = one; if (polygon->t2 == 0.0f) polygon->t2 = zero; if (polygon->t2 == 1.0f) polygon->t2 = one; } } } /*----------------------------------------------------------------------------- Name : meshFixupUV Description : nudges UV coordinates inwards to eliminate filtered oversampling errors Inputs : mesh - the mesh to fixup Outputs : Return : ----------------------------------------------------------------------------*/ void meshFixupUV(meshdata* mesh) { sdword index; polygonobject* object; for (index = 0, object = &mesh->object[0]; index < mesh->nPolygonObjects; index++, object++) { meshObjectFixupUV(object, mesh->localMaterial); } } /*----------------------------------------------------------------------------- Name : meshRecolorize Description : Recolorize all the textures of a mesh based upon new colors that may have been picked. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void meshRecolorize(meshdata *mesh) { sdword index, j, k; trhandle *handles, validHandle; StaticInfo *info; char fullName[FL_Path]; trhandle handle; info = meshNameToStaticInfo(mesh->fileName); if (info == NULL) { return; } for (index = 0; index < mesh->nLocalMaterials + mesh->nPublicMaterials; index++) { //for all materials in mesh //3 states of this member:TR_Invalid, pointer to list of texture handles or 0 (no texture) if (mesh->localMaterial[index].texture != TR_Invalid && mesh->localMaterial[index].texture != 0) { //if textured material if (!mesh->localMaterial[index].bTexturesRegistered) { //if textures were never registered properly meshTextureNameToPath(fullName, mesh->fileName, (char *)mesh->localMaterial[index].texture); handle = meshTextureRegisterAllPlayers(fullName, mesh);//register the texture if (handle != TR_Invalid) { //if anything was registered mesh->localMaterial[index].texture = handle; mesh->localMaterial[index].bTexturesRegistered = TRUE; } } else { //textures were previously registered properly handles = (trhandle *)mesh->localMaterial[index].texture; validHandle = TR_Invalid; //4 passes for all players //first pass: see if any texture colors are way out of whack for (j = 0; j < MAX_MULTIPLAYER_PLAYERS; j++) { if (handles[j] != TR_Invalid) { //if this texture allocated for (k = 0; k < MAX_MULTIPLAYER_PLAYERS; k++) { if (((ShipStaticInfo *)info)->teamColor[k]) { //if this team color will be loaded if (k != j && trColorsEqual(&teColorSchemes[k].textureColor, trIndex(handles[j])) == trPaletteIndex(handles[j])) { //if this handle's color is in some other team's desired color for (k = 0; k < MAX_MULTIPLAYER_PLAYERS; k++) { //delete all handles; this texture will have to be loaded from scratch if (handles[k] != TR_Invalid) { trRegisterRemoval(handles[k]); handles[k] = TR_Invalid; } } goto doneDeletingAllOldTextures;//break-break, skip next 2 passes } } } } } //second pass: find a valid handle for (j = 0; j < MAX_MULTIPLAYER_PLAYERS; j++) { //find a valid handle if (handles[j] != TR_Invalid) { validHandle = handles[j]; break; } } //third pass: removals of unused color schemes for (j = 0; j < MAX_MULTIPLAYER_PLAYERS; j++) { //for all players if (!((ShipStaticInfo *)info)->teamColor[j]) { //if this player does not need this mesh if (handles[j] != TR_Invalid) { //this texture no longer needed trRegisterRemoval(handles[j]); //delete this version of the texture (without reloading) ... handles[j] = TR_Invalid; } } } doneDeletingAllOldTextures: //fourth pass: additions and updates of color schemes for (j = 0; j < MAX_MULTIPLAYER_PLAYERS; j++) { //for all players if (((ShipStaticInfo *)info)->teamColor[j]) { //if this player needs this mesh if (handles[j] != TR_Invalid) { //if texture was loaded properly before trTextureColorsUpdate(handles[j],//update the colors &teColorSchemes[j].textureColor); } else { //this texture was never loaded properly //register the texture with a new color if (validHandle == TR_Invalid) { //if no valid handles (they've all be unregistered) meshTextureNameToPath(fullName, mesh->fileName, mesh->localMaterial[index].textureNameSave); validHandle = handles[j] = trTextureRegister(fullName, &teColorSchemes[j].textureColor, mesh); } else { //else a valid handle aleready exists for this texture handles[j] = trRegisterAddition(validHandle, &teColorSchemes[j].textureColor); } } } #if MESH_ERROR_CHECKING else if (handles[j] != TR_Invalid) { //this texture no longer needed dbgFatalf(DBG_Loc, "Improperly removed texture for player #%d", j); } #endif } /* #if MESH_ERROR_CHECKING //make sure no duplicate handles for (l = 0; l < MAX_MULTIPLAYER_PLAYERS; l++) { for (k = 0; k < MAX_MULTIPLAYER_PLAYERS; k++) { //find a valid handle if (k != l && handles[l] != TR_Invalid && handles[l] != 0) { if (handles[l] == handles[k]) { dbgFatalf(DBG_Loc, "handles[%d] == handles[%d] in iteration %d.", l, k, j); } } } } #endif */ } } } } /*----------------------------------------------------------------------------- Name : meshFree Description : Free up the specified mesh. Inputs : mesh - pointer to mesh to be freed. Outputs : Return : void Note : does not free up any instances of thie mesh or any textures bound to it. ----------------------------------------------------------------------------*/ void meshFree(meshdata *mesh) { sdword index, j; for (index = 0; index < mesh->nLocalMaterials + mesh->nPublicMaterials; index++) { //for all materials // if (mesh->localMaterial[index].texture != TR_Invalid)//if material has a texture if (mesh->localMaterial[index].bTexturesRegistered) { for (j = 0; j < TE_NumberPlayers; j++) { //for each player if (((trhandle *)mesh->localMaterial[index].texture)[j] != TR_Invalid) { //if this player was this race trTextureUnregister(((trhandle *)mesh->localMaterial[index].texture)[j]); } //unregister the texture } memFree((ubyte *)mesh->localMaterial[index].texture); //free the texture handle list } } #if MESH_RETAIN_FILENAMES memFree(mesh->fileName); #endif memFree((void *)mesh); } /*----------------------------------------------------------------------------- Name : meshCurrentMaterialDefault Description : Makes specified material current Inputs : material - material to make current iColorScheme - index of player associated with this mesh (for texture coloring) or 0 for no player. Outputs : .. Return : void ----------------------------------------------------------------------------*/ void meshCurrentMaterialDefault(materialentry *material, sdword iColorScheme) { GLfloat attribs[4]; GLenum face; #if MESH_TEAM_COLORS color teamAmbient, teamDiffuse; #if MESH_SPECULAR color teamSpecular; #endif #endif if (bitTest(material->flags, MDF_2Sided)) { //if 2-sided material face = GL_FRONT_AND_BACK; rndBackFaceCullEnable(FALSE); //disable culling if (!usingShader) glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); //2-sided lightmodel } else { face = GL_FRONT; rndBackFaceCullEnable(TRUE); //enable culling if (!usingShader) glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); //1-sided lightmodel } gSelfIllum = FALSE; if (g_ReplaceHack) { rndTextureEnvironment(RTE_Replace); } else if (bitTest(material->flags, MDF_SelfIllum)) { gSelfIllum = TRUE; if (bFade && (RGLtype == GLtype)) { rndTextureEnvironment(RTE_Modulate); } else { rndTextureEnvironment(RTE_Replace); } } else { rndTextureEnvironment(RTE_Modulate); } #if MESH_TEAM_COLORS if ((material->flags & (MDF_BaseColor | MDF_StripeColor)) == 0) { //if not a team color polygon #endif attribs[0] = (GLfloat)(colRed(material->ambient)) / 255.0f; attribs[1] = (GLfloat)(colGreen(material->ambient)) / 255.0f; attribs[2] = (GLfloat)(colBlue(material->ambient)) / 255.0f; attribs[3] = 1.0f; if (usingShader) { shSetMaterial(attribs, NULL); //shader } else { rndMaterialfv(face, GL_AMBIENT, attribs); } attribs[0] = (GLfloat)(colRed(material->diffuse)) / 255.0f; attribs[1] = (GLfloat)(colGreen(material->diffuse)) / 255.0f; attribs[2] = (GLfloat)(colBlue(material->diffuse)) / 255.0f; if (bFade) { attribs[3] = meshFadeAlpha; } else { attribs[3] = 1.0f; } if (usingShader) { shSetMaterial(NULL, attribs); //shader } else { rndMaterialfv(face, GL_DIFFUSE, attribs); } #if MESH_SPECULAR attribs[0] = (GLfloat)(colRed(material->specular)) / 255.0f; attribs[1] = (GLfloat)(colGreen(material->specular)) / 255.0f; attribs[2] = (GLfloat)(colBlue(material->specular)) / 255.0f; glMaterialfv(face, GL_SPECULAR, attribs); #endif #if MESH_TEAM_COLORS } else { //it's a team color if (material->flags & MDF_BaseColor) { //it's a base color poly teamAmbient = teColorSchemes[iColorScheme].ambient; teamDiffuse = teColorSchemes[iColorScheme].diffuse; #if MESH_SPECULAR teamSpecular = teColorSchemes[iColorScheme].specular; #endif } else { //else it's a stripe color teamAmbient = teColorSchemes[iColorScheme].stripeAmbient; teamDiffuse = teColorSchemes[iColorScheme].stripeDiffuse; #if MESH_SPECULAR teamSpecular = teColorSchemes[iColorScheme].stripeSpecular; #endif } attribs[0] = (GLfloat)(colRed(teamAmbient)) / 255.0f; attribs[1] = (GLfloat)(colGreen(teamAmbient)) / 255.0f; attribs[2] = (GLfloat)(colBlue(teamAmbient)) / 255.0f; attribs[3] = 1.0f; if (usingShader) { shSetMaterial(attribs, NULL); //shader } else { rndMaterialfv(face, GL_AMBIENT, attribs); } attribs[0] = (GLfloat)(colRed(teamDiffuse)) / 255.0f; attribs[1] = (GLfloat)(colGreen(teamDiffuse)) / 255.0f; attribs[2] = (GLfloat)(colBlue(teamDiffuse)) / 255.0f; if (bFade) { attribs[3] = meshFadeAlpha; } else { attribs[3] = 1.0f; } if (usingShader) { shSetMaterial(NULL, attribs); //shader } else { rndMaterialfv(face, GL_DIFFUSE, attribs); } #if MESH_SPECULAR attribs[0] = (GLfloat)(colRed(teamSpecular)) / 255.0f; attribs[1] = (GLfloat)(colGreen(teamSpecular)) / 255.0f; attribs[2] = (GLfloat)(colBlue(teamSpecular)) / 255.0f; glMaterialfv(face, GL_SPECULAR, attribs); #endif } #endif //MESH_TEAM_COLORS // if (material->texture != TR_Invalid && !g_WireframeHack) if (material->bTexturesRegistered && !g_WireframeHack) { //if a textured polygon #if MESH_SURFACE_NAME_DEBUG if (material->pName != NULL && (!strcmp(material->pName, "bot1front"))) { gTestTexture = TRUE; } else { gTestTexture = FALSE; } #endif rndTextureEnable(TRUE); trMakeCurrent(((trhandle *)material->texture)[iColorScheme]); if (bitTest(material->flags, MPM_Smooth) && enableSmoothing == TRUE) { meshPolyMode = MPM_SmoothTexture; if (!g_SpecHack) { glShadeModel(GL_SMOOTH); } } else { meshPolyMode = MPM_Texture; glShadeModel(GL_FLAT); } } else { rndTextureEnable(FALSE); if (bitTest(material->flags, MPM_Smooth) && enableSmoothing == TRUE) { meshPolyMode = MPM_Smooth; if (!g_SpecHack) { glShadeModel(GL_SMOOTH); } } else { meshPolyMode = MPM_Flat; glShadeModel(GL_FLAT); } } if (usingShader) { shUpdateLighting(); //shader } } /*----------------------------------------------------------------------------- Name : meshCurrentMaterialTex Description : Makes specified material current using a single texture, not the one in the material entries. Inputs : material - material to make current Outputs : .. Return : void ----------------------------------------------------------------------------*/ void meshCurrentMaterialTex(materialentry *material, sdword iColorScheme) { GLenum face; if (bitTest(material->flags, MDF_2Sided)) { //if 2-sided material face = GL_FRONT_AND_BACK; rndBackFaceCullEnable(FALSE); //disable culling if (!usingShader) glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); //2-sided lightmodel } else { face = GL_FRONT; rndBackFaceCullEnable(TRUE); //enable culling if (!usingShader) glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); //1-sided lightmodel } // if (enableTextures == TRUE) // { if (bitTest(material->flags, MPM_Smooth) && enableSmoothing == TRUE) { meshPolyMode = MPM_SmoothTexture; if (!g_SpecHack) { glShadeModel(GL_SMOOTH); } } else { meshPolyMode = MPM_Texture; glShadeModel(GL_FLAT); } #if 0 } else { if (bitTest(material->flags, MPM_Smooth) && enableSmoothing == TRUE) { meshPolyMode = MPM_Smooth; if (!g_SpecHack) { glShadeModel(GL_SMOOTH); } } else { meshPolyMode = MPM_Flat; glShadeModel(GL_FLAT); } } #endif } /*----------------------------------------------------------------------------- Name : meshAlmostCurrentMaterial Description : Current material switch function that doesn't change any GL interal stuff. Inputs : material - the material to use iColorScheme - ignored (for compatability) Outputs : Return : ----------------------------------------------------------------------------*/ void meshAlmostCurrentMaterial(materialentry* material, sdword iColorScheme) { // if (material->texture != TR_Invalid) if (material->bTexturesRegistered) { if (bitTest(material->flags, MPM_Smooth) && enableSmoothing == TRUE) { meshPolyMode = MPM_SmoothTexture; } else { meshPolyMode = MPM_Texture; } } else { if (bitTest(material->flags, MPM_Smooth) && enableSmoothing == TRUE) { meshPolyMode = MPM_Smooth; } else { meshPolyMode = MPM_Flat; } } } /*----------------------------------------------------------------------------- Name : meshSpecColour Description : set specular colour given parameters Inputs : ... Outputs : Return : ----------------------------------------------------------------------------*/ void meshSpecColour(normalentry* normal, vertexentry* vertex, real32* m, real32* minv) { vector vert; vector norm; ubyte colour[4]; norm.x = normal->x; norm.y = normal->y; norm.z = normal->z; vert.x = vertex->x; vert.y = vertex->y; vert.z = vertex->z; colour[0] = specColour[0]; colour[1] = specColour[1]; colour[2] = specColour[2]; colour[3] = specColour[3]; shSpecularColour(specIndex, 0, &vert, &norm, colour, m, minv); glColor4ub(colour[0], colour[1], colour[2], colour[3]); } void meshMorphedSpecColour(vector* normal, real32* m, real32* minv) { ubyte colour[4]; colour[0] = specColour[0]; colour[1] = specColour[1]; colour[2] = specColour[2]; colour[3] = specColour[3]; shSpecularColour(specIndex, 0, NULL, normal, colour, m, minv); glColor4ub(colour[0], colour[1], colour[2], colour[3]); } /*----------------------------------------------------------------------------- Name : meshObjectRender Description : Render a single polygon object (part of a meshdata structure) Inputs : object - object to render materials - material list to use in rendering iColorScheme - index of player associated with this mesh (for texture coloring) or 0 for no player. Outputs : Return : void ----------------------------------------------------------------------------*/ #if MESH_MATERIAL_STATS static sdword nMaterialChanges = 0; static sdword iMaterialMax = 0; #endif //MESH_MATERIAL_STATS void meshObjectOutput(polygonobject* object, materialentry* materials, sdword iColorScheme) { sdword iPoly; polyentry* polygon; FILE* out; out = fopen("uv.txt", "at"); if (out == NULL) { return; } fprintf(out, "-----\n"); fprintf(out, "%d\n", object->nPolygons); polygon = object->pPolygonList; //get first polygon list entry for (iPoly = 0; iPoly < object->nPolygons; iPoly++, polygon++) { fprintf(out, "%d [%0.20f %0.20f] [%0.20f %0.20f] [%0.20f %0.20f]\n", iPoly, polygon->s0, polygon->t0, polygon->s1, polygon->t1, polygon->s2, polygon->t2); } fclose(out); } void meshObjectPoints(polygonobject* object, materialentry* materials, sdword iColorScheme) { sdword iPoly; vertexentry* vertexList; polyentry* polygon; bool lightOn, texOn; vertexList = object->pVertexList; polygon = object->pPolygonList; lightOn = rndLightingEnable(FALSE); texOn = rndTextureEnable(FALSE); glPointSize(4.0f); for (iPoly = 0; iPoly < object->nPolygons; iPoly++, polygon++) { if (iPoly == visiblePoly) { glBegin(GL_POINTS); glColor3ub(255,0,0); glVertex3fv((GLfloat*)&vertexList[polygon->iV0].x); glColor3ub(0,255,0); glVertex3fv((GLfloat*)&vertexList[polygon->iV1].x); glColor3ub(0,0,255); glVertex3fv((GLfloat*)&vertexList[polygon->iV2].x); glEnd(); } } glPointSize(1.0f); rndLightingEnable(lightOn); rndTextureEnable(texOn); } void meshObjectRender(polygonobject *object, materialentry *materials, sdword iColorScheme) { sdword iPoly; vertexentry *vertexList; polyentry *polygon; normalentry *normal, *normalList; sdword currentMaterial = -1; GLenum mode = GL_SMOOTH; sdword lightOn; bool enableBlend; glShadeModel(mode); enableBlend = bFade; if (g_WireframeHack) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); lightOn = rndLightingEnable(FALSE); glColor3ub(200,200,200); enableBlend = glCapFeatureExists(GL_LINE_SMOOTH); if (enableBlend) { glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); rndAdditiveBlends(FALSE); } if (!g_HiddenRemoval) { glDisable(GL_CULL_FACE); } } alodIncPolys(object->nPolygons); vertexList = object->pVertexList; //get base of vertex list normalList = object->pNormalList; //get base of normal list polygon = object->pPolygonList; //get first polygon list entry glBegin(GL_TRIANGLES); //prepare to draw triangles for (iPoly = 0; iPoly < object->nPolygons; iPoly++) { #if MESH_ANAL_CHECKING //validate vertex indices dbgAssert(polygon->iV0 < object->nVertices); dbgAssert(polygon->iV1 < object->nVertices); dbgAssert(polygon->iV2 < object->nVertices); #endif if (polygon->iMaterial != currentMaterial) { //if a new material glEnd(); //end current polygon run currentMaterial = polygon->iMaterial; //remember current material meshCurrentMaterial(&materials[currentMaterial], iColorScheme);//set new material if (enableBlend) { glEnable(GL_BLEND); } glBegin(GL_TRIANGLES); //start new run #if MESH_MATERIAL_STATS nMaterialChanges++; //record material stats iMaterialMax = max(currentMaterial, iMaterialMax); #endif //MESH_MATERIAL_STATS } if (g_SpecificPoly) { if (iPoly != visiblePoly) { polygon++; continue; } else { real32 s, t; visibleUV[0][0] = polygon->s0; visibleUV[0][1] = polygon->t0; visibleUV[1][0] = polygon->s1; visibleUV[1][1] = polygon->t1; visibleUV[2][0] = polygon->s2; visibleUV[2][1] = polygon->t2; s = 0.5f / 256.0f;//(real32)scaledWidth; t = 0.5f / 256.0f;//(real32)scaledHeight; s *= (real32)visibleDirection; t *= (real32)visibleDirection; if (visibleDirection != 0) { switch (visibleSegment) { case 0: if (visibleWhich == 0) { polygon->s0 += s; } else { polygon->t0 += t; } break; case 1: if (visibleWhich == 0) { polygon->s1 += s; } else { polygon->t1 += t; } break; default: if (visibleWhich == 0) { polygon->s2 += s; } else { polygon->t2 += t; } } visibleDirection = 0; } } } switch (meshPolyMode) { case MPM_Flat: dbgAssert(polygon->iFaceNormal != UWORD_Max); normal = &normalList[polygon->iFaceNormal]; glNormal3f(normal->x, normal->y, normal->z); glVertex3fv((GLfloat*)&vertexList[polygon->iV0].x); glVertex3fv((GLfloat*)&vertexList[polygon->iV1].x); glVertex3fv((GLfloat*)&vertexList[polygon->iV2].x); #if RND_POLY_STATS rndNumberPolys++; #endif //RND_POLY_STATS break; case MPM_Texture: dbgAssert(polygon->iFaceNormal != UWORD_Max); normal = &normalList[polygon->iFaceNormal]; glNormal3f(normal->x, normal->y, normal->z); glTexCoord2f(polygon->s0, polygon->t0); glVertex3fv((GLfloat*)&vertexList[polygon->iV0].x); glTexCoord2f(polygon->s1, polygon->t1); glVertex3fv((GLfloat*)&vertexList[polygon->iV1].x); glTexCoord2f(polygon->s2, polygon->t2); glVertex3fv((GLfloat*)&vertexList[polygon->iV2].x); #if RND_POLY_STATS rndNumberPolys++; rndNumberTextured++; #endif //RND_POLY_STATS break; case MPM_Smooth: dbgAssert(vertexList[polygon->iV0].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV0].iVertexNormal]; glNormal3f(normal->x, normal->y, normal->z); glVertex3fv((GLfloat*)&vertexList[polygon->iV0].x); dbgAssert(vertexList[polygon->iV1].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV1].iVertexNormal]; glNormal3f(normal->x, normal->y, normal->z); glVertex3fv((GLfloat*)&vertexList[polygon->iV1].x); dbgAssert(vertexList[polygon->iV2].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV2].iVertexNormal]; glNormal3f(normal->x, normal->y, normal->z); glVertex3fv((GLfloat*)&vertexList[polygon->iV2].x); #if RND_POLY_STATS rndNumberPolys++; rndNumberSmoothed++; #endif //RND_POLY_STATS break; case MPM_SmoothTexture: dbgAssert(vertexList[polygon->iV0].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV0].iVertexNormal]; glNormal3f(normal->x, normal->y, normal->z); glTexCoord2f(polygon->s0, polygon->t0); glVertex3fv((GLfloat*)&vertexList[polygon->iV0].x); dbgAssert(vertexList[polygon->iV1].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV1].iVertexNormal]; glNormal3f(normal->x, normal->y, normal->z); glTexCoord2f(polygon->s1, polygon->t1); glVertex3fv((GLfloat*)&vertexList[polygon->iV1].x); dbgAssert(vertexList[polygon->iV2].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV2].iVertexNormal]; glNormal3f(normal->x, normal->y, normal->z); glTexCoord2f(polygon->s2, polygon->t2); glVertex3fv((GLfloat*)&vertexList[polygon->iV2].x); #if MESH_SURFACE_NAME_DEBUG if (gTestTexture) { dbgMessagef("\n(%4.2f, %4.2f, %4.2f) => (%4.2f, %4.2f)", vertexList[polygon->iV0].x, vertexList[polygon->iV0].y, vertexList[polygon->iV0].z, polygon->s0, polygon->t0); dbgMessagef("\n(%4.2f, %4.2f, %4.2f) => (%4.2f, %4.2f)", vertexList[polygon->iV1].x, vertexList[polygon->iV1].y, vertexList[polygon->iV1].z, polygon->s1, polygon->t1); dbgMessagef("\n(%4.2f, %4.2f, %4.2f) => (%4.2f, %4.2f)", vertexList[polygon->iV2].x, vertexList[polygon->iV2].y, vertexList[polygon->iV2].z, polygon->s2, polygon->t2); } #endif #if RND_POLY_STATS rndNumberPolys++; rndNumberTextured++; rndNumberSmoothed++; #endif //RND_POLY_STATS break; default: #if MESH_ERROR_CHECKING dbgFatalf(DBG_Loc, "meshRender: invalid meshPolyMode: 0x%x", meshPolyMode); #endif break; } polygon++; } glEnd(); //done drawing these triangles glShadeModel(GL_SMOOTH); if (!usingShader) glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); if (enableBlend) { glDisable(GL_BLEND); } if (g_WireframeHack) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); rndLightingEnable(lightOn); if (enableBlend) { glDisable(GL_LINE_SMOOTH); } } } void meshEnableVertexArrays(void* vlist, sdword first, sdword count) { glVertexPointer(3, GL_FLOAT, 4*sizeof(GLfloat), vlist); glEnableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_EDGE_FLAG_ARRAY); glDisableClientState(GL_INDEX_ARRAY); if (glCapFeatureExists(GL_COMPILED_ARRAYS_EXT)) { glLockArraysEXT(first, count); } } void meshDisableVertexArrays(void) { glDisableClientState(GL_VERTEX_ARRAY); if (glCapFeatureExists(GL_COMPILED_ARRAYS_EXT)) { glUnlockArraysEXT(); } } /*----------------------------------------------------------------------------- Name : meshObjectRenderLit Description : this mesh renderer performs lighting operations itself as a workaround for buggy or slow GL impl's. uses ArrayElement for vertices, & will compiled arrays Inputs : ... Outputs : Return : Assert : lighting is enabled ----------------------------------------------------------------------------*/ void meshObjectRenderLit(polygonobject *object, materialentry *materials, sdword iColorScheme) { sdword iPoly; vertexentry *vertexList; polyentry *polygon; normalentry *normal, *normalList; sdword currentMaterial = -1; GLenum mode = GL_SMOOTH; glShadeModel(mode); rndLightingEnable(FALSE); vertexList = object->pVertexList; //get base of vertex list normalList = object->pNormalList; //get base of normal list polygon = object->pPolygonList; //get first polygon list entry alodIncPolys(object->nPolygons); meshEnableVertexArrays((void*)vertexList, 0, object->nVertices); glBegin(GL_TRIANGLES); //prepare to draw triangles for (iPoly = 0; iPoly < object->nPolygons; iPoly++) { #if MESH_ANAL_CHECKING //validate vertex indices dbgAssert(polygon->iV0 < object->nVertices); dbgAssert(polygon->iV1 < object->nVertices); dbgAssert(polygon->iV2 < object->nVertices); #endif if (polygon->iMaterial != currentMaterial) { //if a new material glEnd(); //end current polygon run currentMaterial = polygon->iMaterial; //remember current material meshCurrentMaterial(&materials[currentMaterial], iColorScheme);//set new material if (bFade) { glEnable(GL_BLEND); } glBegin(GL_TRIANGLES); //start new run #if MESH_MATERIAL_STATS nMaterialChanges++; //record material stats iMaterialMax = max(currentMaterial, iMaterialMax); #endif //MESH_MATERIAL_STATS } switch (meshPolyMode) { case MPM_Flat: dbgAssert(polygon->iFaceNormal != UWORD_Max); normal = &normalList[polygon->iFaceNormal]; meshColour(normal); //shader glArrayElement(polygon->iV0); glArrayElement(polygon->iV1); glArrayElement(polygon->iV2); #if RND_POLY_STATS rndNumberPolys++; #endif //RND_POLY_STATS break; case MPM_Texture: dbgAssert(polygon->iFaceNormal != UWORD_Max); normal = &normalList[polygon->iFaceNormal]; meshColour(normal); //shader glTexCoord2f(polygon->s0, polygon->t0); glArrayElement(polygon->iV0); glTexCoord2f(polygon->s1, polygon->t1); glArrayElement(polygon->iV1); glTexCoord2f(polygon->s2, polygon->t2); glArrayElement(polygon->iV2); #if RND_POLY_STATS rndNumberPolys++; rndNumberTextured++; #endif //RND_POLY_STATS break; case MPM_Smooth: dbgAssert(vertexList[polygon->iV0].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV0].iVertexNormal]; meshColour(normal); //shader glArrayElement(polygon->iV0); dbgAssert(vertexList[polygon->iV1].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV1].iVertexNormal]; meshColour(normal); //shader glArrayElement(polygon->iV1); dbgAssert(vertexList[polygon->iV2].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV2].iVertexNormal]; meshColour(normal); //shader glArrayElement(polygon->iV2); #if RND_POLY_STATS rndNumberPolys++; rndNumberSmoothed++; #endif //RND_POLY_STATS break; case MPM_SmoothTexture: dbgAssert(vertexList[polygon->iV0].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV0].iVertexNormal]; meshColour(normal); //shader glTexCoord2f(polygon->s0, polygon->t0); glArrayElement(polygon->iV0); dbgAssert(vertexList[polygon->iV1].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV1].iVertexNormal]; meshColour(normal); //shader glTexCoord2f(polygon->s1, polygon->t1); glArrayElement(polygon->iV1); dbgAssert(vertexList[polygon->iV2].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV2].iVertexNormal]; meshColour(normal); //shader glTexCoord2f(polygon->s2, polygon->t2); glArrayElement(polygon->iV2); #if MESH_SURFACE_NAME_DEBUG if (gTestTexture) { dbgMessagef("\n(%4.2f, %4.2f, %4.2f) => (%4.2f, %4.2f)", vertexList[polygon->iV0].x, vertexList[polygon->iV0].y, vertexList[polygon->iV0].z, polygon->s0, polygon->t0); dbgMessagef("\n(%4.2f, %4.2f, %4.2f) => (%4.2f, %4.2f)", vertexList[polygon->iV1].x, vertexList[polygon->iV1].y, vertexList[polygon->iV1].z, polygon->s1, polygon->t1); dbgMessagef("\n(%4.2f, %4.2f, %4.2f) => (%4.2f, %4.2f)", vertexList[polygon->iV2].x, vertexList[polygon->iV2].y, vertexList[polygon->iV2].z, polygon->s2, polygon->t2); } #endif #if RND_POLY_STATS rndNumberPolys++; rndNumberTextured++; rndNumberSmoothed++; #endif //RND_POLY_STATS break; default: #if MESH_ERROR_CHECKING dbgFatalf(DBG_Loc, "meshRender: invalid meshPolyMode: 0x%x", meshPolyMode); #endif break; } polygon++; } glEnd(); //done drawing these triangles meshDisableVertexArrays(); glShadeModel(GL_SMOOTH); if (!usingShader) glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); if (bFade) { glDisable(GL_BLEND); } rndLightingEnable(TRUE); //shader } /*----------------------------------------------------------------------------- Name : meshObjectRenderLitRGL Description : this mesh renderer performs lighting operations itself as a workaround for buggy or slow GL impl's Inputs : ... Outputs : Return : Assert : lighting is enabled ----------------------------------------------------------------------------*/ void meshObjectRenderLitRGL(polygonobject *object, materialentry *materials, sdword iColorScheme) { sdword iPoly; vertexentry *vertexList; polyentry *polygon; normalentry *normal, *normalList; sdword currentMaterial = -1; GLenum mode = GL_SMOOTH; glShadeModel(mode); rndLightingEnable(FALSE); vertexList = object->pVertexList; //get base of vertex list normalList = object->pNormalList; //get base of normal list polygon = object->pPolygonList; //get first polygon list entry alodIncPolys(object->nPolygons); glBegin(GL_TRIANGLES); //prepare to draw triangles for (iPoly = 0; iPoly < object->nPolygons; iPoly++) { #if MESH_ANAL_CHECKING //validate vertex indices dbgAssert(polygon->iV0 < object->nVertices); dbgAssert(polygon->iV1 < object->nVertices); dbgAssert(polygon->iV2 < object->nVertices); #endif if (polygon->iMaterial != currentMaterial) { //if a new material glEnd(); //end current polygon run currentMaterial = polygon->iMaterial; //remember current material meshCurrentMaterial(&materials[currentMaterial], iColorScheme);//set new material if (bFade) { glEnable(GL_BLEND); } glBegin(GL_TRIANGLES); //start new run #if MESH_MATERIAL_STATS nMaterialChanges++; //record material stats iMaterialMax = max(currentMaterial, iMaterialMax); #endif //MESH_MATERIAL_STATS } switch (meshPolyMode) { case MPM_Flat: dbgAssert(polygon->iFaceNormal != UWORD_Max); normal = &normalList[polygon->iFaceNormal]; meshColour(normal); //shader glVertex3fv((GLfloat*)&vertexList[polygon->iV0]); glVertex3fv((GLfloat*)&vertexList[polygon->iV1]); glVertex3fv((GLfloat*)&vertexList[polygon->iV2]); #if RND_POLY_STATS rndNumberPolys++; #endif //RND_POLY_STATS break; case MPM_Texture: dbgAssert(polygon->iFaceNormal != UWORD_Max); normal = &normalList[polygon->iFaceNormal]; meshColour(normal); //shader glTexCoord2f(polygon->s0, polygon->t0); glVertex3fv((GLfloat*)&vertexList[polygon->iV0]); glTexCoord2f(polygon->s1, polygon->t1); glVertex3fv((GLfloat*)&vertexList[polygon->iV1]); glTexCoord2f(polygon->s2, polygon->t2); glVertex3fv((GLfloat*)&vertexList[polygon->iV2]); #if RND_POLY_STATS rndNumberPolys++; rndNumberTextured++; #endif //RND_POLY_STATS break; case MPM_Smooth: dbgAssert(vertexList[polygon->iV0].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV0].iVertexNormal]; meshColour(normal); //shader glVertex3fv((GLfloat*)&vertexList[polygon->iV0]); dbgAssert(vertexList[polygon->iV1].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV1].iVertexNormal]; meshColour(normal); //shader glVertex3fv((GLfloat*)&vertexList[polygon->iV1]); dbgAssert(vertexList[polygon->iV2].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV2].iVertexNormal]; meshColour(normal); //shader glVertex3fv((GLfloat*)&vertexList[polygon->iV2]); #if RND_POLY_STATS rndNumberPolys++; rndNumberSmoothed++; #endif //RND_POLY_STATS break; case MPM_SmoothTexture: dbgAssert(vertexList[polygon->iV0].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV0].iVertexNormal]; meshColour(normal); //shader glTexCoord2f(polygon->s0, polygon->t0); glVertex3fv((GLfloat*)&vertexList[polygon->iV0]); dbgAssert(vertexList[polygon->iV1].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV1].iVertexNormal]; meshColour(normal); //shader glTexCoord2f(polygon->s1, polygon->t1); glVertex3fv((GLfloat*)&vertexList[polygon->iV1]); dbgAssert(vertexList[polygon->iV2].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV2].iVertexNormal]; meshColour(normal); //shader glTexCoord2f(polygon->s2, polygon->t2); glVertex3fv((GLfloat*)&vertexList[polygon->iV2]); #if MESH_SURFACE_NAME_DEBUG if (gTestTexture) { dbgMessagef("\n(%4.2f, %4.2f, %4.2f) => (%4.2f, %4.2f)", vertexList[polygon->iV0].x, vertexList[polygon->iV0].y, vertexList[polygon->iV0].z, polygon->s0, polygon->t0); dbgMessagef("\n(%4.2f, %4.2f, %4.2f) => (%4.2f, %4.2f)", vertexList[polygon->iV1].x, vertexList[polygon->iV1].y, vertexList[polygon->iV1].z, polygon->s1, polygon->t1); dbgMessagef("\n(%4.2f, %4.2f, %4.2f) => (%4.2f, %4.2f)", vertexList[polygon->iV2].x, vertexList[polygon->iV2].y, vertexList[polygon->iV2].z, polygon->s2, polygon->t2); } #endif #if RND_POLY_STATS rndNumberPolys++; rndNumberTextured++; rndNumberSmoothed++; #endif //RND_POLY_STATS break; default: #if MESH_ERROR_CHECKING dbgFatalf(DBG_Loc, "meshRender: invalid meshPolyMode: 0x%x", meshPolyMode); #endif break; } polygon++; } glEnd(); //done drawing these triangles glShadeModel(GL_SMOOTH); if (!usingShader) glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); if (bFade) { glDisable(GL_BLEND); } rndLightingEnable(TRUE); //shader } /*----------------------------------------------------------------------------- Name : meshObjectRenderLitRGLvx Description : rGL version of meshObjectRenderLit that takes advantage of rGL's vertex arrays which are decoupled from other client state Inputs : ... Outputs : Return : ----------------------------------------------------------------------------*/ void meshObjectRenderLitRGLvx(polygonobject *object, materialentry *materials, sdword iColorScheme) { sdword iPoly; vertexentry *vertexList; polyentry *polygon; normalentry *normal, *normalList; sdword currentMaterial = -1; GLenum mode = GL_SMOOTH; hmatrix modelview, projection; //load identity matrices into modelview, projection glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&projection); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelview); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //tell rGL to reuse our buffers and such rglEnable(RGL_RASTERIZE_ONLY); glShadeModel(mode); rndLightingEnable(FALSE); vertexList = object->pVertexList; //get base of vertex list //possibly expand the size of the vertex pool transGrowVertexLists((object->nVertices + 3) & (~3)); if (transPerspectiveMatrix(&projection)) { transTransformCompletely( object->nVertices, clipVertexList, eyeVertexList, vertexList, &modelview, &projection); } else { //object -> eye transTransformVertexList(object->nVertices, eyeVertexList, vertexList, &modelview); //eye -> clip transGeneralPerspectiveTransform(object->nVertices, clipVertexList, eyeVertexList, &projection); } //use our vertex buffer glVertexPointer(4, GL_FLOAT, 0, (void*)clipVertexList); glEnableClientState(GL_VERTEX_ARRAY); normalList = object->pNormalList; //get base of normal list polygon = object->pPolygonList; //get first polygon list entry //increase the number of polys the autolod module has seen alodIncPolys(object->nPolygons); glBegin(GL_TRIANGLES); //prepare to draw triangles for (iPoly = 0; iPoly < object->nPolygons; iPoly++) { #if MESH_ANAL_CHECKING //validate vertex indices dbgAssert(polygon->iV0 < object->nVertices); dbgAssert(polygon->iV1 < object->nVertices); dbgAssert(polygon->iV2 < object->nVertices); #endif if (polygon->iMaterial != currentMaterial) { //if a new material glEnd(); //end current polygon run currentMaterial = polygon->iMaterial; //remember current material meshCurrentMaterial(&materials[currentMaterial], iColorScheme);//set new material if (bFade) { glEnable(GL_BLEND); } glBegin(GL_TRIANGLES); //start new run #if MESH_MATERIAL_STATS nMaterialChanges++; //record material stats iMaterialMax = max(currentMaterial, iMaterialMax); #endif //MESH_MATERIAL_STATS } switch (meshPolyMode) { case MPM_Flat: dbgAssert(polygon->iFaceNormal != UWORD_Max); normal = &normalList[polygon->iFaceNormal]; meshColour(normal); glArrayElement(polygon->iV0); glArrayElement(polygon->iV1); glArrayElement(polygon->iV2); #if RND_POLY_STATS rndNumberPolys++; #endif //RND_POLY_STATS break; case MPM_Texture: dbgAssert(polygon->iFaceNormal != UWORD_Max); normal = &normalList[polygon->iFaceNormal]; meshColour(normal); glTexCoord2f(polygon->s0, polygon->t0); glArrayElement(polygon->iV0); glTexCoord2f(polygon->s1, polygon->t1); glArrayElement(polygon->iV1); glTexCoord2f(polygon->s2, polygon->t2); glArrayElement(polygon->iV2); #if RND_POLY_STATS rndNumberPolys++; rndNumberTextured++; #endif //RND_POLY_STATS break; case MPM_Smooth: dbgAssert(vertexList[polygon->iV0].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV0].iVertexNormal]; meshColour(normal); glArrayElement(polygon->iV0); dbgAssert(vertexList[polygon->iV1].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV1].iVertexNormal]; meshColour(normal); glArrayElement(polygon->iV1); dbgAssert(vertexList[polygon->iV2].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV2].iVertexNormal]; meshColour(normal); glArrayElement(polygon->iV2); #if RND_POLY_STATS rndNumberPolys++; rndNumberSmoothed++; #endif //RND_POLY_STATS break; case MPM_SmoothTexture: dbgAssert(vertexList[polygon->iV0].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV0].iVertexNormal]; meshColour(normal); glTexCoord2f(polygon->s0, polygon->t0); glArrayElement(polygon->iV0); dbgAssert(vertexList[polygon->iV1].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV1].iVertexNormal]; meshColour(normal); glTexCoord2f(polygon->s1, polygon->t1); glArrayElement(polygon->iV1); dbgAssert(vertexList[polygon->iV2].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV2].iVertexNormal]; meshColour(normal); glTexCoord2f(polygon->s2, polygon->t2); glArrayElement(polygon->iV2); #if MESH_SURFACE_NAME_DEBUG if (gTestTexture) { dbgMessagef("\n(%4.2f, %4.2f, %4.2f) => (%4.2f, %4.2f)", vertexList[polygon->iV0].x, vertexList[polygon->iV0].y, vertexList[polygon->iV0].z, polygon->s0, polygon->t0); dbgMessagef("\n(%4.2f, %4.2f, %4.2f) => (%4.2f, %4.2f)", vertexList[polygon->iV1].x, vertexList[polygon->iV1].y, vertexList[polygon->iV1].z, polygon->s1, polygon->t1); dbgMessagef("\n(%4.2f, %4.2f, %4.2f) => (%4.2f, %4.2f)", vertexList[polygon->iV2].x, vertexList[polygon->iV2].y, vertexList[polygon->iV2].z, polygon->s2, polygon->t2); } #endif #if RND_POLY_STATS rndNumberPolys++; rndNumberTextured++; rndNumberSmoothed++; #endif //RND_POLY_STATS break; default: #if MESH_ERROR_CHECKING dbgFatalf(DBG_Loc, "meshRender: invalid meshPolyMode: 0x%x", meshPolyMode); #endif break; } polygon++; } glEnd(); //done drawing these triangles glShadeModel(GL_SMOOTH); if (!usingShader) glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); if (bFade) { glDisable(GL_BLEND); } rndLightingEnable(TRUE); //reload projection matrix glMatrixMode(GL_PROJECTION); glLoadMatrixf((GLfloat*)&projection); //ASSERT: modelview is always popped after a meshObjectRender glMatrixMode(GL_MODELVIEW); //reset state rglDisable(RGL_RASTERIZE_ONLY); glDisableClientState(GL_VERTEX_ARRAY); } /*----------------------------------------------------------------------------- Name : meshSpecObjectRender Description : renders a mesh with specular-only shading. to be used with renderers that don't support specular modes in their internal pipeline (ie. !rGL) Inputs : .. Outputs : Return : ----------------------------------------------------------------------------*/ void meshSpecObjectRender(polygonobject *object, materialentry *materials, sdword iColorScheme) { sdword iPoly; vertexentry *vertexList; polyentry *polygon; normalentry *normal, *normalList; sdword currentMaterial = -1; GLenum mode = GL_SMOOTH; sdword lightOn; real32 modelview[16], modelviewInv[16]; lightOn = rndLightingEnable(FALSE); glGetFloatv(GL_MODELVIEW_MATRIX, modelview); shInvertMatrix(modelviewInv, modelview); glShadeModel(mode); alodIncPolys(object->nPolygons); vertexList = object->pVertexList; //get base of vertex list normalList = object->pNormalList; //get base of normal list polygon = object->pPolygonList; //get first polygon list entry glBegin(GL_TRIANGLES); //prepare to draw triangles for (iPoly = 0; iPoly < object->nPolygons; iPoly++) { #if MESH_ANAL_CHECKING //validate vertex indices dbgAssert(polygon->iV0 < object->nVertices); dbgAssert(polygon->iV1 < object->nVertices); dbgAssert(polygon->iV2 < object->nVertices); #endif if (polygon->iMaterial != currentMaterial) { //if a new material glEnd(); //end current polygon run currentMaterial = polygon->iMaterial; //remember current material meshCurrentMaterial(&materials[currentMaterial], iColorScheme);//set new material glBegin(GL_TRIANGLES); //start new run #if MESH_MATERIAL_STATS nMaterialChanges++; //record material stats iMaterialMax = max(currentMaterial, iMaterialMax); #endif //MESH_MATERIAL_STATS } switch (meshPolyMode) { case MPM_Flat: dbgAssert(polygon->iFaceNormal != UWORD_Max); normal = &normalList[polygon->iFaceNormal]; meshSpecColour(normal, &vertexList[polygon->iV0], modelview, modelviewInv); glVertex3fv((GLfloat*)&vertexList[polygon->iV0]); meshSpecColour(normal, &vertexList[polygon->iV1], modelview, modelviewInv); glVertex3fv((GLfloat*)&vertexList[polygon->iV1]); meshSpecColour(normal, &vertexList[polygon->iV2], modelview, modelviewInv); glVertex3fv((GLfloat*)&vertexList[polygon->iV2]); #if RND_POLY_STATS rndNumberPolys++; #endif //RND_POLY_STATS break; case MPM_Texture: dbgAssert(polygon->iFaceNormal != UWORD_Max); normal = &normalList[polygon->iFaceNormal]; glTexCoord2f(polygon->s0, polygon->t0); meshSpecColour(normal, &vertexList[polygon->iV0], modelview, modelviewInv); glVertex3fv((GLfloat*)&vertexList[polygon->iV0]); glTexCoord2f(polygon->s1, polygon->t1); meshSpecColour(normal, &vertexList[polygon->iV1], modelview, modelviewInv); glVertex3fv((GLfloat*)&vertexList[polygon->iV1]); glTexCoord2f(polygon->s2, polygon->t2); meshSpecColour(normal, &vertexList[polygon->iV2], modelview, modelviewInv); glVertex3fv((GLfloat*)&vertexList[polygon->iV2]); #if RND_POLY_STATS rndNumberPolys++; rndNumberTextured++; #endif //RND_POLY_STATS break; case MPM_Smooth: dbgAssert(vertexList[polygon->iV0].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV0].iVertexNormal]; meshSpecColour(normal, &vertexList[polygon->iV0], modelview, modelviewInv); glVertex3fv((GLfloat*)&vertexList[polygon->iV0]); dbgAssert(vertexList[polygon->iV1].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV1].iVertexNormal]; meshSpecColour(normal, &vertexList[polygon->iV1], modelview, modelviewInv); glVertex3fv((GLfloat*)&vertexList[polygon->iV1]); dbgAssert(vertexList[polygon->iV2].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV2].iVertexNormal]; meshSpecColour(normal, &vertexList[polygon->iV2], modelview, modelviewInv); glVertex3fv((GLfloat*)&vertexList[polygon->iV2]); #if RND_POLY_STATS rndNumberPolys++; rndNumberSmoothed++; #endif //RND_POLY_STATS break; case MPM_SmoothTexture: dbgAssert(vertexList[polygon->iV0].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV0].iVertexNormal]; glTexCoord2f(polygon->s0, polygon->t0); meshSpecColour(normal, &vertexList[polygon->iV0], modelview, modelviewInv); glVertex3fv((GLfloat*)&vertexList[polygon->iV0]); dbgAssert(vertexList[polygon->iV1].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV1].iVertexNormal]; glTexCoord2f(polygon->s1, polygon->t1); meshSpecColour(normal, &vertexList[polygon->iV1], modelview, modelviewInv); glVertex3fv((GLfloat*)&vertexList[polygon->iV1]); dbgAssert(vertexList[polygon->iV2].iVertexNormal != UWORD_Max); normal = &normalList[vertexList[polygon->iV2].iVertexNormal]; glTexCoord2f(polygon->s2, polygon->t2); meshSpecColour(normal, &vertexList[polygon->iV2], modelview, modelviewInv); glVertex3fv((GLfloat*)&vertexList[polygon->iV2]); #if RND_POLY_STATS rndNumberPolys++; rndNumberTextured++; rndNumberSmoothed++; #endif //RND_POLY_STATS break; default: #if MESH_ERROR_CHECKING dbgFatalf(DBG_Loc, "meshRender: invalid meshPolyMode: 0x%x", meshPolyMode); #endif break; } polygon++; } glEnd(); //done drawing these triangles glShadeModel(GL_SMOOTH); if (!usingShader) glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); rndLightingEnable(lightOn); } /*----------------------------------------------------------------------------- Name : meshLerpVerts Description : Helper for morphed object rendering. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void meshLerpVerts(vector* c, vertexentry* a, vertexentry* b, real32 frac) { real32 ofrac; ofrac = 1.0f - frac; c->x = ofrac * a->x + frac * b->x; c->y = ofrac * a->y + frac * b->y; c->z = ofrac * a->z + frac * b->z; } /*----------------------------------------------------------------------------- Name : meshLerpNormal Description : Helper for morphed object rendering. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void meshLerpNormal(vector* c, normalentry* a, normalentry* b, real32 frac) { real32 ofrac; ofrac = 1.0f - frac; c->x = ofrac * a->x + frac * b->x; c->y = ofrac * a->y + frac * b->y; c->z = ofrac * a->z + frac * b->z; } /*----------------------------------------------------------------------------- Name : meshMorphedObjectRender Description : Render a mesh object morphed between two endpoint objects Inputs : object1, object2 - objects being lerped between uvPolys - polygons to get the u/v coordinates from. Must be exactly the same as the polygon objects. materials - material list for this object. frac - how much of object2 to use. iColorScheme - color scheme to render in, if any Outputs : Return : Note : use object1's stuff, but lerp to object2 ----------------------------------------------------------------------------*/ void meshMorphedObjectRender( polygonobject* object1, polygonobject* object2, polyentry *uvPolys, materialentry* materials, real32 frac, sdword iColorScheme) { sdword iPoly; vertexentry* vertexList1; vertexentry* vertexList2; polyentry* polygon; polyentry* uvPolygon; normalentry* normalList1; normalentry* normalList2; sdword currentMaterial = -1; vector vert0, vert1, vert2, normal; GLenum mode = GL_SMOOTH; #if MESH_MORPH_DEBUG sdword colorIndex = 0; color morphLineColor1, morphLineColor2; GLboolean texEnabled, lightEnabled, blendEnabled; #endif bool lightOn; real32 modelview[16], modelviewInv[16]; dbgAssert(object1->nVertices == object2->nVertices); dbgAssert(object1->nPolygons == object2->nPolygons); glShadeModel(mode); if (g_SpecHack) { lightOn = rndLightingEnable(FALSE); glGetFloatv(GL_MODELVIEW_MATRIX, modelview); shInvertMatrix(modelviewInv, modelview); } alodIncPolys(object1->nPolygons); vertexList1 = object1->pVertexList; vertexList2 = object2->pVertexList; normalList1 = object1->pNormalList; normalList2 = object2->pNormalList; polygon = object1->pPolygonList; uvPolygon = uvPolys; glBegin(GL_TRIANGLES); for (iPoly = 0; iPoly < object1->nPolygons; iPoly++) { #if MESH_ANAL_CHECKING dbgAssert(polygon->iV0 < object1->nVertices); dbgAssert(polygon->iV1 < object1->nVertices); dbgAssert(polygon->iV2 < object1->nVertices); #endif if (polygon->iMaterial != currentMaterial) { glEnd(); currentMaterial = polygon->iMaterial; meshCurrentMaterial(&materials[currentMaterial], iColorScheme); glBegin(GL_TRIANGLES); #if MESH_MATERIAL_STATS nMaterialChanges++; iMaterialMax = max(currentMaterial, iMaterialMax); #endif //MESH_MATERIAL_STATS } meshLerpVerts(&vert0, &vertexList1[polygon->iV0], &vertexList2[polygon->iV0], frac); meshLerpVerts(&vert1, &vertexList1[polygon->iV1], &vertexList2[polygon->iV1], frac); meshLerpVerts(&vert2, &vertexList1[polygon->iV2], &vertexList2[polygon->iV2], frac); #if MESH_MORPH_DEBUG //morphed mesh debug code if (meshMorphDebug) { //disable a bunch of stuff glEnd(); texEnabled = glIsEnabled(GL_TEXTURE_2D); lightEnabled = glIsEnabled(GL_LIGHTING); blendEnabled = glIsEnabled(GL_BLEND); glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glDisable(GL_BLEND); //draw outlines of endpoint meshes morphLineColor1 = meshMorphLineColors[colorIndex].from; morphLineColor2 = meshMorphLineColors[colorIndex].to; colorIndex++; //choose a color for the line if (meshMorphLineColors[colorIndex].from == colBlack) { colorIndex = 0; } if (!keyIsHit(ONEKEY)) { primLine3((vector *)&vertexList1[polygon->iV0], (vector *)&vertexList1[polygon->iV1], morphLineColor2); } if (!keyIsHit(TWOKEY)) { primLine3((vector *)&vertexList2[polygon->iV0], (vector *)&vertexList2[polygon->iV1], morphLineColor1); } morphLineColor1 = meshMorphLineColors[colorIndex].from; morphLineColor2 = meshMorphLineColors[colorIndex].to; colorIndex++; //choose a color for the line if (meshMorphLineColors[colorIndex].from == colBlack) { colorIndex = 0; } if (!keyIsHit(ONEKEY)) { primLine3((vector *)&vertexList1[polygon->iV1], (vector *)&vertexList1[polygon->iV2], morphLineColor2); } if (!keyIsHit(TWOKEY)) { primLine3((vector *)&vertexList2[polygon->iV1], (vector *)&vertexList2[polygon->iV2], morphLineColor1); } morphLineColor1 = meshMorphLineColors[colorIndex].from; morphLineColor2 = meshMorphLineColors[colorIndex].to; colorIndex++; //choose a color for the line if (meshMorphLineColors[colorIndex].from == colBlack) { colorIndex = 0; } if (!keyIsHit(ONEKEY)) { primLine3((vector *)&vertexList1[polygon->iV2], (vector *)&vertexList1[polygon->iV0], morphLineColor2); } if (!keyIsHit(TWOKEY)) { primLine3((vector *)&vertexList2[polygon->iV2], (vector *)&vertexList2[polygon->iV0], morphLineColor1); } //draw the connecting lines glEnable(GL_LINE_STIPPLE); glLineStipple(1, 0x5a5a); if (!keyIsHit(THREEKEY)) { primLine3((vector *)&vertexList1[polygon->iV0], (vector *)&vertexList2[polygon->iV0], colWhite); primLine3((vector *)&vertexList1[polygon->iV1], (vector *)&vertexList2[polygon->iV1], colWhite); primLine3((vector *)&vertexList1[polygon->iV2], (vector *)&vertexList2[polygon->iV2], colWhite); } glDisable(GL_LINE_STIPPLE); if (!keyIsHit(FOURKEY)) { primLine3(&vert0, &vert1, colRGB(128, 128, 128)); primLine3(&vert1, &vert2, colRGB(128, 128, 128)); primLine3(&vert2, &vert0, colRGB(128, 128, 128)); } if (texEnabled) { glEnable(GL_TEXTURE_2D); } if (texEnabled) { glEnable(GL_LIGHTING); } if (blendEnabled) { glEnable(GL_BLEND); } glBegin(GL_TRIANGLES); } #endif //MESH_MORPH_DEBUG switch (meshPolyMode) { case MPM_Flat: dbgAssert(polygon->iFaceNormal != UWORD_Max); if (mode != GL_FLAT) { mode = GL_FLAT; glShadeModel(mode); } meshLerpNormal(&normal, &normalList1[polygon->iFaceNormal], &normalList2[polygon->iFaceNormal], frac); if (g_SpecHack) meshMorphedSpecColour(&normal, modelview, modelviewInv); glNormal3f(normal.x, normal.y, normal.z); glVertex3fv((GLfloat*)&vert0); glVertex3fv((GLfloat*)&vert1); glVertex3fv((GLfloat*)&vert2); #if RND_POLY_STATS rndNumberPolys++; #endif //RND_POLY_STATS break; case MPM_Texture: dbgAssert(polygon->iFaceNormal != UWORD_Max); if (mode != GL_FLAT) { mode = GL_FLAT; glShadeModel(mode); } meshLerpNormal(&normal, &normalList1[polygon->iFaceNormal], &normalList2[polygon->iFaceNormal], frac); if (g_SpecHack) meshMorphedSpecColour(&normal, modelview, modelviewInv); glNormal3f(normal.x, normal.y, normal.z); glTexCoord2f(uvPolygon->s0, uvPolygon->t0); glVertex3fv((GLfloat*)&vert0); glTexCoord2f(uvPolygon->s1, uvPolygon->t1); glVertex3fv((GLfloat*)&vert1); glTexCoord2f(uvPolygon->s2, uvPolygon->t2); glVertex3fv((GLfloat*)&vert2); #if RND_POLY_STATS rndNumberPolys++; rndNumberTextured++; #endif //RND_POLY_STATS break; case MPM_Smooth: dbgAssert(vertexList1[polygon->iV0].iVertexNormal != UWORD_Max); if (mode != GL_SMOOTH) { mode = GL_SMOOTH; glShadeModel(mode); } meshLerpNormal(&normal, &normalList1[vertexList1[polygon->iV0].iVertexNormal], &normalList2[vertexList2[polygon->iV0].iVertexNormal], frac); if (g_SpecHack) meshMorphedSpecColour(&normal, modelview, modelviewInv); glNormal3f(normal.x, normal.y, normal.z); glVertex3fv((GLfloat*)&vert0); dbgAssert(vertexList1[polygon->iV1].iVertexNormal != UWORD_Max); meshLerpNormal(&normal, &normalList1[vertexList1[polygon->iV1].iVertexNormal], &normalList2[vertexList2[polygon->iV1].iVertexNormal], frac); if (g_SpecHack) meshMorphedSpecColour(&normal, modelview, modelviewInv); glNormal3f(normal.x, normal.y, normal.z); glVertex3fv((GLfloat*)&vert1); dbgAssert(vertexList1[polygon->iV2].iVertexNormal != UWORD_Max); meshLerpNormal(&normal, &normalList1[vertexList1[polygon->iV2].iVertexNormal], &normalList2[vertexList2[polygon->iV2].iVertexNormal], frac); if (g_SpecHack) meshMorphedSpecColour(&normal, modelview, modelviewInv); glNormal3f(normal.x, normal.y, normal.z); glVertex3fv((GLfloat*)&vert2); #if RND_POLY_STATS rndNumberPolys++; rndNumberSmoothed++; #endif //RND_POLY_STATS break; case MPM_SmoothTexture: if (mode != GL_SMOOTH) { mode = GL_SMOOTH; glShadeModel(mode); } dbgAssert(vertexList1[polygon->iV0].iVertexNormal != UWORD_Max); meshLerpNormal(&normal, &normalList1[vertexList1[polygon->iV0].iVertexNormal], &normalList2[vertexList2[polygon->iV0].iVertexNormal], frac); if (g_SpecHack) meshMorphedSpecColour(&normal, modelview, modelviewInv); glNormal3f(normal.x, normal.y, normal.z); glTexCoord2f(uvPolygon->s0, uvPolygon->t0); glVertex3fv((GLfloat*)&vert0); dbgAssert(vertexList1[polygon->iV1].iVertexNormal != UWORD_Max); meshLerpNormal(&normal, &normalList1[vertexList1[polygon->iV1].iVertexNormal], &normalList2[vertexList2[polygon->iV1].iVertexNormal], frac); if (g_SpecHack) meshMorphedSpecColour(&normal, modelview, modelviewInv); glNormal3f(normal.x, normal.y, normal.z); glTexCoord2f(uvPolygon->s1, uvPolygon->t1); glVertex3fv((GLfloat*)&vert1); dbgAssert(vertexList1[polygon->iV2].iVertexNormal != UWORD_Max); meshLerpNormal(&normal, &normalList1[vertexList1[polygon->iV2].iVertexNormal], &normalList2[vertexList2[polygon->iV2].iVertexNormal], frac); if (g_SpecHack) meshMorphedSpecColour(&normal, modelview, modelviewInv); glNormal3f(normal.x, normal.y, normal.z); glTexCoord2f(uvPolygon->s2, uvPolygon->t2); glVertex3fv((GLfloat*)&vert2); #if RND_POLY_STATS rndNumberPolys++; rndNumberTextured++; rndNumberSmoothed++; #endif //RND_POLY_STATS break; default: #if MESH_ERROR_CHECKING dbgFatalf(DBG_Loc, "meshRender: invalid meshPolyMode: 0x%x", meshPolyMode); #endif break; } polygon++; uvPolygon++; } glEnd(); if (g_SpecHack) { rndLightingEnable(lightOn); } glShadeModel(GL_SMOOTH); if (!usingShader) glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); } /*----------------------------------------------------------------------------- Name : meshMorphedObjectRenderTex Description : Render a morphed mesh object, lerping between two meshes with a single texture map. Inputs : object1, object2 - objects being lerped between uvPolys - polygons to get the u/v coordinates from. Must be exactly the same as the polygon objects. materials - material list for this object. frac - how much of object2 to use. iColorScheme - color scheme to render in, or 0 if none. Outputs : Return : void ----------------------------------------------------------------------------*/ void meshMorphedObjectRenderTex(polygonobject* object1, polygonobject* object2, polyentry *uvPolys, materialentry* material, real32 frac, sdword iColorScheme) { meshCurrentMaterial = meshCurrentMaterialTex; meshMorphedObjectRender(object1, object2, uvPolys, material, frac, 0); meshCurrentMaterial = meshCurrentMaterialDefault; } /*----------------------------------------------------------------------------- Name : meshObjectRenderTex Description : Renders an object using a single texture map for the entire surface Inputs : object - object to render material - materials to use (surface attribs will be interpreted properly) Outputs : Return : void ----------------------------------------------------------------------------*/ void meshObjectRenderTex(polygonobject *object, materialentry *material) { meshCurrentMaterial = meshCurrentMaterialTex; meshObjectRender(object, material, 0); meshCurrentMaterial = meshCurrentMaterialDefault; } /*----------------------------------------------------------------------------- Name : meshObjectRenderR Description : Render a single polygon object and all it's 'offspring' objects. Inputs : object - object to render. materials - material list to use in rendering iColorScheme - index of player associated with this mesh (for texture coloring) or 0 for no player. Outputs : renders the object and all it's daughter objects. Return : void ----------------------------------------------------------------------------*/ void meshObjectRenderR(polygonobject *object, materialentry *materials, sdword iColorScheme) { polygonobject *daughter; glPushMatrix(); glMultMatrixf((float *)&object->localMatrix); //set matrix for this object shPushLightMatrix(&object->localMatrix); for (daughter = object->pDaughter; daughter != NULL; daughter = daughter->pSister) { meshObjectRenderR(daughter, materials, iColorScheme); //render all daughter objects } MESH_OBJECT_RENDER(object, materials, iColorScheme) shPopLightMatrix(); glPopMatrix(); } /*----------------------------------------------------------------------------- Name : meshRender Description : Render the specified mesh Inputs : mesh - mesh to be rendered iColorScheme - index of player associated with this mesh (for texture coloring) or 0 for no player. Outputs : .. Return : ?? Note : This function does not even call any GL wrappers. ----------------------------------------------------------------------------*/ sdword meshRender(meshdata *mesh, sdword iColorScheme) { polygonobject *object; meshFixupPacoUV(mesh, texLinearFiltering); if (g_NoMatSwitch) { meshCurrentMaterial = meshAlmostCurrentMaterial; } else { meshCurrentMaterial = meshCurrentMaterialDefault; } #if MESH_MATERIAL_STATS nMaterialChanges = 0; iMaterialMax = 0; #endif //MESH_MATERIAL_STATS for (object = &mesh->object[0]; object != NULL; object = object->pSister) { meshObjectRenderR(object, mesh->localMaterial, iColorScheme); } #if MESH_MATERIAL_STATS meshMaterialChanges += nMaterialChanges; //record material stats meshTotalMaterials += iMaterialMax + 1; #endif //MESH_MATERIAL_STATS meshCurrentMaterial = meshCurrentMaterialDefault; return(0); } /*----------------------------------------------------------------------------- Name : meshObjectRenderHierarchy Description : Render a polygon object and all it's offspring Inputs : binding - base of a binding list which is updated and returned everything else - same as meshObjectRender Outputs : Return : ----------------------------------------------------------------------------*/ mhlocalbinding *meshObjectRenderHierarchy(mhlocalbinding *binding, polygonobject *object, materialentry *materials, sdword iColorScheme) { polygonobject *daughter; glPushMatrix(); if (binding->function != NULL) { binding->function(binding->flags, &object->localMatrix,//call matrix update function !!! called every frame &binding->matrix, binding->userData, binding->userID); glMultMatrixf((float *)&binding->matrix); //set matrix for this object shPushLightMatrix(&binding->matrix); } else { glMultMatrixf((float *)&object->localMatrix); //set matrix for this object shPushLightMatrix(&object->localMatrix); } binding++; for (daughter = object->pDaughter; daughter != NULL; daughter = daughter->pSister) { binding = meshObjectRenderHierarchy(binding, daughter, materials, iColorScheme); //render all daughter objects } MESH_OBJECT_RENDER(object, materials, iColorScheme) shPopLightMatrix(); glPopMatrix(); return(binding); } /*----------------------------------------------------------------------------- Name : meshRenderShipHierarchy Description : Render a specified LOD of a ship using it's hierarchy binding info. Inputs : ship - ship to render currentLOD - LOD to render at. mesh - mesh to render normally if a proper hierarchy cannot be found. Outputs : renders a ship Return : void ----------------------------------------------------------------------------*/ void meshRenderShipHierarchy(shipbindings *bindings, sdword currentLOD, meshdata *mesh, sdword iColorScheme) { polygonobject *object; mhlocalbinding *binding; //binding list for this LOD meshFixupPacoUV(mesh, texLinearFiltering); #if MESH_PRE_CALLBACK if (bindings->preCallback) { //call the pre callback bindings->preCallback(mesh, bindings, currentLOD); } #endif if (g_NoMatSwitch) { meshCurrentMaterial = meshAlmostCurrentMaterial; } else { meshCurrentMaterial = meshCurrentMaterialDefault; } #if MESH_MATERIAL_STATS nMaterialChanges = 0; iMaterialMax = 0; #endif //MESH_MATERIAL_STATS binding = bindings->localBinding[currentLOD]; for (object = &mesh->object[0]; object != NULL; object = object->pSister) { binding = meshObjectRenderHierarchy(binding, object, mesh->localMaterial, iColorScheme); } #if MESH_MATERIAL_STATS meshMaterialChanges += nMaterialChanges; //record material stats meshTotalMaterials += iMaterialMax + 1; #endif //MESH_MATERIAL_STATS if (bindings->postCallback) { //call the post callback bindings->postCallback(mesh, bindings, currentLOD); } meshCurrentMaterial = meshCurrentMaterialDefault; } /*----------------------------------------------------------------------------- Name : meshConcatByUserData Description : The rest of the recursion function of meshFindHierarchyMatrixByUserData Inputs : binding - starting binding pointer. object - current object in hierarchy. userData - data to compare against. Outputs : dest - where to store the concatenated matrix. Return : ----------------------------------------------------------------------------*/ mhbinding *meshConcatByUserData(hmatrix *dest, hmatrix *destNC, mhbinding *binding, polygonobject *object, void *userData) { polygonobject *daughter; hmatrix mat; hmatMultiplyHMatByHMat(&mat, dest, &object->localMatrix);//concatenate a matrix if (binding->userData == userData) { //if found user data we were looking for *dest = mat; *destNC = object->localMatrix; return(NULL); //stop looking } binding++; for (daughter = object->pDaughter; daughter != NULL; daughter = daughter->pSister) { //for all child objects binding = meshConcatByUserData(&mat, destNC, binding, daughter, userData);//concatenate all daughter objects if (binding == NULL) { //if that tree found it *dest = mat; return(NULL); } } return(binding); } /*----------------------------------------------------------------------------- Name : meshFindHierarchyMatrixByUserData Description : Create a concatenated matrix for the mesh hierarchy member with the specified user data pointer. Inputs : bindings - list of mesh bindings userData - userData pointer to compare against. Outputs : dest - where to store the concatenated matrix. Return : TRUE if found, FALSE otherwise ----------------------------------------------------------------------------*/ bool meshFindHierarchyMatrixByUserData(hmatrix *dest, hmatrix *destNC, mhbinding *binding, void *userData) { polygonobject *object; *dest = IdentityHMatrix; for (object = &binding->object[0]; object != NULL; object = object->pSister) { dbgAssert(binding->object == object); binding = meshConcatByUserData(dest, destNC, binding, object, userData); if (binding == NULL) { return(TRUE); } } return(FALSE); } /*----------------------------------------------------------------------------- Name : meshBindingListRecurse Description : Mesh-hierarchy recursion function to handle the creation of mesh hierarchy binding lists. Inputs : bindingList - list we're creating. index - current entry in this list to set object - current object we're setting Outputs : stores the a pointer to object and all it's children in the binding list in an order appropriate for rendering. Return : new index into the list ----------------------------------------------------------------------------*/ sdword meshBindingListRecurse(mhbinding *bindingList, sdword index, polygonobject *object) { polygonobject *daughter; bindingList[index].object = object; //save the link to this object bindingList[index].function = NULL; //NULL means no function index++; for (daughter = object->pDaughter; daughter != NULL; daughter = daughter->pSister) { //service all children first index = meshBindingListRecurse(bindingList, index, daughter);//render all daughter objects } return(index); } /*----------------------------------------------------------------------------- Name : meshBindingListCreate Description : Create a binding list for a mesh Inputs : mesh - pointer to mesh to create binding list for Outputs : Allocates and creates a binding list for the specified mesh. Only the polyobject *will be set in each binding structure. Order of structure reflects order to render in. Return : Pointer to new binding list. ----------------------------------------------------------------------------*/ mhbinding *meshBindingListCreate(meshdata *mesh) { sdword index = 0; polygonobject *sister; mhbinding *bindingList; bindingList = memAlloc(mesh->nPolygonObjects * sizeof(mhbinding), "MeshBindingList", NonVolatile); for (sister = &mesh->object[0]; sister != NULL; sister = sister->pSister) { //search all the base objects index = meshBindingListRecurse(bindingList, index, sister); } #if MESH_ERROR_CHECKING if (index != mesh->nPolygonObjects) { dbgFatalf(DBG_Loc, "index %d != mesh->nPolygonObjects %d for '%s'.", index, mesh->nPolygonObjects, mesh->name); } #endif return(bindingList); } /*----------------------------------------------------------------------------- Name : meshBindingListDelete Description : Free memory associated with a binding list. Inputs : list - list to delete Outputs : just memFree's it Return : ----------------------------------------------------------------------------*/ void meshBindingListDelete(mhbinding *list) { memFree(list); } /*----------------------------------------------------------------------------- Name : meshBindingFindByName Description : Find a named mesh binding. Inputs : startBinding - starting binding to search from, very start if NULL list - base of binding list mesh - mesh to search in. name - name of binding to search for. Outputs : before returning the binding, sets a pointer to the source poly object. Return : pointer to binding whose name matches or NULL if no match ----------------------------------------------------------------------------*/ mhbinding *meshBindingFindByName(mhbinding *startBinding, mhbinding *list, meshdata *mesh, char *name) { sdword count = mesh->nPolygonObjects; if (startBinding != NULL) { //start at either start of list or starting binding as referenced startBinding++; //start on entry _after_ previous find count -= startBinding - list; //less bindings to search list = startBinding; //new list start } while (count > 0) { //search rest of list if (list->object->pName != NULL) { if (strcmp(list->object->pName, name) == 0) { //if names match return(list); //all done } } list++; //update pointer/counter count--; } return(NULL); } /*----------------------------------------------------------------------------- Name : meshHierarchyWalkR Description : Recursion function for meshHierarchyWalk Inputs : object - object to recursively walk cbMesh - callback to call Outputs : Return : TRUE means to stop walking the list ----------------------------------------------------------------------------*/ bool meshHierarchyWalkR(meshdata *mesh, polygonobject *object, meshcallback preCallback, meshcallback postCallback) { polygonobject *daughter; if (preCallback) { if (preCallback(mesh, object, (udword)object->iObject)) { return(TRUE); } } for (daughter = object->pDaughter; daughter != NULL; daughter = daughter->pSister) { if (meshHierarchyWalkR(mesh, daughter, preCallback, postCallback)) { return(TRUE); } } if (postCallback) { if (postCallback(mesh, object, (udword)object->iObject)) { return(TRUE); } } return(FALSE); } /*----------------------------------------------------------------------------- Name : meshHierarchyWalk Description : Walk a mesh hierarchy in the order it will be rendered, calling a user function for each mesh object. Inputs : mesh - mesh object to walk cbMesh - callback to call for each node in the hierarchy Outputs : Return : void ----------------------------------------------------------------------------*/ void meshHierarchyWalk(meshdata *mesh, meshcallback preCallback, meshcallback postCallback) { polygonobject *object; for (object = &mesh->object[0]; object != NULL; object = object->pSister) { if (meshHierarchyWalkR(mesh, object, preCallback, postCallback)) { break; } } }