/*============================================================================= Name : render.c Purpose : Render module initialization and render a specific scene. Created 6/21/1997 by lmoloney Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #ifdef gshaw //#define DEBUG_COLLISIONS #endif #define RND_DOCKLIGHT_TWEAK 0 #define VISIBLE_POLYS 0 #ifdef khentschel #ifndef HW_Release #define VERBOSE_SHIP_STATS 1 #else #define VERBOSE_SHIP_STATS 0 #endif #define DISPLAY_LOD_SCALE 1 #else #define VERBOSE_SHIP_STATS 0 #define DISPLAY_LOD_SCALE 1 #endif #ifdef ddunlop #ifndef HW_Release #define FONT_CHECKSPECIAL 0 // special define for testing extended characters #else #define FONT_CHECKSPECIAL 0 // turn off this function #endif #endif #define SHOW_TRAIL_STATS 0 #define RND_WILL_PANIC 0 #define USE_RND_HINT 0 #define WILL_TWO_PASS 0 #define WIN32_LEAN_AND_MEAN #include #include "glinc.h" #include #include #include #include #include "debug.h" #include "switches.h" #include "netcheck.h" #include "spaceobj.h" #include "mouse.h" #include "prim2d.h" #include "region.h" #include "task.h" #include "key.h" #include "universe.h" #include "nis.h" #include "camera.h" #include "cameracommand.h" #include "prim3d.h" #include "font.h" #include "select.h" #include "texreg.h" #include "utility.h" #include "globals.h" #include "light.h" #include "shader.h" #include "tactical.h" #include "mesh.h" #include "render.h" #include "collision.h" #include "univupdate.h" #include "soundevent.h" #include "gun.h" #include "dock.h" #include "navlights.h" #include "main.h" #include "tweak.h" #include "defensefighter.h" #include "clouds.h" #include "nebulae.h" #include "fastmath.h" #include "btg.h" #include "demo.h" #include "file.h" #include "clipper.h" #include "glcaps.h" #include "teams.h" #include "singleplayer.h" #include "hs.h" #include "screenshot.h" #include "salcapcorvette.h" #include "autolod.h" #include "proximitysensor.h" #include "objectives.h" #include "subtitle.h" #include "tutor.h" #include "animatic.h" #include "bink.h" #include "StringsOnly.h" #include "mainrgn.h" #include "aiship.h" #include "commandnetwork.h" #include "ProfileTimers.h" #include "LagPrint.h" #include "glcompat.h" #include "tracking.h" #include "launchmgr.h" #include "devstats.h" bool8 rndFogOn = FALSE; #if RND_VISUALIZATION extern BOOL dockLines; extern BOOL gunLines; extern bool8 RENDER_BOXES; extern bool8 RENDER_LIGHTLINES; #endif extern bool8 g_ReplaceHack; extern bool8 g_WireframeHack; extern bool8 g_HiddenRemoval; extern color HorseRaceDropoutColor; vector g_RndPosition; bool rndTakeScreenshot = FALSE; /*============================================================================= Private Types: =============================================================================*/ /*============================================================================= Data: =============================================================================*/ //this function pointer is what to call to render the main view renderfunction rndMainViewRender = rndMainViewRenderFunction; static sdword rndHint = 0; HGLRC hGLRenderContext; HDC hGLDeviceContext; HWND hGLWindow; udword meshRenders; real32 rndAspectRatio; //aspect ratio of rendering context static sdword rndFillCounter = 0; static color rndFillColour = 0; //callback functions optionally called during rendering a mission sphere rendercallback rndPreObjectCallback = NULL, rndPostObjectCallback = NULL; sdword rndPerspectiveCorrect = FALSE; sdword rndTextureEnabled = FALSE; sdword rndTextureEnviron = RTE_Modulate; sdword rndNormalization = FALSE; sdword rndAdditiveBlending = FALSE; sdword rndLightingEnabled = TRUE; sdword rndScissorEnabled = FALSE; //data for billboard geometry hmatrix rndCameraMatrix; hmatrix rndProjectionMatrix; hvector rndBillboardCentre; #if RND_ERROR_CHECKING sdword rndBillboardEnabled = FALSE; #endif typedef struct c4ub_v3f_s { ubyte c[4]; real32 v[3]; } c4ub_v3f; c4ub_v3f* stararray = NULL; c4ub_v3f* bigstararray = NULL; //asteroid0 stuff typedef struct asteroid0data { color c; vector* position; } asteroid0data; #define ASTEROID0DATA_SIZE 80 asteroid0data asteroid0Data[ASTEROID0DATA_SIZE]; //frame counter udword rndFrameCount; //frame rate data and definitions #if RND_FRAME_RATE #define RND_FrameRatePeriod 2.0f #define RND_FrameRateSeconds 2.0f #define RND_FrameRateMaxRate 10 #define RND_FrameRateStack 1024 #define RND_FrameRateKey NUMPAD7 udword rndFrameRateStart; udword rndFrameRate; udword rndFrameCountMax, rndFrameCountMin; udword rndPrintCount; sdword rndDisplayFrameRate = 0; taskhandle rndFrameRateTaskHandle; extern regionhandle ghMainRegion; #endif //RND_FRAME_RATE #if defined(COLLISION_CHECK_STATS) || defined(PROFILE_TIMERS) sdword rndDisplayCollStats = 0; #define RND_CollStatsKey NUMPADSLASH #endif //polygon statistics measurments #if RND_POLY_STATS #define RND_PolyStatsPeriod 2.0f #define RND_PolyStatsSeconds 2.0f #define RND_PolyStatsStack 1024 #define RND_PolyStatsKey NUMPAD8 sdword rndDisplayPolyStats = 0; sdword rndNumberPolys; sdword rndNumberTextured; sdword rndNumberSmoothed; sdword rndNumberDots; sdword rndNumberLines; taskhandle rndPolyStatsTaskHandle; char rndPolyStatsString[256]; color rndPolyStatsColor = colReddish; sdword rndPolyStatFrameCounter = 0; #endif //RND_POLY_STATS #if RND_XYZ #define RND_XYZKey NUMPAD9 bool rndXYZPrint = FALSE; #endif //scaling minimum cap crap static sdword RND_CAPSCALECAP_STATS = 0; char rndCapScaleCapStatsString[256]; #if RND_SCALECAP_TWEAK #define scaleCapSlopeDelta 0.000001f //adjust the scaling cap static char scaleCapString[256] = ""; #endif //RND_SCALECAP_TWEAK #define RND_FOV_TWEAK 0 real32 btgFieldOfView = 50.0f; #if RND_FOV_TWEAK #define btgFieldOfViewDelta 1.0f static char fovString[256] = ""; #endif static sdword shipsAtLOD[5]; static sdword polysAtLOD[5]; //data for debugging the GL state by dumping it's contents #if RND_GL_STATE_DEBUG typedef struct { char *name; GLenum enumeration; bool bDefault; } enumentry; #define enumEntry(string, n) {string, n, FALSE} #define enumDefaultEntry(string, n) {string, n, TRUE} #define enumEnd {NULL, 0, FALSE} #define enumError {"ERROR", 0xffffff8, TRUE} enumentry rndBoolEnums[] = { enumEntry("GL_TRUE", GL_TRUE), enumDefaultEntry("GL_FALSE", GL_FALSE), enumEnd }; enumentry rndAlphaTestFuncEnum[] = { enumEntry("GL_NEVER", GL_NEVER), enumEntry("GL_LESS", GL_LESS), enumEntry("GL_EQUAL", GL_EQUAL), enumEntry("GL_LEQUAL", GL_LEQUAL), enumEntry("GL_GREATER", GL_GREATER), enumEntry("GL_NOTEQUAL", GL_NOTEQUAL), enumEntry("GL_GEQUAL", GL_GEQUAL), enumEntry("GL_ALWAYS", GL_ALWAYS), enumError, enumEnd }; enumentry rndBlendFuncEnums[] = { enumEntry("GL_ZERO", GL_ZERO), enumEntry("GL_ONE", GL_ONE), enumEntry("GL_DST_COLOR", GL_DST_COLOR), enumEntry("GL_ONE_MINUS_DST_COLOR", GL_ONE_MINUS_DST_COLOR), enumEntry("GL_SRC_ALPHA", GL_SRC_ALPHA), enumEntry("GL_ONE_MINUS_SRC_ALPHA", GL_ONE_MINUS_SRC_ALPHA), enumEntry("GL_DST_ALPHA", GL_DST_ALPHA), enumEntry("GL_ONE_MINUS_DST_ALPHA", GL_ONE_MINUS_DST_ALPHA), enumEntry("GL_SRC_ALPHA_SATURATE", GL_SRC_ALPHA_SATURATE), enumError, enumEnd }; enumentry rndFaceEnums[] = { enumEntry("GL_FRONT", GL_FRONT), enumEntry("GL_BACK", GL_BACK), enumEntry("GL_FRONT_AND_BACK", GL_FRONT_AND_BACK), enumError, enumEnd }; enumentry rndMatrixEnums[] = { enumEntry("GL_MODELVIEW", GL_MODELVIEW), enumEntry("GL_PROJECTION", GL_PROJECTION), enumEntry("GL_TEXTURE", GL_TEXTURE), enumError, enumEnd }; enumentry rndHintEnums[] = { enumEntry("GL_FASTEST", GL_FASTEST), enumEntry("GL_NICEST", GL_NICEST), enumEntry("GL_DONT_CARE", GL_DONT_CARE), enumError, enumEnd }; enumentry rndShadeModelEnums[] = { enumEntry("GL_FLAT", GL_FLAT), enumEntry("GL_SMOOTH", GL_SMOOTH), enumError, enumEnd }; enumentry rndTextureEnvEnums[] = { enumEntry("GL_MODULATE", GL_MODULATE), enumEntry("GL_DECAL", GL_DECAL), enumEntry("GL_BLEND", GL_BLEND), enumEntry("GL_REPLACE", GL_REPLACE), enumError, enumEnd }; typedef struct { char *heading; GLenum enumeration; sdword type; sdword nValues; enumentry *enumTable; } glstateentry; #define stateEntry(string, enumb, type, nValues, table) {string, enumb, type, nValues, table} #define G_Bool 0 #define G_GetBool 1 #define G_Integer 2 #define G_Float 3 #define G_FloatByte 4 #define G_IntFunc 5 //definitions for special-case functions #define G_TextureEnv 0 glstateentry rndStateSaveTable[] = { stateEntry("GL_ALPHA_TEST", GL_ALPHA_TEST, G_Bool, 1, rndBoolEnums), stateEntry("GL_BLEND", GL_BLEND, G_Bool, 1, rndBoolEnums), stateEntry("GL_CULL_FACE", GL_CULL_FACE, G_Bool, 1, rndBoolEnums), stateEntry("GL_DEPTH_TEST", GL_DEPTH_TEST, G_Bool, 1, rndBoolEnums), stateEntry("GL_FOG", GL_FOG, G_Bool, 1, rndBoolEnums), stateEntry("GL_LIGHT0", GL_LIGHT0, G_Bool, 1, rndBoolEnums), stateEntry("GL_LIGHT1", GL_LIGHT1, G_Bool, 1, rndBoolEnums), stateEntry("GL_LIGHTING", GL_LIGHTING, G_Bool, 1, rndBoolEnums), stateEntry("GL_LINE_SMOOTH", GL_LINE_SMOOTH, G_Bool, 1, rndBoolEnums), stateEntry("GL_LINE_STIPPLE", GL_LINE_STIPPLE, G_Bool, 1, rndBoolEnums), stateEntry("GL_NORMALIZE", GL_NORMALIZE, G_Bool, 1, rndBoolEnums), stateEntry("GL_POINT_SMOOTH", GL_POINT_SMOOTH, G_Bool, 1, rndBoolEnums), stateEntry("GL_POLYGON_SMOOTH", GL_POLYGON_SMOOTH, G_Bool, 1, rndBoolEnums), stateEntry("GL_POLYGON_STIPPLE", GL_POLYGON_STIPPLE, G_Bool, 1, rndBoolEnums), stateEntry("GL_SCISSOR_TEST", GL_SCISSOR_TEST, G_Bool, 1, rndBoolEnums), stateEntry("GL_TEXTURE_2D", GL_TEXTURE_2D, G_Bool, 1, rndBoolEnums), //stateEntry("GL_TEXTURE_ENV", G_TextureEnv, G_IntFunc, 1, rndTextureEnvEnums), //stateEntry("GL_TEXTURE_2D_BINDING", GL_TEXTURE_2D_BINDING, G_Integer, 1, NULL), stateEntry("GL_ALPHA_TEST_FUNC", GL_ALPHA_TEST_FUNC, G_Integer, 1, rndAlphaTestFuncEnum), stateEntry("GL_ALPHA_TEST_REF", GL_ALPHA_TEST_REF, G_Float, 1, NULL), stateEntry("GL_BLEND_DST", GL_BLEND_DST, G_Integer, 1, rndBlendFuncEnums), stateEntry("GL_BLEND_SRC", GL_BLEND_SRC, G_Integer, 1, rndBlendFuncEnums), stateEntry("GL_BLUE_BIAS", GL_BLUE_BIAS, G_FloatByte, 1, NULL), stateEntry("GL_COLOR_CLEAR_VALUE", GL_COLOR_CLEAR_VALUE, G_Float, 4, NULL), stateEntry("GL_COLOR_MATERIAL_FACE", GL_COLOR_MATERIAL_FACE, G_Integer, 1, rndFaceEnums), stateEntry("GL_CULL_FACE_MODE", GL_CULL_FACE_MODE, G_Integer, 1, rndFaceEnums), stateEntry("GL_CURRENT_COLOR", GL_CURRENT_COLOR, G_FloatByte, 4, NULL), stateEntry("GL_CURRENT_RASTER_COLOR", GL_CURRENT_RASTER_COLOR, G_FloatByte, 4, NULL), stateEntry("GL_CURRENT_RASTER_POSITION", GL_CURRENT_RASTER_POSITION, G_Float, 4, NULL), stateEntry("GL_CURRENT_TEXTURE_COORDS", GL_CURRENT_TEXTURE_COORDS, G_Float, 4, NULL), stateEntry("GL_DEPTH_BITS", GL_DEPTH_BITS, G_Integer, 1, NULL), stateEntry("GL_DEPTH_WRITEMASK", GL_DEPTH_WRITEMASK, G_GetBool, 1, rndBoolEnums), stateEntry("GL_FOG_COLOR", GL_FOG_COLOR, G_FloatByte, 4, NULL), stateEntry("GL_FOG_DENSITY", GL_FOG_DENSITY, G_Float, 1, NULL), stateEntry("GL_GREEN_BIAS", GL_GREEN_BIAS, G_FloatByte, 1, NULL), stateEntry("GL_LIGHT_MODEL_AMBIENT", GL_LIGHT_MODEL_AMBIENT, G_FloatByte, 4, NULL), stateEntry("GL_LIGHT_MODEL_TWO_SIDE", GL_LIGHT_MODEL_TWO_SIDE, G_GetBool, 1, rndBoolEnums), stateEntry("GL_LINE_WIDTH", GL_LINE_WIDTH, G_Float, 1, NULL), stateEntry("GL_LINE_WIDTH_GRANULARITY", GL_LINE_WIDTH_GRANULARITY, G_Float, 1, NULL), stateEntry("GL_MATRIX_MODE", GL_MATRIX_MODE, G_Integer, 1, rndMatrixEnums), stateEntry("GL_MAX_TEXTURE_SIZE", GL_MAX_TEXTURE_SIZE, G_Integer, 1, NULL), stateEntry("GL_MAX_VIEWPORT_DIMS", GL_MAX_VIEWPORT_DIMS, G_Integer, 2, NULL), stateEntry("GL_MODELVIEW_MATRIX", GL_MODELVIEW_MATRIX, G_Float, 16, NULL), stateEntry("GL_PERSPECTIVE_CORRECTION_HINT", GL_PERSPECTIVE_CORRECTION_HINT,G_Integer, 1, rndHintEnums), stateEntry("GL_POINT_SIZE", GL_POINT_SIZE, G_Float, 1, NULL), stateEntry("GL_POINT_SIZE_GRANULARITY", GL_POINT_SIZE_GRANULARITY, G_Float, 1, NULL), stateEntry("GL_POLYGON_MODE", GL_POLYGON_MODE, G_Integer, 1, rndFaceEnums), stateEntry("GL_PROJECTION_MATRIX", GL_PROJECTION_MATRIX, G_Float, 16, NULL), stateEntry("GL_RED_BIAS", GL_RED_BIAS, G_FloatByte, 1, NULL), stateEntry("GL_SCISSOR_BOX", GL_SCISSOR_BOX, G_Integer, 4, NULL), stateEntry("GL_SHADE_MODEL", GL_SHADE_MODEL, G_Integer, 1, rndShadeModelEnums), stateEntry("GL_SUBPIXEL_BITS", GL_SUBPIXEL_BITS, G_Integer, 1, NULL), stateEntry("GL_VIEWPORT", GL_VIEWPORT, G_Integer, 1, NULL), {NULL, 0, 0, 0, NULL} }; bool rndGLStateSaving = FALSE; char rndGLStateLogFileName[128]; sdword rndGLStateLogIndex = 0; #endif //RND_GL_STATE_DEBUG #if RND_PLUG_DISABLEABLE bool8 rndShamelessPlugEnabled = TRUE; #endif /*============================================================================= Functions: =============================================================================*/ #if RND_GL_STATE_DEBUG /*----------------------------------------------------------------------------- Name : rndIntToString Description : Convert a GL enumeration to a string Inputs : enumb - enumeration to convert table - table of enumerations/strings Outputs : Return : name of enumeration ----------------------------------------------------------------------------*/ char *rndIntToString(GLenum enumb, enumentry *table) { sdword index; for (index = 0; table[index].name != NULL; index++) { if (enumb == table[index].enumeration || table[index].bDefault) { return(table[index].name); } } dbgAssert(FALSE); return NULL; } /*----------------------------------------------------------------------------- Name : rndGLStateLog Description : Log the state of the GL machine. Inputs : location - user-specified name for where this function is called from Outputs : Return : void ----------------------------------------------------------------------------*/ #define MAX_FLOATS 16 #define MAX_INTS 4 #define MAX_BOOLS 1 void rndGLStateLogFunction(char *location) { sdword index, j; GLfloat floats[MAX_FLOATS]; GLint ints[MAX_INTS]; GLboolean bools[MAX_BOOLS]; char totalString[256]; char valueString[128]; FILE *f; f = fopen(rndGLStateLogFileName, "at"); if (f == NULL) { dbgMessagef("\nError opening '%s' for GL state logging.", rndGLStateLogFileName); return; } #if RND_GL_STATE_WINDOW dbgMessagef("\n******************** %s", location); #endif fprintf(f, "******************** %s\n", location); for (index = 0; rndStateSaveTable[index].heading != NULL; index++) { sprintf(totalString, "%32s:", rndStateSaveTable[index].heading); switch (rndStateSaveTable[index].type) { case G_Bool: bools[0] = glIsEnabled(rndStateSaveTable[index].enumeration); for (j = 0; j < rndStateSaveTable[index].nValues; j++) { if (rndStateSaveTable[index].enumTable != NULL) { sprintf(valueString, "%s", rndIntToString(bools[j], rndStateSaveTable[index].enumTable)); } else { sprintf(valueString, "%d", bools[j]); } strcat(totalString, valueString); if (j + 1 < rndStateSaveTable[index].nValues) { strcat(totalString, ", "); } } break; case G_GetBool: glGetBooleanv(rndStateSaveTable[index].enumeration, bools); for (j = 0; j < rndStateSaveTable[index].nValues; j++) { if (rndStateSaveTable[index].enumTable != NULL) { sprintf(valueString, "%s", rndIntToString(bools[j], rndStateSaveTable[index].enumTable)); } else { sprintf(valueString, "%d", bools[j]); } strcat(totalString, valueString); if (j + 1 < rndStateSaveTable[index].nValues) { strcat(totalString, ", "); } } break; case G_Integer: glGetIntegerv(rndStateSaveTable[index].enumeration, ints); for (j = 0; j < rndStateSaveTable[index].nValues; j++) { if (rndStateSaveTable[index].enumTable != NULL) { sprintf(valueString, "%s", rndIntToString(ints[j], rndStateSaveTable[index].enumTable)); } else { sprintf(valueString, "%d", ints[j]); } strcat(totalString, valueString); if (j + 1 < rndStateSaveTable[index].nValues) { strcat(totalString, ", "); } } break; case G_Float: glGetFloatv(rndStateSaveTable[index].enumeration, floats); for (j = 0; j < rndStateSaveTable[index].nValues; j++) { sprintf(valueString, "%.2f", floats[j]); strcat(totalString, valueString); if (j + 1 < rndStateSaveTable[index].nValues) { strcat(totalString, ", "); } } break; case G_FloatByte: glGetFloatv(rndStateSaveTable[index].enumeration, floats); for (j = 0; j < rndStateSaveTable[index].nValues; j++) { sprintf(valueString, "%d", colRealToUbyte(floats[j])); strcat(totalString, valueString); if (j + 1 < rndStateSaveTable[index].nValues) { strcat(totalString, ", "); } } break; case G_IntFunc: switch (rndStateSaveTable[index].enumeration) { case G_TextureEnv: // glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, ints); break; default: dbgAssert(FALSE); } for (j = 0; j < rndStateSaveTable[index].nValues; j++) { if (rndStateSaveTable[index].enumTable != NULL) { sprintf(valueString, "%s", rndIntToString(ints[j], rndStateSaveTable[index].enumTable)); } else { sprintf(valueString, "%d", ints[j]); } strcat(totalString, valueString); if (j + 1 < rndStateSaveTable[index].nValues) { strcat(totalString, ", "); } } break; default: dbgAssert(FALSE); } #if RND_GL_STATE_WINDOW dbgMessagef("\n%s", totalString); #endif fprintf(f, "%s\n", totalString); } fclose(f); } #endif //RND_GL_STATE_DEBUG #if RND_FRAME_RATE /*----------------------------------------------------------------------------- Name : rndFrameRateTaskFunction Description : Task function for the frame rate Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ #pragma optimize("gy", off) //turn on stack frame (we need ebp for this function) void rndFrameRateTaskFunction(void) { rndFrameRateStart = rndFrameRate = rndPrintCount = 0; //it counters rndFrameCountMax = 0; //set min/max counters rndFrameCountMin = SDWORD_Max; //to rediculous values taskYield(0); while (1) { taskStackSaveCond(0); rndFrameRate = rndFrameCount - rndFrameRateStart; //number of frames since last printed rndFrameRateStart = rndFrameCount; //reset start frame rndFrameCountMax = max(rndFrameCountMax, rndFrameRate);//update min/max rndFrameCountMin = min(rndFrameCountMin, rndFrameRate); if (rndDisplayFrameRate) { //display frame rate dbgMessagef("\nFrame rate: %.2f <= %.2f <= %.2f", (real32)(rndFrameCountMin) / RND_FrameRateSeconds, (real32)(rndFrameRate) / RND_FrameRateSeconds, (real32)(rndFrameCountMax) / RND_FrameRateSeconds); } rndPrintCount++; if (rndPrintCount >= RND_FrameRateMaxRate) { //if time to reset min/max rndPrintCount = 0; rndFrameCountMax = 0; //set min/max counters rndFrameCountMin = SDWORD_Max; //to rediculous values } taskStackRestoreCond(); taskYield(0); } } #pragma optimize("", on) //toggle frame rate on/off sdword rndFrameRateToggle(regionhandle region, sdword ID, udword event, udword data) { rndDisplayFrameRate ^= TRUE; dbgMessagef("\nFrame rate printing %s", rndDisplayFrameRate ? "ON" : "OFF"); return(0); } #endif //RND_FRAME_RATE #if defined(COLLISION_CHECK_STATS) || defined(PROFILE_TIMERS) udword rndCollStatsToggle(regionhandle region, sdword ID, udword event, udword data) { rndDisplayCollStats ^= TRUE; dbgMessagef("\nCollision stats printing %s", rndDisplayCollStats ? "ON" : "OFF"); return(0); } #endif //COLLISION_CHECK_STATS #if RND_POLY_STATS #pragma optimize("gy", off) //turn on stack frame (we need ebp for this function) void rndPolyStatsTaskFunction(void) { rndNumberPolys = 0; rndNumberTextured = 0; rndNumberSmoothed = 0; rndNumberDots = 0; rndNumberLines = 0; taskYield(0); while (1) { taskStackSaveCond(0); if (rndDisplayPolyStats && (rndPolyStatFrameCounter != 0 && rndNumberPolys != 0)) { //display frame rate sprintf(rndPolyStatsString, "\n%d, (%.2f, %.2f), %d, %d", rndNumberPolys / rndPolyStatFrameCounter, 100.0f * rndNumberTextured / rndNumberPolys, 100.0f * rndNumberSmoothed / rndNumberPolys, 10, 11); //print the poly count of the current LOD if (selSelected.numShips == 1) { static char polyCountString[256]; static Ship *ship; static lod *lodInfo; static sdword index, polyCount, vertexCount, lodIndex; static meshdata *mesh; ship = selSelected.ShipPtr[0]; if (lodTuningMode) { lodIndex = rndLOD; } else { lodIndex = ship->currentLOD; } if (lodIndex < ship->staticinfo->staticheader.LOD->nLevels) { lodInfo = &ship->staticinfo->staticheader.LOD->level[lodIndex]; if ((lodInfo->flags & LM_LODType) == LT_Mesh) { //if it's a mesh type lod mesh = (meshdata *)lodInfo->pData; //get the mesh pointer for (index = vertexCount = polyCount = 0; index < mesh->nPolygonObjects; index++) { //count vertices and polygons polyCount += mesh->object[index].nPolygons; vertexCount += mesh->object[index].nVertices; } sprintf(polyCountString, ", %d, %d, %d", lodIndex, vertexCount, polyCount); strcat(rndPolyStatsString, polyCountString); } } } dbgMessage(rndPolyStatsString); #if MESH_MATERIAL_STATS sprintf(meshMaterialStatsString, "\n%.2f / %.2f / %.2f", (real32)meshMaterialChanges / (real32)rndPolyStatFrameCounter, (real32)meshTotalMaterials / (real32)rndPolyStatFrameCounter, (real32)meshMaterialChanges / (real32)meshTotalMaterials); dbgMessage(meshMaterialStatsString); #endif //MESH_MATERIAL_STATS } rndPrintCount++; rndPolyStatsColor = colReddish; rndNumberPolys = 0; rndNumberTextured = 0; rndNumberSmoothed = 0; rndNumberDots = 0; rndNumberLines = 0; rndPolyStatFrameCounter = 0; #if MESH_MATERIAL_STATS meshMaterialChanges = 0; meshTotalMaterials = 0; #endif //MESH_MATERIAL_STATS taskStackRestoreCond(); taskYield(0); } } #pragma optimize("", on) //toggle frame rate on/off sdword rndPolyStatsToggle(regionhandle region, sdword ID, udword event, udword data) { rndDisplayPolyStats ^= TRUE; dbgMessagef("\nFrame rate printing %s", rndDisplayPolyStats ? "ON" : "OFF"); if (rndDisplayPolyStats) { strcpy(rndPolyStatsString, "\nnPolys (%textured, %smoothed), nDots, nLines, shipLod, nVertices, nPolys"); dbgMessage(rndPolyStatsString); rndPolyStatsColor = colWhite; #if MESH_MATERIAL_STATS strcpy(meshMaterialStatsString, "\nnMaterialChanges / nMaterials / factor"); dbgMessage(meshMaterialStatsString); #endif //MESH_MATERIAL_STATS } return(0); } #endif //RND_POLY_STATS static real32 scalar; void rndCapScaleCapStatsTaskFunction(void) { if (selSelected.numShips == 1) { Ship* ship = selSelected.ShipPtr[0]; ShipStaticInfo* shipstaticinfo = (ShipStaticInfo*)ship->staticinfo; if (ship->trail[0] == NULL) { scalar = 0.0f; } else { scalar = ship->trail[0]->scalecap; if (scalar == -1.0f) { scalar = 0.0f; } } } else { scalar = 0.0f; } sprintf(rndCapScaleCapStatsString, "\n trailScaleCap %f", scalar); } bool setupPixelFormat(HDC hDC) { int pixelFormat; PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), /* size */ 1, /* version */ PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER, /* support double-buffering */ PFD_TYPE_RGBA, /* color type */ // 16, /* prefered color depth */ MAIN_WindowDepth, 0, 0, 0, 0, 0, 0, /* color bits (ignored) */ 0, /* no alpha buffer */ 0, /* alpha bits (ignored) */ 0, /* no accumulation buffer */ 0, 0, 0, 0, /* accum bits (ignored) */ 16, /* depth buffer */ 0, /* no stencil buffer */ 0, /* no auxiliary buffers */ PFD_MAIN_PLANE, /* main layer */ 0, /* reserved */ 0, 0, 0, /* no layer, visible, damage masks */ }; if (rwglChoosePixelFormat == NULL || glNT) { pixelFormat = ChoosePixelFormat(hDC, &pfd); } else { pixelFormat = rwglChoosePixelFormat(hDC, &pfd); } if (pixelFormat == 0) { dbgMessagef("\nChoosePixelFormat failed (%d).", GetLastError()); return FALSE; } if (rwglSetPixelFormat == NULL || glNT) { if (SetPixelFormat(hDC, pixelFormat, &pfd) != TRUE) { dbgMessagef("\nSetPixelFormat failed (%d).", GetLastError()); return FALSE; } } else { if (rwglSetPixelFormat(hDC, pixelFormat, &pfd) != TRUE) { dbgMessagef("\nSetPixelFormat failed (%d).", GetLastError()); return FALSE; } } return TRUE; } bool setupPalette(HDC hDC) { int pixelFormat; PIXELFORMATDESCRIPTOR pfd; if (rwglGetPixelFormat == NULL || glNT) { pixelFormat = GetPixelFormat(hDC); } else { pixelFormat = rwglGetPixelFormat(hDC); } if (rwglDescribePixelFormat == NULL || glNT) { DescribePixelFormat(hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd); } else { rwglDescribePixelFormat(hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd); } if (pfd.dwFlags & PFD_NEED_PALETTE) { dbgMessage("rndInit: needs paletted display"); return FALSE; } else { return TRUE; } } typedef int (*AUXINITPOSITIONPROC)(GLuint, GLuint, GLuint, GLuint, GLuint); sdword rndSmallInit(rndinitdata* initData, bool GL) { if (GL) { hGLWindow = initData->hWnd; hGLDeviceContext = GetDC(initData->hWnd); if (!setupPixelFormat(hGLDeviceContext)) { return FALSE; } if (!setupPalette(hGLDeviceContext)) { return FALSE; } hGLRenderContext = (HGLRC)rwglCreateContext((unsigned int)hGLDeviceContext); rwglMakeCurrent((int)hGLDeviceContext, (int)hGLRenderContext); } else { AUXINITPOSITIONPROC initPositionProc; initPositionProc = (AUXINITPOSITIONPROC)rwglGetProcAddress("rauxInitPosition"); dbgAssert(initPositionProc != NULL); hGLWindow = initData->hWnd; rglCreateWindow((GLint)hGLWindow, (GLint)MAIN_WindowWidth, (GLint)MAIN_WindowDepth); if (!initPositionProc(0, 0, initData->width, initData->height, MAIN_WindowDepth)) { return FALSE; } } glLockArraysEXT = (LOCKARRAYSEXTproc)rwglGetProcAddress("glLockArraysEXT"); glUnlockArraysEXT = (UNLOCKARRAYSEXTproc)rwglGetProcAddress("glUnlockArraysEXT"); return TRUE; } /*----------------------------------------------------------------------------- Name : rndInit Description : Initialize the rendering system. Inputs : initData - pointer to a structure containing initialization info. Outputs : ?? Return : ERROR if an error occurred. ----------------------------------------------------------------------------*/ static GLfloat lightPosition0[] = {10000.0f, 1.0f, 1.0f, 0.1f}; sdword rndInit(rndinitdata *initData) { static GLfloat ambientProperties[] = {0.9f, 0.5f, 0.5f, 1.0f}; static GLfloat diffuseProperties[] = {0.8f, 0.8f, 0.8f, 1.0f}; static GLfloat specularProperties[] = {1.0f, 1.0f, 1.0f, 1.0f}; if (!RGL) { hGLWindow = initData->hWnd; hGLDeviceContext = GetDC(initData->hWnd); if (!setupPixelFormat(hGLDeviceContext)) { dbgMessage("\nrndInit: GL couldn't setupPixelFormat"); return !OKAY; } if (!setupPalette(hGLDeviceContext)) { dbgMessage("\nrndInit: GL couldn't setupPalette"); return !OKAY; } hGLRenderContext = (HGLRC)rwglCreateContext((unsigned int)hGLDeviceContext); //greate GL render context and select into window rwglMakeCurrent((int)hGLDeviceContext, (int)hGLRenderContext); // auxInitPosition(0, 0, initData->width, initData->height); //create the viewport for rendering // auxInitDisplayMode(AUX_RGB | AUX_DEPTH | AUX_DOUBLE); //set mode (direct color, Z-buffered, double buffered) } else { AUXINITPOSITIONPROC initPositionProc; initPositionProc = (AUXINITPOSITIONPROC)rwglGetProcAddress("rauxInitPosition"); dbgAssert(initPositionProc != NULL); hGLWindow = initData->hWnd; rglCreateWindow((GLint)hGLWindow, (GLint)MAIN_WindowWidth, (GLint)MAIN_WindowDepth); if (!initPositionProc(0, 0, initData->width, initData->height, MAIN_WindowDepth)) { dbgMessage("\nrndInit: RGL couldn't initPositionProc"); return !OKAY; } } glCapStartup(); if (!glCapFeatureExists(GL_SHARED_TEXTURE_PALETTE_EXT)) { trNoPalettes = TRUE; } #if RND_VERBOSE_LEVEL >= 1 dbgMessagef("\nrndInit: OpenGL Vendor :%s", glGetString(GL_VENDOR)); dbgMessagef("\nrndInit: OpenGL Renderer :%s", glGetString(GL_RENDERER)); dbgMessagef("\nrndInit: OpenGL Version :%s", glGetString(GL_VERSION)); dbgMessagef("\nrndInit: OpenGL Extensions :%s", glGetString(GL_EXTENSIONS)); #endif rndSetClearColor(colRGBA(0,0,0,255)); glClearDepth( 1.0 ); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); rndAdditiveBlending = FALSE; glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glLightfv( GL_LIGHT0, GL_AMBIENT, ambientProperties); glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuseProperties); glLightfv( GL_LIGHT0, GL_SPECULAR, specularProperties); glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1.0f); // glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientProperties); glEnable( GL_LIGHT0 ); // glShadeModel(GL_FLAT); glCullFace(GL_BACK); //enable culling of back-facing polys glEnable(GL_CULL_FACE); rndPreObjectCallback = rndPostObjectCallback = NULL; //no render callbacks yet rndAspectRatio = (GLfloat)initData->width / (GLfloat)initData->height; rndFrameCount = 0; #if RND_FRAME_RATE rndFrameRateTaskHandle = taskStart(rndFrameRateTaskFunction, RND_FrameRatePeriod, 0);//start frame rate task regKeyChildAlloc(ghMainRegion, RND_FrameRateKey, RPE_KeyDown, rndFrameRateToggle, 1, RND_FrameRateKey); #endif//RND_FRAME_RATE #if defined(COLLISION_CHECK_STATS) || defined(PROFILE_TIMERS) regKeyChildAlloc(ghMainRegion, RND_CollStatsKey, RPE_KeyDown, rndCollStatsToggle, 1, RND_CollStatsKey); #endif #if RND_POLY_STATS rndPolyStatsTaskHandle = taskStart(rndPolyStatsTaskFunction, RND_PolyStatsPeriod, 0);//start frame rate task regKeyChildAlloc(ghMainRegion, RND_PolyStatsKey, RPE_KeyDown, rndPolyStatsToggle, 1, RND_PolyStatsKey); #endif //RND_POLY_STATS #if TEST_SPAN_PERFORMANCE testHook(); #endif glDepthFunc(glCapDepthFunc); glLockArraysEXT = (LOCKARRAYSEXTproc)rwglGetProcAddress("glLockArraysEXT"); glUnlockArraysEXT = (UNLOCKARRAYSEXTproc)rwglGetProcAddress("glUnlockArraysEXT"); return(OKAY); } /*----------------------------------------------------------------------------- Name : rndClose Description : Closes the rendering module. Inputs : void Outputs : .. Return : void ----------------------------------------------------------------------------*/ void rndClose(void) { if (RGL) { rglDeleteWindow((GLint)hGLWindow); } else { if (hGLRenderContext) { rwglMakeCurrent(0, 0); //release GL render context rwglDeleteContext((int)hGLRenderContext); } ReleaseDC(hGLWindow, hGLDeviceContext); //release the Device context } } /*----------------------------------------------------------------------------- Name : rndBillboardEnable Description : Enable billboard mode Inputs : centre - location of the centre or 'pivot' point of the billboard geometry. Outputs : Loads identity for modelview matrix and translates by (cameramatrix * centre) which is stored in rndBillboardCentre. Return : void Note : Billboard geometry should be drawn on the z = 0 plane about x = y = 0. ----------------------------------------------------------------------------*/ void rndBillboardEnable(vector *centre) { hvector hCentre; #if RND_ERROR_CHECKING if (rndBillboardEnabled) { dbgFatal(DBG_Loc, "Billboarding already enabled"); } rndBillboardEnabled = TRUE; #endif glLoadIdentity(); hCentre.x = centre->x; hCentre.y = centre->y; hCentre.z = centre->z; hCentre.w = 1.0f; hmatMultiplyHMatByHVec(&rndBillboardCentre, &rndCameraMatrix, &hCentre); glTranslatef(rndBillboardCentre.x, rndBillboardCentre.y, rndBillboardCentre.z); } /*----------------------------------------------------------------------------- Name : rndBillboardDisable Description : Disable billboard mode. Inputs : void Outputs : Loads rndCameraMatrix into modelview (should only be 1 level on the model-view matrix stack). Return : void ----------------------------------------------------------------------------*/ void rndBillboardDisable(void) { #if RND_ERROR_CHECKING if (rndBillboardEnabled == FALSE) { dbgFatal(DBG_Loc, "Billboarding already disabled."); } rndBillboardEnabled = FALSE; #endif glLoadMatrixf((GLfloat *)(&rndCameraMatrix)); //restore previous camera matrix } void rndFilter(bool on) { if (!RGL) { return; } if (on) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } } static real64 rndNear(real64 in) { return (RGLtype == D3Dtype) ? 0.5 * in : in; } /*----------------------------------------------------------------------------- Name : rndBackgroundRender Description : Render the background of a mission sphere Inputs : radius - radius of mission sphere galaxy - pointer to galaxyinfo structure describing background Outputs : renders each segment as a quadrangle about the mission sphere Return : void ----------------------------------------------------------------------------*/ void rndBackgroundRender(real32 radius, Camera* camera, bool bDrawStars) { real32 projection[16]; sdword index; glGetFloatv(GL_PROJECTION_MATRIX, projection); glMatrixMode(GL_PROJECTION); glLoadIdentity(); rgluPerspective(btgFieldOfView, rndAspectRatio, //set projection matrix rndNear(camera->clipPlaneNear), camera->clipPlaneFar); glMatrixMode(GL_MODELVIEW); if (showBackgrounds && gameIsRunning) { btgRender(); } //draw the stars in the mission sphere trClearCurrent(); glBindTexture(GL_TEXTURE_2D, 0); rndTextureEnable(FALSE); if (bDrawStars && gameIsRunning) { bool blends, pointSize; blends = glCapFeatureExists(GL_POINT_SMOOTH); pointSize = glCapFeatureExists(GL_POINT_SIZE); glPointSize(1.0f); if (blends) { glEnable(GL_POINT_SMOOTH); glEnable(GL_BLEND); //additively blend only if background are enabled rndAdditiveBlends(showBackgrounds); if (pointSize && (MAIN_WindowWidth > 1024)) { glPointSize(1.2f); } } //need to reset the projection matrix glMatrixMode(GL_PROJECTION); glLoadIdentity(); rgluPerspective(btgFieldOfView, rndAspectRatio, rndNear(camera->clipPlaneNear), camera->clipPlaneFar); //draw small stars if (glCapFeatureExists(GL_VERTEX_ARRAY)) { glInterleavedArrays(GL_C4UB_V3F, 0, (GLvoid*)stararray); glDrawArrays(GL_POINTS, 0, universe.star3dinfo->Num3dStars - NUM_BIG_STARS); } else { glBegin(GL_POINTS); for (index = 0; index < (universe.star3dinfo->Num3dStars - NUM_BIG_STARS); index++) { glColor4ub(stararray[index].c[0], stararray[index].c[1], stararray[index].c[2], stararray[index].c[3]); glVertex3fv((GLfloat*)&stararray[index].v); } glEnd(); } if (blends && pointSize) { switch (MAIN_WindowWidth) { case 640: case 800: glPointSize(2.1f); break; case 1024: glPointSize(2.5f); break; case 1280: glPointSize(2.65f); break; default: glPointSize(2.8f); } } //draw big stars if (glCapFeatureExists(GL_VERTEX_ARRAY)) { glInterleavedArrays(GL_C4UB_V3F, 0, (GLvoid*)bigstararray); glDrawArrays(GL_POINTS, 0, NUM_BIG_STARS); } else { glBegin(GL_POINTS); for (index = 0; index < NUM_BIG_STARS; index++) { glColor4ub(bigstararray[index].c[0], bigstararray[index].c[1], bigstararray[index].c[2], bigstararray[index].c[3]); glVertex3fv((GLfloat*)&bigstararray[index].v); } glEnd(); } if (blends) { glDisable(GL_BLEND); glDisable(GL_POINT_SMOOTH); if (pointSize) { glPointSize(1.0f); } } } rndAdditiveBlends(FALSE); glMatrixMode(GL_PROJECTION); glLoadMatrixf(projection); glMatrixMode(GL_MODELVIEW); } /*----------------------------------------------------------------------------- Name : rndCameraInside Description : decides whether the camera is inside a ship or not Inputs : spaceobj - the object to test camera - the current camera object Outputs : Return : TRUE if camera is inside, FALSE otherwise ----------------------------------------------------------------------------*/ bool rndCameraInside(SpaceObj* spaceobj, Camera* camera) { if ((spaceobj->flags & SOF_Rotatable) && (spaceobj->flags & SOF_Impactable)) { vector dist; real32 collspheresize; collspheresize = spaceobj->staticinfo->staticheader.staticCollInfo.collspheresize; collspheresize *= collspheresize; //sphere test vecSub(dist, spaceobj->posinfo.position, camera->eyeposition); if (vecMagnitudeSquared(dist) > collspheresize) { //further away than the bounding sphere, trivially reject return FALSE; } //rect test (final) return collCheckRectPoint((SpaceObjRotImp*)spaceobj, &camera->eyeposition); } else { return FALSE; } } /*----------------------------------------------------------------------------- Name : rndInsideShip Description : determines whether the camera is inside a ship Inputs : spaceobj - the object to consider camera - current camera object Outputs : Return : TRUE if camera inside, else FALSE ----------------------------------------------------------------------------*/ bool rndInsideShip(SpaceObj* spaceobj, Camera* camera) { if (nisIsRunning) { return FALSE; } if (bitTest(universe.mainCameraCommand.ccMode,CCMODE_PILOT_SHIP)) { return FALSE; } return rndCameraInside(spaceobj, camera); } /*----------------------------------------------------------------------------- Name : rndShipVisible Description : determines if a SpaceObj is within the viewing frustum via a few different means Inputs : spaceobj - the spaceobject to consider camera - current camera object Outputs : Return : TRUE if ship is visible, else FALSE ----------------------------------------------------------------------------*/ bool rndShipVisible(SpaceObj* spaceobj, Camera* camera) { vector veye, vobj; vecSub(veye, camera->eyeposition, camera->lookatpoint); vecSub(vobj, camera->eyeposition, spaceobj->posinfo.position); if ((spaceobj->flags & SOF_Rotatable) && (spaceobj->flags & SOF_Impactable)) { //bbox frustum culling StaticCollInfo* sinfo = &((SpaceObjRotImp*)spaceobj)->staticinfo->staticheader.staticCollInfo; #if 0 //GLcompat return !rglIsClipped((GLfloat*)&sinfo->collrectoffset, sinfo->uplength, sinfo->rightlength, sinfo->forwardlength); #else return !clipBBoxIsClipped((real32*)&sinfo->collrectoffset, sinfo->uplength, sinfo->rightlength, sinfo->forwardlength); #endif } else { //sphere test if (spaceobj->objtype == OBJ_ShipType) { //account for ship's radius real32 radius = ((Ship*)spaceobj)->staticinfo->staticheader.staticCollInfo.approxcollspheresize; return (vecDotProduct(veye, vobj) > -radius); } else { return (vecDotProduct(veye, vobj) > 0.0f); } } } /*----------------------------------------------------------------------------- Name : rndShipVisibleUsingCoordSys Description : calls rndShipVisible after setting up appropriate matrices in the GL Inputs : spaceobj - the spaceobject to consider camera - current camera object Outputs : Return : TRUE if visible, FALSE otherwise ----------------------------------------------------------------------------*/ bool rndShipVisibleUsingCoordSys(SpaceObj* spaceobj, Camera* camera) { GLfloat projection[16], modelview[16]; hmatrix coordMatrixForGL; bool result; glGetFloatv(GL_PROJECTION_MATRIX, projection); glGetFloatv(GL_MODELVIEW_MATRIX, modelview); glMatrixMode(GL_PROJECTION); glLoadIdentity(); rgluPerspective(camera->fieldofview, rndAspectRatio, rndNear(camera->clipPlaneNear), camera->clipPlaneFar); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); rgluLookAt(camera->eyeposition.x, camera->eyeposition.y, camera->eyeposition.z, camera->lookatpoint.x, camera->lookatpoint.y, camera->lookatpoint.z, camera->upvector.x, camera->upvector.y, camera->upvector.z); hmatMakeHMatFromMat(&coordMatrixForGL, &((SpaceObjRot*)spaceobj)->rotinfo.coordsys); hmatPutVectIntoHMatrixCol4(spaceobj->posinfo.position, coordMatrixForGL); glMultMatrixf((GLfloat*)&coordMatrixForGL); result = rndShipVisible(spaceobj, camera); glMatrixMode(GL_PROJECTION_MATRIX); glLoadMatrixf(projection); glMatrixMode(GL_MODELVIEW_MATRIX); glLoadMatrixf(modelview); return result; } /*----------------------------------------------------------------------------- Name : rndFade Description : configures the rendering pipeline for fading ships out of existence at the edge of the renderlist Inputs : spaceobj - the object camera - the camera Outputs : Return : TRUE if the ship is not visible (totally faded), FALSE otherwise ----------------------------------------------------------------------------*/ bool rndFade(SpaceObj* spaceobj, Camera* camera) { vector distvec; real32 distsqr0, distsqr1, distsqr; real32 fadedist, maxdist, mindist; real32 mult = 1.3f; vecSub(distvec, camera->lookatpoint, spaceobj->posinfo.position); distsqr0 = vecMagnitudeSquared(distvec); vecSub(distvec, camera->eyeposition, spaceobj->posinfo.position); distsqr1 = vecMagnitudeSquared(distvec); distsqr = (distsqr0 < distsqr1) ? distsqr0 : distsqr1; if (spaceobj->objtype == OBJ_ShipType) { switch (((ShipStaticInfo*)spaceobj->staticinfo)->shipclass) { case CLASS_Mothership: fadedist = RENDER_FADE_MOTHERSHIP; maxdist = RENDER_LIMIT_MOTHERSHIP; break; case CLASS_HeavyCruiser: fadedist = RENDER_FADE_HEAVYCRUISER; maxdist = RENDER_LIMIT_HEAVYCRUISER; break; case CLASS_Carrier: fadedist = RENDER_FADE_CARRIER; maxdist = RENDER_LIMIT_CARRIER; break; case CLASS_Destroyer: fadedist = RENDER_FADE_DESTROYER; maxdist = RENDER_LIMIT_DESTROYER; break; case CLASS_Frigate: fadedist = RENDER_FADE_FRIGATE; maxdist = RENDER_LIMIT_FRIGATE; break; case CLASS_Corvette: fadedist = RENDER_FADE_CORVETTE; maxdist = RENDER_LIMIT_CORVETTE; break; case CLASS_Fighter: fadedist = RENDER_FADE_FIGHTER; maxdist = RENDER_LIMIT_FIGHTER; break; case CLASS_Resource: fadedist = RENDER_FADE_RESOURCE; maxdist = RENDER_LIMIT_RESOURCE; break; case CLASS_NonCombat: fadedist = RENDER_FADE_NONCOMBAT; maxdist = RENDER_LIMIT_NONCOMBAT; break; default: fadedist = RENDER_FADE_FRIGATE; //happy medium maxdist = RENDER_LIMIT_FRIGATE; } if (((ShipStaticInfo*)spaceobj->staticinfo)->renderlistFade != 0.0f) { fadedist = ((ShipStaticInfo*)spaceobj->staticinfo)->renderlistFade; maxdist = ((ShipStaticInfo*)spaceobj->staticinfo)->renderlistLimitSqr; } fadedist *= mult; maxdist = mult * fsqrt(maxdist); } else if (spaceobj->objtype == OBJ_DerelictType) { if (((DerelictStaticInfo*)spaceobj->staticinfo)->renderlistFade != 0.0f) { fadedist = ((DerelictStaticInfo*)spaceobj->staticinfo)->renderlistFade; maxdist = ((DerelictStaticInfo*)spaceobj->staticinfo)->renderlistLimitSqr; } else if (spaceobj->flags & SOF_BigObject) { fadedist = RENDER_MAXVIEWABLE_FADE; maxdist = RENDER_MAXVIEWABLE_DISTANCE_SQR; } else { fadedist = RENDER_VIEWABLE_FADE; maxdist = RENDER_VIEWABLE_DISTANCE_SQR; } fadedist *= mult; maxdist = mult * fsqrt(maxdist); } else { if (spaceobj->flags & SOF_BigObject) { fadedist = mult * RENDER_MAXVIEWABLE_FADE; maxdist = mult * fsqrt(RENDER_MAXVIEWABLE_DISTANCE_SQR); } else { fadedist = mult * RENDER_VIEWABLE_FADE; maxdist = mult * fsqrt(RENDER_VIEWABLE_DISTANCE_SQR); } } mindist = maxdist - fadedist; fadedist *= fadedist; mindist *= mindist; if (distsqr < mindist || g_ReplaceHack) { if (RGL && !usingShader) rglLightingAdjust(0.0f); else meshSetFade(0.0f); } else if (distsqr < mindist+fadedist) { real32 amount = (distsqr - mindist) / fadedist; if (RGL && !usingShader) rglLightingAdjust(amount); else meshSetFade(amount); rndAdditiveBlends(FALSE); } else { if (RGL && !usingShader) rglLightingAdjust(1.0f); else meshSetFade(1.0f); return TRUE; } return FALSE; } void rndUnFade() { if (RGL && !usingShader) rglLightingAdjust(0.0f); else meshSetFade(0.0f); } /*----------------------------------------------------------------------------- Name : rndRenderAHomeworld Description : Special 'world-render' code for drawing planets Inputs : camera - what camera we're rendering from world - what planet we're drawing Outputs : Return : ----------------------------------------------------------------------------*/ #define camera ((Camera *)voidCamera) #define world ((Derelict *)voidWorld) void rndRenderAHomeworld(void* voidCamera, void *voidWorld) { real32 planetScale; vector *position; meshdata *worldMesh; hmatrix coordMatrixForGL; sdword colorScheme = world->colorScheme; planetScale = world->staticinfo->scaleFactor; position = &world->posinfo.position; worldMesh = (meshdata *)world->staticinfo->staticheader.LOD->level[0].pData; glMatrixMode(GL_PROJECTION); glLoadIdentity(); rgluPerspective(btgFieldOfView, rndAspectRatio, rndNear(camera->clipPlaneNear), CAMERA_CLIP_FAR_PLANET); glMatrixMode(GL_MODELVIEW); glPushMatrix(); hmatMakeHMatFromMat(&coordMatrixForGL,&world->rotinfo.coordsys); hmatPutVectIntoHMatrixCol4(*position, coordMatrixForGL); glMultMatrixf((float *)&coordMatrixForGL);//ship's rotation matrix glScalef(planetScale, planetScale, planetScale); rndNormalizeEnable(TRUE); glDisable(GL_DEPTH_TEST); meshObjectRender(&worldMesh->object[0], worldMesh->localMaterial, colorScheme); glEnable(GL_DEPTH_TEST); rndNormalizeEnable(FALSE); glPopMatrix(); glMatrixMode(GL_PROJECTION); glLoadMatrixf((GLfloat*)&rndProjectionMatrix); glMatrixMode(GL_MODELVIEW); } #undef world #undef camera /*----------------------------------------------------------------------------- Name : rndRenderAWorldEffect Description : Render an effect in the manner of a homeworld. Inputs : camera - camera we're looking at it from effect - effect we are to render Outputs : Return : void ----------------------------------------------------------------------------*/ void rndRenderAWorldEffect(Camera *camera, Effect *effect) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); rgluPerspective(btgFieldOfView, rndAspectRatio, rndNear(camera->clipPlaneNear), CAMERA_CLIP_FAR_PLANET); glMatrixMode(GL_MODELVIEW); rndNormalizeEnable(TRUE); glDisable(GL_DEPTH_TEST); etgEffectDraw(effect); glEnable(GL_DEPTH_TEST); rndNormalizeEnable(FALSE); glMatrixMode(GL_PROJECTION); glLoadMatrixf((GLfloat*)&rndProjectionMatrix); glMatrixMode(GL_MODELVIEW); } static real32 lastTime0 = 0.0f; static real32 lastTime1 = 0.0f; void rndResetFloatTime(void) { lastTime0 = taskTimeElapsed; lastTime1 = taskTimeElapsed; } /*----------------------------------------------------------------------------- Name : rndCameraOffset0 Description : camera offset fn 0 Inputs : Outputs : Return : a value to be added to a camera coordinate ----------------------------------------------------------------------------*/ real32 rndCameraOffset0(void) { real32 et; static real32 t = 0.0f; t += taskTimeElapsed - lastTime0; lastTime0 = taskTimeElapsed; et = t * CAMERA_FLOAT_SCALAR0; return (real32)(sin((real64)et) - cos(0.5f * (real64)et)); } /*----------------------------------------------------------------------------- Name : rndCameraOffset1 Description : camera offset fn 1 Inputs : Outputs : Return : a value to be added to a camera coordinate ----------------------------------------------------------------------------*/ real32 rndCameraOffset1(void) { real32 et; static real32 t = 0.0f; t += taskTimeElapsed - lastTime1; lastTime1 = taskTimeElapsed; et = t * CAMERA_FLOAT_SCALAR1; return (real32)(sin((real64)et) + cos(0.5f * (real64)et)); } /*----------------------------------------------------------------------------- Name : rndScaleCameraOffsets Description : apply scaling to the camera float offsets to fudge a slightly more linear falloff with distance Inputs : camera - the camera offset - offsets, 2 of 'em Outputs : scaledOffset - scaled versions of the offsets Return : ----------------------------------------------------------------------------*/ void rndScaleCameraOffsets(real32 *scaledOffset, Camera* camera, real32* offset) { real32 dist; dist = camera->distance * CAMERA_FLOAT_DIST_SCALAR; if (dist > 1.0f) { scaledOffset[0] = offset[0] * dist; scaledOffset[1] = offset[1] * dist; scaledOffset[2] = offset[2] * dist; } else { scaledOffset[0] = offset[0]; scaledOffset[1] = offset[1]; scaledOffset[2] = offset[2]; } } /*----------------------------------------------------------------------------- Name : rndDockScalar Description : returns a value [0..1] indicating the amount of docking light being received Inputs : ship - ship to light dockship - the ship that 'ship' is docking with nearVal, farVal - range of light Outputs : Return : scalar [0..1] indicating amount of light being received ----------------------------------------------------------------------------*/ real32 rndDockScalar(Ship* ship, Ship* dockship, real32 nearVal, real32 farVal) { vector veclen; real32 dist; vecSub(veclen, dockship->posinfo.position, ship->posinfo.position); dist = fsqrt(vecMagnitudeSquared(veclen)); if (dist < nearVal) dist = nearVal; if (dist > farVal) dist = farVal; dist -= nearVal; dist /= (farVal - nearVal); return 1.0f - dist; } /*----------------------------------------------------------------------------- Name : rndDrawAsteroid0 Description : renders asteroid0's (small asteroids that provide visible structure to a level) Inputs : n - number of points to render Outputs : displays the asteroid0Data list Return : ----------------------------------------------------------------------------*/ void rndDrawAsteroid0(sdword n) { sdword index; color c; vector* v; glPointSize(glCapFeatureExists(GL_POINT_SIZE) ? 2.0f : 1.0f); glBegin(GL_POINTS); for (index = 0; index < n; index++) { c = asteroid0Data[index].c; v = asteroid0Data[index].position; glColor3ub(colRed(c), colGreen(c), colBlue(c)); glVertex3fv((GLfloat*)v); } glEnd(); glPointSize(1.0f); } /*----------------------------------------------------------------------------- Name : rndPreRenderDebugStuff Description : A bunch of debug stuff from the beginning of the render function. Inputs : camera - same as parameter to rndMainViewRender Outputs : Return : void ----------------------------------------------------------------------------*/ void rndPreRenderDebugStuff(Camera *camera) { #if GUN_TUNE_MODE if (gunTuningMode && (selSelected.numShips == 1)) { if (keyIsStuck(INSERTKEY)) { tuningGun++; keyClearSticky(INSERTKEY); } } #endif #if LOD_PRINT_DISTANCE if (keyIsStuck(INSERTKEY)) { if (rndLOD < 10) { rndLOD++; dbgMessagef("\nLevel of detail = %d", rndLOD); } keyClearSticky(INSERTKEY); } if (keyIsStuck(HOMEKEY)) { if (rndLOD > 0) { rndLOD--; dbgMessagef("\nLevel of detail = %d", rndLOD); } keyClearSticky(HOMEKEY); } if (keyIsStuck(DELETEKEY)) { lodDrawingMode ^= TRUE; if (selSelected.numShips == 1) { vector length; length.x = camera->eyeposition.x - selSelected.ShipPtr[0]->posinfo.position.x; length.y = camera->eyeposition.y - selSelected.ShipPtr[0]->posinfo.position.y; length.z = camera->eyeposition.z - selSelected.ShipPtr[0]->posinfo.position.z; dbgMessagef("\nCamera distance = %f", vecMagnitudeSquared(length)); } else { dbgMessagef("\nPlease select one and only one object for LOD tuning."); } keyClearSticky(DELETEKEY); } if (keyIsStuck(ENDKEY)) { lodTuningMode ^= TRUE; dbgMessagef("\nLOD tuning %s", lodTuningMode ? "ON" : "OFF"); keyClearSticky(ENDKEY); } #endif #if RND_XYZ if (keyIsStuck(RND_XYZKey)) { keyClearSticky(RND_XYZKey); rndXYZPrint ^= TRUE; } #endif //RND_XYZ } /*----------------------------------------------------------------------------- Name : rndPostRender3DStuff Description : Post-object rendering of 3D debugging stuff Inputs : camera - same as parameter to rndMainViewRender Outputs : Return : ----------------------------------------------------------------------------*/ void rndPostRenderDebug3DStuff(Camera *camera) { #if NIS_DRAW_CAMERA if (nisIsRunning && nisCamera != NULL && nisCamera != camera) { //if we should draw the NIS camera vector upVector = nisCamera->eyeposition; vector cameraVector; static bool flashFlag; rndLightingEnable(FALSE); vecSub(cameraVector, nisCamera->lookatpoint, nisCamera->eyeposition); vecMultiplyByScalar(cameraVector, 200); vecAdd(cameraVector, cameraVector, nisCamera->lookatpoint); primLine3(&nisCamera->eyeposition, &cameraVector, flashFlag ? colWhite : colFuscia); vecScalarMultiply(upVector, nisCamera->upvector, 200); vecAdd(upVector, nisCamera->eyeposition, upVector); // upVector.z -= abs(nisCamera->eyeposition.z - cameraVector.z) + // abs(nisCamera->eyeposition.y - cameraVector.y); primLine3(&nisCamera->eyeposition, &upVector, colWhite); // if (selSelected.numShips > 0) // { // primLine3(&nisCamera->eyeposition, &selSelected.ShipPtr[0]->posinfo.position, flashFlag ? colWhite : colFuscia); // } flashFlag ^= TRUE; } #endif } #if FONT_CHECKSPECIAL fonthandle testing=1; extern fontregistry frFontRegistry[FR_NumberFonts]; #endif /*----------------------------------------------------------------------------- Name : rndPostRenderDebug2DStuff Description : Post-object rendering of 2D debug stuff Inputs : camera - same as parameter to rndMainViewRender Outputs : Return : ----------------------------------------------------------------------------*/ void rndPostRenderDebug2DStuff(Camera *camera) { #if LOD_PRINT_DISTANCE if (lodDrawingMode && selSelected.numShips == 1) { vector length; vector shippos = selSelected.ShipPtr[0]->collInfo.collPosition; sdword nCurrentLOD; if (!lodTuningMode) { nCurrentLOD = selSelected.ShipPtr[0]->currentLOD; } else { nCurrentLOD = rndLOD; } vecSub(length,camera->eyeposition,shippos); fontPrintf(0, MAIN_WindowHeight - fontHeight(" ") - 1, colWhite, "Current LOD = %d, distance = %f, x = %.1f y = %.1f z = %.1f", nCurrentLOD, vecMagnitudeSquared(length),shippos.x,shippos.y,shippos.z); } #endif #if GUN_TUNE_MODE if (gunTuningMode && (selSelected.numShips == 1)) { gunTuneGun(selSelected.ShipPtr[0]); } #endif #if RND_SCALECAP_TWEAK #ifndef khentschel fontPrint(0, 0, colWhite, scaleCapString); #endif #endif #if SHOW_TRAIL_STATS { char buf[256]; static lastUpdated; static lastNotUpdated; if (trailsUpdated == 0) { sprintf(buf, "ren %3d up %3d not %3d", trailsRendered, lastUpdated, lastNotUpdated); } else { sprintf(buf, "ren %3d up %3d not %3d", trailsRendered, trailsUpdated, trailsNotUpdated); lastUpdated = trailsUpdated; lastNotUpdated = trailsNotUpdated; } fontPrint(0, 32, colWhite, buf); trailsUpdated = trailsNotUpdated = 0; { extern sdword bTrailRender, bTrailDraw; sprintf(buf, "render %d draw %d", bTrailRender, bTrailDraw); fontPrint(0, 48, colWhite, buf); if (keyIsHit(SHIFTKEY)) { if (keyIsHit(NINEKEY)) bTrailRender = 1; if (keyIsHit(ZEROKEY)) bTrailDraw = 1; } else { if (keyIsHit(NINEKEY)) bTrailRender = 0; if (keyIsHit(ZEROKEY)) bTrailDraw = 0; } } } #endif #if USE_RND_HINT { char buf[32]; switch (rndHint) { case 0: sprintf(buf, "hint [default]"); break; case 1: sprintf(buf, "hint [on]"); break; case 2: sprintf(buf, "hint [off]"); break; default: sprintf(buf, "hint [?]"); } fontPrint(MAIN_WindowWidth - fontWidth(buf), fontHeight(buf), colWhite, buf); } #endif #if RND_FOV_TWEAK if (keyIsHit(NINEKEY)) { btgFieldOfView -= btgFieldOfViewDelta; sprintf(fovString, "fov = %f", btgFieldOfView); } if (keyIsHit(ZEROKEY)) { btgFieldOfView += btgFieldOfViewDelta; sprintf(fovString, "fov = %f", btgFieldOfView); } fontPrint(MAIN_WindowWidth - fontWidth(fovString), 0, colWhite, fovString); #endif //RND_FOV_TWEAK #if RND_SCALECAP_TWEAK if (keyIsHit(RND_CapScaleCapKey)) { if (keyIsHit(CONTROLKEY)) { RND_CAPSCALECAP_STATS = TRUE; } else { RND_CAPSCALECAP_STATS = FALSE; } } if (RND_CAPSCALECAP_STATS) { rndCapScaleCapStatsTaskFunction(); } #endif //RND_SCALECAP_TWEAK #if RND_DOCKLIGHT_TWEAK if (selSelected.numShips == 1) { Ship* ship = selSelected.ShipPtr[0]; if (ship->dockvars.reserveddocking != -1 && ship->dockvars.dockship != NULL) { vector veclen; real32 dist; Ship* dockship; char string[64]; dockship = ship->dockvars.dockship; vecSub(veclen, ship->posinfo.position, dockship->posinfo.position); dist = fsqrt(vecMagnitudeSquared(veclen)); sprintf(string, "%0.2f m", dist); fontPrint(0, 0, colWhite, string); } } #endif #if RND_FRAME_RATE if (rndDisplayFrameRate) { char string[256]; #if VERBOSE_SHIP_STATS sdword i; #endif extern sdword trTextureChanges, trAvoidedChanges; if (RGL) { sprintf(string, "(%d . %d) (%u) %u . %u . %u", trTextureChanges, trAvoidedChanges, alodGetPolys(), rglNumPolys(), rglCulledPolys(), meshRenders); } else { sprintf(string, "(%d . %d) (%u) . %u", trTextureChanges, trAvoidedChanges, alodGetPolys(), meshRenders); } trTextureChanges = 0; trAvoidedChanges = 0; meshRenders = 0; fontPrint(MAIN_WindowWidth - fontWidth(string), 0, colWhite, string); #if VERBOSE_SHIP_STATS for (i = 0; i < 5; i++) { sprintf(string, "%d: %d / %d", i, shipsAtLOD[i], polysAtLOD[i]); shipsAtLOD[i] = 0; polysAtLOD[i] = 0; fontPrint(MAIN_WindowWidth - fontWidth(string), (i+1)*fontHeight(string), colWhite, string); } #endif #if DISPLAY_LOD_SCALE sprintf(string, "scale %f", lodScaleFactor); fontPrintf(0, fontHeight(string), colWhite, string); #endif if (alodGetPanic()) { sprintf(string, "PANIC!"); fontPrint((MAIN_WindowWidth - fontWidth(string)) / 2, 0, colRGB(255,0,0), string); } if (universe.mainCameraCommand.currentCameraStack->focus.numShips == 1) { Ship* ship = universe.mainCameraCommand.currentCameraStack->focus.ShipPtr[0]; vector veclen; real32 dist; // vecSub(veclen, ship->posinfo.position, camera->eyeposition); vecSub(veclen, ship->collInfo.collPosition, camera->eyeposition); dist = fsqrt(vecMagnitudeSquared(veclen)); sprintf(string, "dist %0.2f m", dist); fontPrint((MAIN_WindowWidth - fontWidth(string)) / 2, fontHeight(" "), colWhite, string); } } #endif #if MEM_STATISTICS if (memStatsLogging) { sdword y = 0, x = 0, maxWidth = 0, width, index; fontPrint(0, y, colWhite, memStatString); for (index = 0; index < MS_NumberCookieNames; index++) { if (memStatsCookieNames[index].outputString[0] == 0) { continue; } y += fontHeight(" ") + 1; width = fontWidth(memStatsCookieNames[index].outputString); maxWidth = max(maxWidth, width); if (y >= MAIN_WindowHeight) { y = fontHeight(" ") + 1; x += maxWidth + 1; maxWidth = 0; if (x > MAIN_WindowWidth) { break; } } fontPrint(x, y, colWhite, memStatsCookieNames[index].outputString); } } #endif #if DEM_CHECKSUM if (demChecksumError) { fontPrint(2, MAIN_WindowHeight - 60, colWhite, demChecksumString); } #endif #if FONT_CHECKSPECIAL fontMakeCurrent(testing); fontPrint(10,50,colWhite,"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"); if ((keyIsStuck(CONTROLKEY)) && (keyIsHit(SHIFTKEY))) { testing--; if (testing < 1) testing = 1; keyClearSticky(CONTROLKEY); } else if (keyIsStuck(CONTROLKEY)) { testing++; if (frFontRegistry[testing].name == NULL) { testing--; } keyClearSticky(CONTROLKEY); } #endif trkTrackValuesDisplay(); } /*----------------------------------------------------------------------------- Name : rndMainViewRenderNothingFunction Description : Don't do anything; just return. For when something else is already rendering the screen. Inputs : camera - it's not even used Outputs : Return : void ----------------------------------------------------------------------------*/ void rndMainViewRenderNothingFunction(Camera *camera) { return; } /*----------------------------------------------------------------------------- Name : rndMainViewAllButRenderFunction Description : Compute selection info for space obj's but don't render them Inputs : Outputs : Return : Note : Because this function mirrors much of the functionality of rndMainViewRenderFunction, any drastic changes to rndMainViewRenderFunction will have to be made this function. ----------------------------------------------------------------------------*/ void rndMainViewAllButRenderFunction(Camera *camera) { ; } /*----------------------------------------------------------------------------- Name : rndMainViewRenderFunction Description : Render a mission sphere as referenced by a specific camera. Inputs : camera - pointer to a camera structure which includes a mission sphere. Outputs : ?? Return : void Note : Because much of the functionality of this function is mirrored in rndMainViewAllButRenderFunction, any drastic changes to rndMainViewRenderFunction will have to be made to rndMainViewAllButRenderFunction. ----------------------------------------------------------------------------*/ void rndMainViewRenderFunction(Camera *camera) { Node *objnode; bool twopass; SpaceObj *spaceobj; udword i,numstars; Star3d *star; static sdword init = FALSE; // hvector cameraSpace, eyeSpace; vector to; lod *level; ShipStaticInfo *shipStaticInfo; sdword playerIndex; hmatrix coordMatrixForGL; // matrix scaledMatrix; // real32 scaleFactor; // meshdata *worldMesh; Effect *effect; static sdword shipTrails; extern sdword trailsUpdated, trailsNotUpdated, trailsRendered; sdword colorScheme; bool displayEffect; sdword asteroid0Count; real32 scaledOffset[3]; static real32 cameraOffset[3] = {0.0f, 0.0f, 0.0f}; static bool cameraFloating = FALSE; mouseCursorObjPtr = NULL; //Falko: got an obscure crash where mouseCursorObjPtr was mangled, will this work? dbgAssert(camera != NULL); //verify parameters if (!binkDonePlaying) { //!!! can be cleaned up using the render function pointer return; } if (RGL) { rglFeature(RGL_LOCK); //exclusive lock on the framebuffer } primModeClear2(); //go to 3D rendering mode glMatrixMode(GL_PROJECTION); glLoadIdentity(); rgluPerspective(camera->fieldofview, rndAspectRatio, //set projection matrix rndNear(camera->clipPlaneNear), camera->clipPlaneFar); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (gameIsRunning && !nisIsRunning && piePointSpecMode == PSM_Idle && !universePause) { if (!cameraFloating) { rndResetFloatTime(); } cameraFloating = TRUE; } else { cameraFloating = FALSE; if (nisIsRunning) { cameraOffset[0] = cameraOffset[1] = cameraOffset[2] = 0.0f; } } if (bitTest(universe.mainCameraCommand.ccMode,CCMODE_PILOT_SHIP)) { cameraFloating = FALSE; cameraOffset[0] = cameraOffset[1] = cameraOffset[2] = 0.0f; } if (cameraFloating) { cameraOffset[0] = CAMERA_FLOAT_OFFSET_SCALAR0 * rndCameraOffset0(); cameraOffset[1] = CAMERA_FLOAT_OFFSET_SCALAR1 * rndCameraOffset1(); cameraOffset[2] = (0.5f * cameraOffset[0]) + (0.5f * cameraOffset[1]); } rndScaleCameraOffsets(scaledOffset, camera, cameraOffset); rgluLookAt(camera->eyeposition.x + scaledOffset[2], camera->eyeposition.y + scaledOffset[0], camera->eyeposition.z + scaledOffset[1], camera->lookatpoint.x, camera->lookatpoint.y, camera->lookatpoint.z, camera->upvector.x, camera->upvector.y, camera->upvector.z); //get local copy of matrices glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)(&rndCameraMatrix)); glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat *)(&rndProjectionMatrix)); //position light(s) in world lightPositionSet(); rndPreRenderDebugStuff(camera); //!!! should this be here? It does not sound like it. if (feRenderEverything) { glClear(GL_COLOR_BUFFER_BIT); feRenderEverything = FALSE; } //draw the texture-mapped background #if RND_BACKGROUND_STATIC glPushMatrix(); glTranslatef(camera->eyeposition.x, camera->eyeposition.y, camera->eyeposition.z); #endif primErrorMessagePrint(); numstars=universe.star3dinfo->Num3dStars; if (stararray == NULL) { c4ub_v3f* ptr; stararray = (c4ub_v3f*)memAlloc((numstars - NUM_BIG_STARS) * sizeof(c4ub_v3f), "stararray", NonVolatile); bigstararray = (c4ub_v3f*)memAlloc(NUM_BIG_STARS * sizeof(c4ub_v3f), "bigstararray", NonVolatile); ptr = stararray; for (i = 0, star = universe.star3dinfo->Stars; i < (numstars - NUM_BIG_STARS); i++, star++, ptr++) { ptr->c[0] = star->r; ptr->c[1] = star->g; ptr->c[2] = star->b; ptr->c[3] = 200; ptr->v[0] = star->position.x; ptr->v[1] = star->position.y; ptr->v[2] = star->position.z; } for (ptr = bigstararray; i < numstars; i++, star++, ptr++) { ptr->c[0] = star->r; ptr->c[1] = star->g; ptr->c[2] = star->b; ptr->c[3] = 200; ptr->v[0] = star->position.x; ptr->v[1] = star->position.y; ptr->v[2] = star->position.z; } } rndLightingEnable(FALSE); //stars are self-illuminated primErrorMessagePrint(); glDisable(GL_DEPTH_TEST); //and infinitely far away primErrorMessagePrint(); if (singlePlayerGame && singlePlayerGameInfo.currentMission == 13) { rndBackgroundRender(BG_RADIUS, camera, !showBackgrounds); } else { rndBackgroundRender(BG_RADIUS, camera, TRUE); } rndTextureEnvironment(RTE_Modulate); glEnable(GL_DEPTH_TEST); //re-enable depth test primErrorMessagePrint(); rndLightingEnable(TRUE); //and lighting primErrorMessagePrint(); #if RND_BACKGROUND_STATIC glPopMatrix(); #endif #if !LIGHT_PLAYER_COLORS // lightDefaultLightSet(); lightSetLighting(); #endif //render all worlds in the universe for (i = 0; i < UNIV_NUMBER_WORLDS; i++) { if (universe.world[i] != NULL) { //if this one defined if (universe.world[i]->objtype == OBJ_DerelictType) { //if it's a derelict rndRenderAHomeworld(camera, universe.world[i]); } else { //else it must be a world effect dbgAssert(universe.world[i]->objtype == OBJ_EffectType); rndRenderAWorldEffect(camera, (Effect *)universe.world[i]); } } else { //stop processing worlds break; } } if (rndPreObjectCallback != NULL) { //render the pre-object callback if needed rendercallback rc = rndPreObjectCallback; rndPreObjectCallback = NULL; rc(); } if (RGL && rndFogOn) { glEnable(GL_FOG); } #if PIE_MOVE_NEARTO //clear out the 'move near to' info selClosestTarget = NULL; selClosestDistance = SDWORD_Max; #endif //PIE_MOVE_NEARTO trailsRendered = shipTrails = 0; alodSetPolys(0); #if WILL_TWO_PASS if (RGL && (RGLtype == SWtype)) { twopass = TRUE; } else { twopass = FALSE; } #else twopass = FALSE; #endif if (twopass) { objnode = universe.RenderList.tail; } else { objnode = universe.RenderList.head; } while (objnode != NULL) { spaceobj = (SpaceObj *)listGetStructOfNode(objnode); g_WireframeHack = FALSE; rndPerspectiveCorrection(FALSE); switch (spaceobj->objtype) { case OBJ_BulletType: //type bullet if (((Bullet *)spaceobj)->effect == NULL) { //if there is no effect for this bullet rndLightingEnable(FALSE); //draw default line rndTextureEnable(FALSE); if(((Bullet *)spaceobj)->bulletType == BULLET_Laser) { //bullet update 'feature' to draw laser to bullet defenseFighterAdjustLaser(((Bullet *)spaceobj)); } else if (((Bullet *)spaceobj)->bulletType == BULLET_Beam) { if(((Bullet *)spaceobj)->timelived <= UNIVERSE_UPDATE_PERIOD) { goto dontdraw; } } vecAdd(to,spaceobj->posinfo.position,((Bullet *)spaceobj)->lengthvec); primLine3(&spaceobj->posinfo.position, &to, ((Bullet *)spaceobj)->bulletColor); dontdraw: rndLightingEnable(TRUE); } break; case OBJ_EffectType: //if type effect if (!twopass) { effect = (Effect *)spaceobj; //get effect pointer if(effect->owner != NULL) { SpaceObj *effectowner = (SpaceObj *)effect->owner; if(effectowner->objtype == OBJ_BulletType) { //effect is owned by a bullet if(((Bullet *) effectowner)->bulletType == BULLET_Laser) { //effect is owned by a defense fighter laser beam defenseFighterAdjustLaser(((Bullet *) effectowner)); etgEffectUpdate(effect, 0.0f); //luke suggested fix to laser length problem } else if(((Bullet *) effectowner)->bulletType == BULLET_Beam) { if(((Bullet *)effectowner)->timelived <= UNIVERSE_UPDATE_PERIOD) { goto dontdraw2; } } } } etgEffectDraw(effect); dontdraw2:; } break; case OBJ_ShipType: if (spaceobj->staticinfo->staticheader.LOD != NULL) { if(!bitTest(spaceobj->flags,SOF_Cloaked) || ((Ship *)spaceobj)->playerowner == universe.curPlayerPtr || (proximityCanPlayerSeeShip(universe.curPlayerPtr,((Ship *)spaceobj)))) //if ship isn't cloaked or it is the players { //put the ship into the mission map array // mmAddShipToMapArray((ShipPtr)spaceobj); shipStaticInfo = (ShipStaticInfo *)spaceobj->staticinfo; playerIndex = (sdword)((Ship *)spaceobj)->playerowner->playerIndex; dbgAssert((spaceobj->flags & SOF_Rotatable) != 0); selCircleCompute(&rndCameraMatrix, &rndProjectionMatrix, (SpaceObjRotImpTarg *)spaceobj);//compute selection circle #if RND_VISUALIZATION #ifdef DEBUG_COLLISIONS collDrawCollisionInfo((SpaceObjRotImp *)spaceobj); #else if (RENDER_BOXES) { collDrawCollisionInfo((SpaceObjRotImp *)spaceobj); } #endif #endif glPushMatrix(); level = lodLevelGet((void *)spaceobj, &camera->eyeposition, &((SpaceObjRotImp *)spaceobj)->collInfo.collPosition); if (taskTimeElapsed-((Ship *)spaceobj)->flashtimer < FLASH_TIMER) { g_ReplaceHack = TRUE; if (RGL) { glPixelTransferf(GL_RED_BIAS, 0.3f); glPixelTransferf(GL_GREEN_BIAS, 0.3f); glPixelTransferf(GL_BLUE_BIAS, 0.3f); } } switch (level->flags & LM_LODType) { case LT_Invalid: break; case LT_Mesh: displayEffect = hsShouldDisplayEffect((Ship*)spaceobj); #if LIGHT_PLAYER_COLORS lightPlayerColorLightSet(playerIndex); #endif //LIGHT_PLAYER_COLORS hmatMakeHMatFromMat(&coordMatrixForGL,&((SpaceObjRot *)spaceobj)->rotinfo.coordsys); hmatPutVectIntoHMatrixCol4(spaceobj->posinfo.position,coordMatrixForGL); glMultMatrixf((float *)&coordMatrixForGL);//ship's rotation matrix shPushLightMatrix(&coordMatrixForGL); if (selCameraSpace.z < 0.0f) { //((Ship *)spaceobj)->magnitudeSquared = 1.0f - selCameraSpace.z * shipStaticInfo->NLIPS; #if RND_SCALECAP_TWEAK if (selSelected.numShips == 1 && selSelected.ShipPtr[0]->staticinfo == shipStaticInfo) { if (keyIsHit(ONEKEY)) { shipStaticInfo->scaleCap -= scaleCapSlopeDelta; dbgMessagef(scaleCapString, "\nN-LIPS = %f", shipStaticInfo->scaleCap); sprintf(scaleCapString, "N-LIPS = %f", shipStaticInfo->scaleCap); } if (keyIsHit(TWOKEY)) { shipStaticInfo->scaleCap += scaleCapSlopeDelta; dbgMessagef(scaleCapString, "\nN-LIPS = %f", shipStaticInfo->scaleCap); sprintf(scaleCapString, "N-LIPS = %f", shipStaticInfo->scaleCap); } } #endif //RND_SCALECAP_TWEAK if (shipStaticInfo->scaleCap != 0.0f) { //if there's a scaling cap real32 scaleFactor; if (glCapFeatureExists(GL_RESCALE_NORMAL)) { glEnable(GL_RESCALE_NORMAL); rndNormalization = TRUE; } else { rndNormalizeEnable(TRUE); } scaleFactor = 1.0f - selCameraSpace.z * shipStaticInfo->scaleCap; #if RND_VERBOSE_LEVEL >= 1 if (_isnan((double)scaleFactor)) { dbgMessage("\n-- scaleFactor is NaN --"); } #endif ((Ship *)spaceobj)->magnitudeSquared = scaleFactor; glScalef(scaleFactor, scaleFactor, scaleFactor); ((Ship *)spaceobj)->collInfo.selCircleRadius *= scaleFactor; } else { ((Ship *)spaceobj)->magnitudeSquared = 1.0f; } } else //don't even bother rendering if behind camera { ((Ship *)spaceobj)->magnitudeSquared = 1.0f; //!!! break; } #if SO_CLOOGE_SCALE if (shipStaticInfo->scaleFactor != 1.0f) { glScalef(shipStaticInfo->scaleFactor, shipStaticInfo->scaleFactor, shipStaticInfo->scaleFactor); } #endif #if RND_VISUALIZATION if (dockLines) dockDrawDockInfo((Ship *)spaceobj); if (gunLines) gunDrawGunInfo((Ship *)spaceobj); if (dockLines) dockDrawSalvageInfo((SpaceObjRotImpTargGuidanceShipDerelict *)spaceobj); #endif /* switch (shipStaticInfo->shiprace) { case R1: case R2: i = ((Ship *)spaceobj)->playerowner->playerIndex; dbgAssert(i >= 0 && i < 8);//!!! break; default: i = 0; break; } */ i = ((Ship *)spaceobj)->colorScheme; dbgAssert(i >= 0 && i < TE_NumberPlayers); if (bitTest(spaceobj->flags,SOF_Cloaked)) { g_WireframeHack = (bool8)((((Ship *)spaceobj)->playerowner == universe.curPlayerPtr) || proximityCanPlayerSeeShip(universe.curPlayerPtr,((Ship *)spaceobj))); } if (rndShipVisible(spaceobj, camera)) { bool result = rndFade(spaceobj, camera); Ship* ship = (Ship*)spaceobj; ShipSinglePlayerGameInfo* ssinfo = ship->shipSinglePlayerGameInfo; if (ssinfo != NULL) { if (ssinfo->shipHyperspaceState == SHIPHYPERSPACE_NONE) { ssinfo = NULL; } } if (!result) { if (spaceobj->currentLOD == 0) { rndPerspectiveCorrection(TRUE); } meshRenders++; if (rndInsideShip(spaceobj, camera)) { g_WireframeHack = TRUE; g_HiddenRemoval = FALSE; } if (ssinfo != NULL && ssinfo->hsState != HS_INACTIVE && ssinfo->hsState != HS_FINISHED) { if (ssinfo->staticHyperspaceGate) { hsNoGate(TRUE); } hsContinue((Ship*)spaceobj, displayEffect); if (ssinfo->staticHyperspaceGate) { hsNoGate(FALSE); } } // // lighting while docking // if (((Ship*)spaceobj)->dockvars.reserveddocking != -1 && ((Ship*)spaceobj)->dockvars.dockship != NULL) { Ship* dockship; real32 t, nearVal, farVal; dockship = ((Ship*)spaceobj)->dockvars.dockship; nearVal = dockship->staticinfo->dockLightNear; farVal = dockship->staticinfo->dockLightFar; t = rndDockScalar((Ship*)spaceobj, ((Ship*)spaceobj)->dockvars.dockship, nearVal, farVal); shDockLightColor(dockship->staticinfo->dockLightColor); shDockLight(t); } if ((ssinfo != NULL && (ssinfo->hsState != HS_FINISHED || singlePlayerGameInfo.hyperspaceFails)) || ssinfo == NULL) { extern sdword visiblePoly; extern bool g_SpecificPoly; extern bool g_Points; if (((Ship *)spaceobj)->bindings != NULL) { meshRenderShipHierarchy(((Ship *)spaceobj)->bindings, ((Ship *)spaceobj)->currentLOD, (meshdata *)level->pData, i); #if VISIBLE_POLYS if (visiblePoly >= 0) { g_SpecificPoly = TRUE; g_WireframeHack = TRUE; glScalef(1.02f, 1.02f, 1.02f); meshRenderShipHierarchy(((Ship*)spaceobj)->bindings, ((Ship*)spaceobj)->currentLOD, (meshdata*)level->pData, i); g_WireframeHack = FALSE; glScalef(1.01f, 1.01f, 1.01f); g_Points = TRUE; meshRenderShipHierarchy(((Ship*)spaceobj)->bindings, ((Ship*)spaceobj)->currentLOD, (meshdata*)level->pData, i); g_Points = FALSE; g_SpecificPoly = FALSE; } #endif } else { meshRender((meshdata *)level->pData, i); #if VISIBLE_POLYS if (visiblePoly >= 0) { g_SpecificPoly = TRUE; g_WireframeHack = TRUE; glScalef(1.02f, 1.02f, 1.02f); meshRender((meshdata *)level->pData, i); g_WireframeHack = FALSE; glScalef(1.01f, 1.01f, 1.01f); g_Points = TRUE; meshRender((meshdata *)level->pData, i); g_Points = FALSE; g_SpecificPoly = FALSE; } #endif } spaceobj->renderedLODs |= (1 << spaceobj->currentLOD); rndPerspectiveCorrection(FALSE); //navlights if (!bitTest(spaceobj->flags, SOF_Cloaked)) { RenderNAVLights((Ship*)spaceobj); } } g_WireframeHack = FALSE; g_HiddenRemoval = TRUE; shDockLight(0.0f); #if VERBOSE_SHIP_STATS if (rndDisplayFrameRate) { shipsAtLOD[spaceobj->currentLOD]++; { meshdata* mesh = (meshdata*)level->pData; polysAtLOD[spaceobj->currentLOD] += mesh->object[0].nPolygons; } } #endif } rndUnFade(); } shPopLightMatrix(); if (glCapFeatureExists(GL_RESCALE_NORMAL)) { glDisable(GL_RESCALE_NORMAL); rndNormalization = FALSE; } else { rndNormalizeEnable(FALSE); } break; case LT_TinySprite: dbgFatalf(DBG_Loc, "Unsupported LOD type 0x%x", level->flags); break; case LT_SubPixel: rndTextureEnable(FALSE); rndLightingEnable(FALSE); primPointSize3(&spaceobj->posinfo.position, ((Ship *)spaceobj)->collInfo.selCircleRadius, toPlayerColor[playerIndex]); rndLightingEnable(TRUE); break; case LT_Function: dbgFatalf(DBG_Loc, "Unsupported LOD type 0x%x", level->flags); break; case LT_NULL: break; default: dbgFatalf(DBG_Loc, "Unsupported LOD type 0x%x", level->flags); } if (g_ReplaceHack) { g_ReplaceHack = FALSE; if (RGL) { glPixelTransferf(GL_RED_BIAS, 0.0f); glPixelTransferf(GL_GREEN_BIAS, 0.0f); glPixelTransferf(GL_BLUE_BIAS, 0.0f); } } { udword index; for (index = 0; index < 2; index++) { if (((Ship*)spaceobj)->lightning[index] != NULL) { lightning* l = (lightning*)((Ship*)spaceobj)->lightning[index]; cloudRenderAndUpdateLightning(l, spaceobj->currentLOD); l->countdown--; if (l->countdown == 0) { cloudKillLightning(l); ((Ship*)spaceobj)->lightning[index] = NULL; } } } } glPopMatrix(); { Ship* ship = (Ship*)spaceobj; ShipSinglePlayerGameInfo* ssinfo = ship->shipSinglePlayerGameInfo; if (ssinfo != NULL && ssinfo->hsState != HS_INACTIVE && ssinfo->hsState != HS_FINISHED) { if (ssinfo->staticHyperspaceGate) { hsNoGate(TRUE); } hsEnd((Ship*)spaceobj, displayEffect); if (ssinfo->staticHyperspaceGate) { hsNoGate(FALSE); } } } rndPerspectiveCorrection(FALSE); rndFade(spaceobj, camera); if (!bitTest(spaceobj->flags, SOF_Cloaked) || (((Ship*)spaceobj)->playerowner == universe.curPlayerPtr) || proximityCanPlayerSeeShip(universe.curPlayerPtr,(Ship*)spaceobj)) { udword idx; for (idx = 0; idx < MAX_NUM_TRAILS; idx++) { if (((Ship*)spaceobj)->trail[idx] != NULL) { trailDraw(&((Ship*)spaceobj)->enginePosition, ((Ship*)spaceobj)->trail[idx], spaceobj->currentLOD, ((Ship *)spaceobj)->colorScheme); } } } rndUnFade(); } } else { //else behind camera ((Ship *)spaceobj)->collInfo.selCircleRadius = -1.0f; } break; case OBJ_DerelictType: if (((Derelict *)spaceobj)->staticinfo->worldRender) { //if it's a world derelict break; //render as normal } if (((Derelict *)spaceobj)->derelicttype == HyperspaceGate) { selCircleCompute(&rndCameraMatrix, &rndProjectionMatrix, (SpaceObjRotImpTarg *)spaceobj);//compute selection circle #if RND_VISUALIZATION if (RENDER_BOXES) { collDrawCollisionInfo((SpaceObjRotImp *)spaceobj); } #endif break; } //else fall through case OBJ_AsteroidType: case OBJ_DustType: case OBJ_GasType: case OBJ_MissileType: //cloaked effect hack...change later when implement 'stages maybe' if(bitTest(spaceobj->flags,SOF_Cloaked)) break; //if it isn't cloaked, or it is and it is the local players field draw it if (spaceobj->staticinfo->staticheader.LOD != NULL) { dbgAssert((spaceobj->flags & (SOF_Rotatable+SOF_Impactable+SOF_Targetable)) != 0); //add this space object to the mission map array // mmAddObjToMapArray(spaceobj); selCircleCompute(&rndCameraMatrix, &rndProjectionMatrix, (SpaceObjRotImpTarg *)spaceobj);//compute selection circle #if RND_VISUALIZATION #ifdef DEBUG_COLLISIONS collDrawCollisionInfo((SpaceObjRotImp *)spaceobj); #else if (RENDER_BOXES) { collDrawCollisionInfo((SpaceObjRotImp *)spaceobj); } #endif #endif glPushMatrix(); level = lodLevelGet((void *)spaceobj, &camera->eyeposition, &((SpaceObjRotImp *)spaceobj)->collInfo.collPosition); if (taskTimeElapsed-((Ship *)spaceobj)->flashtimer < FLASH_TIMER) { g_ReplaceHack = TRUE; if (RGL) { glPixelTransferf(GL_RED_BIAS, 0.3f); glPixelTransferf(GL_GREEN_BIAS, 0.3f); glPixelTransferf(GL_BLUE_BIAS, 0.3f); } } switch (level->flags & LM_LODType) { case LT_Invalid: break; case LT_Mesh: #if LIGHT_PLAYER_COLORS // lightDefaultLightSet(); //!!! lightSetLighting(); #endif g_RndPosition = spaceobj->posinfo.position; hmatMakeHMatFromMat(&coordMatrixForGL,&((SpaceObjRot *)spaceobj)->rotinfo.coordsys); hmatPutVectIntoHMatrixCol4(spaceobj->posinfo.position,coordMatrixForGL); glMultMatrixf((float *)&coordMatrixForGL);//ship's rotation matrix shPushLightMatrix(&coordMatrixForGL); if (spaceobj->objtype == OBJ_AsteroidType) { real32 scaling = ((Asteroid *)spaceobj)->scaling; if (scaling != 1.0f) { glScalef(scaling,scaling,scaling); } } #ifndef HW_Release if(spaceobj->objtype == OBJ_DerelictType) if(((Derelict *)spaceobj)->derelicttype < NUM_DERELICTTYPES) if (dockLines) dockDrawSalvageInfo((SpaceObjRotImpTargGuidanceShipDerelict *)spaceobj); #endif colorScheme = 0; switch (spaceobj->objtype) { case OBJ_GasType: case OBJ_DustType: rndFade(spaceobj, camera); meshRenders++; cloudRenderSystem((void*)level->pData, ((DustCloud*)spaceobj)->stub, spaceobj->currentLOD); // shipsAtLOD[spaceobj->currentLOD]++; rndUnFade(); break; case OBJ_MissileType: colorScheme = ((Missile *)spaceobj)->colorScheme; goto renderDefault; case OBJ_DerelictType: colorScheme = ((Derelict *)spaceobj)->colorScheme; //fall through renderDefault: default: if (rndShipVisible(spaceobj, camera)) { rndFade(spaceobj, camera); if (spaceobj->currentLOD == 0) { rndPerspectiveCorrection(TRUE); } rndGLStateLog("Before Derelict"); meshRenders++; meshRender((meshdata *)level->pData,colorScheme); spaceobj->renderedLODs |= (1 << spaceobj->currentLOD); rndGLStateLog("After Derelict"); //navlights if (spaceobj->objtype == OBJ_DerelictType && !bitTest(spaceobj->flags, SOF_Cloaked)) { RenderNAVLights((Ship*)spaceobj); } rndPerspectiveCorrection(FALSE); #if VERBOSE_SHIP_STATS if (rndDisplayFrameRate) { shipsAtLOD[spaceobj->currentLOD]++; { meshdata* mesh = (meshdata*)level->pData; polysAtLOD[spaceobj->currentLOD] += mesh->object[0].nPolygons; } } #endif rndUnFade(); } } shPopLightMatrix(); break; case LT_TinySprite: dbgFatalf(DBG_Loc, "Unsupported LOD type 0x%x", level->flags); break; case LT_SubPixel: rndTextureEnable(FALSE); rndLightingEnable(FALSE); primPointSize3(&spaceobj->posinfo.position, ((Ship *)spaceobj)->collInfo.selCircleRadius, spaceobj->staticinfo->staticheader.LOD->pointColor); rndLightingEnable(TRUE); break; case LT_Function: dbgFatalf(DBG_Loc, "Unsupported LOD type 0x%x", level->flags); break; case LT_NULL: break; default: dbgFatalf(DBG_Loc, "Unsupported LOD type 0x%x", level->flags); } if (g_ReplaceHack) { g_ReplaceHack = FALSE; if (RGL) { glPixelTransferf(GL_RED_BIAS, 0.0f); glPixelTransferf(GL_GREEN_BIAS, 0.0f); glPixelTransferf(GL_BLUE_BIAS, 0.0f); } } glPopMatrix(); if (spaceobj->objtype == OBJ_MissileType) { Missile* mis = (Missile*)spaceobj; if (mis->trail != NULL) { playerIndex = (sdword)mis->playerowner->playerIndex; mistrailDraw(&mis->enginePosition, mis->trail, spaceobj->currentLOD, mis->colorScheme); } } } break; case OBJ_NebulaType: //do nothing break; default: dbgFatalf(DBG_Loc, "Undefined object type %d", spaceobj->objtype); } if (twopass) { objnode = objnode->prev; } else { objnode = objnode->next; } } if (twopass) { objnode = universe.RenderList.head; while (objnode != NULL) { spaceobj = (SpaceObj*)listGetStructOfNode(objnode); g_WireframeHack = FALSE; rndPerspectiveCorrection(FALSE); switch (spaceobj->objtype) { case OBJ_EffectType: effect = (Effect *)spaceobj; //get effect pointer if(effect->owner != NULL) { SpaceObj *effectowner = (SpaceObj *)effect->owner; if(effectowner->objtype == OBJ_BulletType) { //effect is owned by a bullet if(((Bullet *) effectowner)->bulletType == BULLET_Laser) { //effect is owned by a defense fighter laser beam defenseFighterAdjustLaser(((Bullet *) effectowner)); etgEffectUpdate(effect, 0.0f); //luke suggested fix to laser length problem } else if(((Bullet *) effectowner)->bulletType == BULLET_Beam) { if(((Bullet *)effectowner)->timelived <= UNIVERSE_UPDATE_PERIOD) { goto dontdraw3; } } } } etgEffectDraw(effect); dontdraw3:; } objnode = objnode->next; } } // // minor renderlist (asteroid0 list) // rndTextureEnable(FALSE); rndLightingEnable(FALSE); g_WireframeHack = FALSE; glPointSize(1.0f); asteroid0Count = 0; objnode = universe.MinorRenderList.head; while (objnode != NULL) { spaceobj = (SpaceObj*)listGetStructOfNode(objnode); switch (spaceobj->objtype) { case OBJ_AsteroidType: asteroid0Data[asteroid0Count].c = spaceobj->staticinfo->staticheader.LOD->pointColor; asteroid0Data[asteroid0Count].position = &spaceobj->posinfo.position; asteroid0Count++; if (asteroid0Count == ASTEROID0DATA_SIZE) { rndDrawAsteroid0(asteroid0Count); asteroid0Count = 0; } break; default: dbgFatalf(DBG_Loc, "MinorRenderList contains invalid object type %d", spaceobj->objtype); } objnode = objnode->next; } if (asteroid0Count != 0) { rndDrawAsteroid0(asteroid0Count); } rndLightingEnable(TRUE); //hyperspace hsStaticRender(); alodAdjustScaleFactor(); rndPerspectiveCorrection(FALSE); if (RGL) { glDisable(GL_FOG); } nebRender(); if (rndPostObjectCallback != NULL) { //render the post-object callback if needed rndPostObjectCallback(); rndPostObjectCallback = NULL; } rndPostRenderDebug3DStuff(camera); primModeSet2(); rndPostRenderDebug2DStuff(camera); if (RGL) { rglFeature(RGL_UNLOCK); //remove our exclusive lock } } GLuint plug_handle = 0; sdword plug_width, plug_height; typedef struct tagTGAHeader { 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; } TGAHeader; //rndLoadTarga udword rndLoadTarga(char* filename, sdword* width, sdword* height) { TGAHeader head; ubyte* data; ubyte* pdata; ubyte* bp; uword* psdata; sdword i; GLuint handle; fileLoadAlloc(filename, (void**)&data, 0); pdata = data; head.idLength = *pdata++; head.colorMapType = *pdata++; head.imageType = *pdata++; psdata = (uword*)pdata; head.colorMapStartIndex = *psdata; pdata += 2; psdata = (uword*)pdata; head.colorMapNumEntries = *psdata; pdata += 2; head.colorMapBitsPerEntry = *pdata++; psdata = (uword*)pdata; head.imageOffsetX = (sword)*psdata; pdata += 2; psdata = (uword*)pdata; head.imageOffsetY = (sword)*psdata; pdata += 2; psdata = (uword*)pdata; head.imageWidth = *psdata; pdata += 2; psdata = (uword*)pdata; head.imageHeight = *psdata; pdata += 2; head.pixelDepth = *pdata++; head.imageDescriptor = *pdata++; dbgAssert(head.pixelDepth == 32); pdata += head.idLength; *width = (sdword)head.imageWidth; *height = (sdword)head.imageHeight; 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]); } glGenTextures(1, &handle); trClearCurrent(); glBindTexture(GL_TEXTURE_2D, handle); 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); return handle; } /*----------------------------------------------------------------------------- Name : rndLoadShamelessPlug Description : loads or frees the Relic shameless plug texture Inputs : on - TRUE or FALSE (load or free) Outputs : plug_handle is either valid or invalid Return : ----------------------------------------------------------------------------*/ void rndLoadShamelessPlug(bool on) { if (on) { plug_handle = rndLoadTarga("plug.tga", &plug_width, &plug_height); } else { if (plug_handle != 0) { glDeleteTextures(1, &plug_handle); plug_handle = 0; } } } /*----------------------------------------------------------------------------- Name : rndShamelessPlug Description : displays the Relic shameless plug in bottom right corner of screen Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void rndShamelessPlug() { GLboolean blendOn, depthOn; GLfloat fwidth, fheight; GLint matrixMode; sdword lightOn; GLfloat projection[16]; GLfloat winWidth, winHeight; #ifdef fpoiker return; #endif #if RND_PLUG_DISABLEABLE if (!rndShamelessPlugEnabled) { return; } #endif if (plug_handle == 0) { rndLoadShamelessPlug(TRUE); } fwidth = (GLfloat)plug_width; fheight = (GLfloat)plug_height; winWidth = (GLfloat)MAIN_WindowWidth; winHeight = (GLfloat)MAIN_WindowHeight; glGetIntegerv(GL_MATRIX_MODE, &matrixMode); glGetFloatv(GL_PROJECTION_MATRIX, projection); glMatrixMode(GL_PROJECTION); glLoadIdentity(); rgluOrtho2D(0.0f, winWidth, 0.0f, winHeight); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); blendOn = glIsEnabled(GL_BLEND); lightOn = rndLightingEnable(FALSE); depthOn = glIsEnabled(GL_DEPTH_TEST); if (!blendOn) glEnable(GL_BLEND); if (depthOn) glDisable(GL_DEPTH_TEST); winWidth -= 1.0f; winHeight -= 1.0f; if (RGLtype == SWtype) { glTexCoord2f(winWidth - fwidth, 0); trClearCurrent(); glBindTexture(GL_TEXTURE_2D, plug_handle); glDrawPixels(plug_width, plug_height, GL_RGBA, GL_UNSIGNED_BYTE, NULL); } else { rndTextureEnable(TRUE); rndAdditiveBlends(FALSE); trClearCurrent(); glBindTexture(GL_TEXTURE_2D, plug_handle); glColor4ub(255,255,255,255); glBegin(GL_QUADS); glTexCoord2f(0.0f, 1.0f); glVertex2f(winWidth - fwidth, fheight); glTexCoord2f(0.0f, 0.0f); glVertex2f(winWidth - fwidth, 0); glTexCoord2f(1.0f, 0.0f); glVertex2f(winWidth, 0); glTexCoord2f(1.0f, 1.0f); glVertex2f(winWidth, fheight); glEnd(); } glPopMatrix(); glMatrixMode(GL_PROJECTION); glLoadMatrixf(projection); if (matrixMode == GL_MODELVIEW) { glMatrixMode(GL_MODELVIEW); } rndLightingEnable(lightOn); if (!blendOn) glDisable(GL_BLEND); if (depthOn) glEnable(GL_DEPTH_TEST); } extern udword receivedPacketNumber; static udword printLagMinFrames = 0; extern udword PRINTLAG_IFGREATERTHAN; extern udword PRINTLAG_MINFRAMES; /*----------------------------------------------------------------------------- Name : rndDrawOnScreenSyncStatus Description : Prints sync status to screen Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void rndDrawOnScreenSyncStatus(void) { #if SYNC_CHECK if ((multiPlayerGame|playPackets) && (gameIsRunning)) { sdword y; udword rsyncpkts = queueNumberEntries(ProcessSyncPktQ); if (randSyncErr) { fontPrintf(400,10,colWhite,"RAND SYNC ERR: Frame %d",randSyncErrFrame); } if (univSyncErr) { fontPrintf(400,20,colWhite,"UNIV SYNC ERR: Frame %d",univSyncErrFrame); } if ((blobSyncErr) && (multiPlayerGame)) // only print cheat detect in multiPlayerGame { fontPrintf(400,30,colWhite,strGetString(strCheatDetect),blobSyncErrFrame); } if (pktSyncErr) { fontPrint(400,40,colWhite,"PKT SYNC ERR"); } y = 50; if ((printLagMinFrames) || (rsyncpkts > PRINTLAG_IFGREATERTHAN)) { if (rsyncpkts > PRINTLAG_IFGREATERTHAN) printLagMinFrames = PRINTLAG_MINFRAMES; else if (printLagMinFrames) printLagMinFrames--; //fontPrintf(400,y,colWhite,"Lag >= %3.3f",(real32)(rsyncpkts * CAPTAINSERVER_PERIOD)); lagSlowComputerIcon(); } lagSlowInternetIcon(); if ((playPackets) && (!recordplayPacketsInGame)) { fontPrintf(400,y,colWhite,"%d %f",receivedPacketNumber,universe.totaltimeelapsed); y += 10; } /* if (multiPlayerGame) { sdword i; for (i=0;i= 1 extern sdword numchans[]; extern sdword channelsinuse; void rndDrawOnScreenDebugInfo(void) { static sdword counter = 0; static color c; sdword y = 0; #if MR_TEST_HPB if (mrTestHPBMode) { if (selSelected.numShips == 1) { sdword width = fontWidth("H: -9999.00"); fontPrintf(MAIN_WindowWidth - width, 0, colWhite, "H: %.2f", mrHeading * 180.0f / PI); fontPrintf(MAIN_WindowWidth - width, fontHeight(" ") + 1, colWhite, "P: %.2f", mrPitch * 180.0f / PI); fontPrintf(MAIN_WindowWidth - width, (fontHeight(" ") + 1) * 2, colWhite, "B: %.2f", mrBank * 180.0f / PI); fontPrintf(MAIN_WindowWidth - width, (fontHeight(" ") + 1) * 3, colWhite, "I: %d", madTestHPBIndex); if (keyIsHit(ALTKEY)) { mrHeading = mrPitch = mrBank = 0.0f; } } mouseCursorHide(); } mrTestHPBMode = FALSE; #endif #if PIE_VISUALIZE_VERTICAL if (pieOnScreen) { static bool pieFlashFlag = FALSE; pieFlashFlag ^= TRUE; if (pieFlashFlag) { primLine2((sdword)pieScreen0.x, 479 - (sdword)pieScreen0.y, (sdword)pieScreen1.x, 479 - (sdword)pieScreen1.y, colWhite); } } #endif //PIE_VISUALIZE_VERTICAL y = 0; #if VISIBLE_POLYS if (selSelected.numShips == 1) { extern sdword visiblePoly, visibleSegment, visibleDirection, visibleWhich; extern real32 visibleUV[3][2]; if (counter > 0) { counter--; } else { counter = 4; if (keyIsHit(NINEKEY)) { //dec if (visiblePoly >= 0) { if (keyIsHit(SHIFTKEY)) { visiblePoly -= 10; if (visiblePoly < -1) { visiblePoly = -1; } } else { visiblePoly--; } } } else if (keyIsHit(ZEROKEY)) { //inc if (keyIsHit(SHIFTKEY)) { visiblePoly += 10; } else { visiblePoly++; } } else if (keyIsHit(ONEKEY)) { visibleDirection = -1; visibleWhich = 0; } else if (keyIsHit(TWOKEY)) { visibleDirection = +1; visibleWhich = 0; } else if (keyIsHit(THREEKEY)) { visibleDirection = -1; visibleWhich = 1; } else if (keyIsHit(FOURKEY)) { visibleDirection = +1; visibleWhich = 1; } else if (keyIsHit(FIVEKEY)) { visibleSegment++; if (visibleSegment > 2) { visibleSegment = 0; } } else if (keyIsHit(SEVENKEY)) { extern bool g_Output; g_Output = TRUE; } } fontPrintf(0, 0, colWhite, "%d poly %d", visibleSegment, visiblePoly); c = (visibleSegment == 0) ? colRGB(255,0,0) : colWhite; fontPrintf(0, 16, c, "%0.4f %0.4f", visibleUV[0][0], visibleUV[0][1]); c = (visibleSegment == 1) ? colRGB(0,255,0) : colWhite; fontPrintf(0, 32, c, "%0.4f %0.4f", visibleUV[1][0], visibleUV[1][1]); c = (visibleSegment == 2) ? colRGB(0,0,255) : colWhite; fontPrintf(0, 48, c, "%0.4f %0.4f", visibleUV[2][0], visibleUV[2][1]); } #endif #if RND_FRAME_RATE if (rndDisplayFrameRate) { //display frame rate cmDeterministicBuildDisplay(); if (rndFrameCountMin == SDWORD_Max) { fontPrintf(0, 0, colReddish, "%.2f <= %.2f <= %.2f", (real32)(rndFrameRate) / RND_FrameRateSeconds, (real32)(rndFrameRate) / RND_FrameRateSeconds, (real32)(rndFrameRate) / RND_FrameRateSeconds); } else { fontPrintf(0, 0, colWhite, "%.2f <= %.2f <= %.2f", (real32)(rndFrameCountMin) / RND_FrameRateSeconds, (real32)(rndFrameRate) / RND_FrameRateSeconds, (real32)(rndFrameCountMax) / RND_FrameRateSeconds); } y += 10; fontPrintf(0,y += 20,colRGB(255,255,0),"Channels in use:%d Guns:%d Ships:%d SFX:%d UI:%d",channelsinuse,numchans[0],numchans[1],numchans[2],numchans[3]); } #endif//RND_FRAME_RATE #ifdef COLLISION_CHECK_STATS if (rndDisplayCollStats) { fontPrintf(0,y += 20,colWhite, "Ship:%d %d Resources:%d %d Derelicts:%d %d Total:%d %d",shipshipwalks,shipshipchecks, shipresourcewalks,shipresourcechecks,shipderelictwalks,shipderelictchecks, shipshipwalks+shipresourcewalks+shipderelictwalks,shipshipchecks+shipresourcechecks+shipderelictchecks); fontPrintf(0,y += 20,colWhite, "Bullet:%d %d Missile:%d %d Mine:%d %d Total:%d %d", bulletwalks,bulletchecks,missilewalks,missilechecks,minewalks,minechecks, bulletwalks+missilewalks+minewalks,bulletchecks+missilechecks+minechecks); fontPrintf(0,y += 20,colWhite, "Ships Avoiding Stuff:%d Avoided Walks:%d Avoided Checks:%d", shipsavoidingstuff,shipsavoidedwalks,shipsavoidedchecks); #ifdef BOB_STATS if (bobStats.statsValid) { sdword timeDuration = bobStats.timeDuration / (1000L); fontPrintf(0,y += 20,colWhite, "Blobtype %d Time: %d numPasses:%d numWalks: %d", bobStats.subBlobs, timeDuration, bobStats.numPasses, bobStats.numWalks); fontPrintf(0,y += 20,colWhite,"numChecks:%d trivialRejects:%d InitialBlobs: %d FinalBlobs: %d", bobStats.numChecks, bobStats.trivialRejects, bobStats.initialBlobs, bobStats.finalBlobs); } #endif #ifdef AISHIP_STATS aishipStatsPrint(&y); #endif } #endif #if RND_POLY_STATS if (rndDisplayPolyStats) { //display frame rate fontPrint(0, MAIN_WindowHeight - fontHeight(rndPolyStatsString), rndPolyStatsColor, rndPolyStatsString); rndPolyStatFrameCounter++; #if MESH_MATERIAL_STATS fontPrint(MAIN_WindowWidth - fontWidth(meshMaterialStatsString), MAIN_WindowHeight - fontHeight(rndPolyStatsString), rndPolyStatsColor, meshMaterialStatsString); #endif //MESH_MATERIAL_STATS } #endif//RND_POLY_STATS #if RND_XYZ if (rndXYZPrint && selSelected.numShips == 1) { static char string[256]; if (selSelecting.numTargets == 1) { sprintf(string, "deltaXYZ = (%.2f, %.2f, %.2f)", selSelecting.TargetPtr[0]->posinfo.position.x - selSelected.ShipPtr[0]->posinfo.position.x, selSelecting.TargetPtr[0]->posinfo.position.y - selSelected.ShipPtr[0]->posinfo.position.y, selSelecting.TargetPtr[0]->posinfo.position.z - selSelected.ShipPtr[0]->posinfo.position.z); } else { sprintf(string, "XYZ = (%.2f, %.2f, %.2f)", selSelected.ShipPtr[0]->posinfo.position.x, selSelected.ShipPtr[0]->posinfo.position.y, selSelected.ShipPtr[0]->posinfo.position.z); } fontPrint(MAIN_WindowWidth - fontWidth(string), 0, colWhite, string); } #endif #if NIS_PRINT_INFO if (nisPrintInfo) { fontPrintf(MAIN_WindowWidth - fontWidth(nisInfoString) - 10, MAIN_WindowHeight - fontHeight(nisInfoString) - 1, colWhite, nisInfoString); } #endif #if 0 if (selSelected.numShips == 1) { real32 overlap = rndComputeOverlap(selSelected.ShipPtr[0], 2.0f); fontPrintf(0, 0, colWhite, "overlap %0.3f ", overlap); } #endif #if RND_SCALECAP_TWEAK if (RND_CAPSCALECAP_STATS) { if (selSelected.numShips == 1) { static Ship* ship; static shiptrail* trail; ship = selSelected.ShipPtr[0]; trail = ship->trail[0]; if (trail != NULL) { if (keyIsHit(RND_CapScaleCapKeyDec)) { //decrement if (trail->scalecap == -1.0f) { trail->scalecap = 0.0f; } trail->scalecap -= rndCapScaleCapSlopeDelta; if (trail->scalecap < 0.0f) { trail->scalecap = 0.0f; } } if (keyIsHit(RND_CapScaleCapKeyInc)) { //increment if (trail->scalecap == -1.0f) { trail->scalecap = 0.0f; } trail->scalecap += rndCapScaleCapSlopeDelta; } } fontPrint(0, MAIN_WindowHeight - fontHeight(rndCapScaleCapStatsString), colWhite, rndCapScaleCapStatsString); } } #endif } #endif /*----------------------------------------------------------------------------- Name : rndDrawScissorBars Description : Draw the scissor window bars top and bottom for when scissoring is only partial or not supported. Inputs : scissorEnabled - does the renderer support scissor windows? Outputs : Return : ----------------------------------------------------------------------------*/ void rndDrawScissorBars(bool scissorEnabled) { sdword y; real32 GLy, oneGLy; if (nisScissorFade == 0.0f && !scissorEnabled) { y = NIS_LetterHeight; GLy = primScreenToGLY(y); oneGLy = primScreenToGLY(MAIN_WindowHeight - y); glColor3f(0.0f, 0.0f, 0.0f); } else { y = (sdword)((real32)NIS_LetterHeight * nisScissorFade); GLy = primScreenToGLY(y); oneGLy = primScreenToGLY(MAIN_WindowHeight - y); dbgAssert(!nisFullyScissored || !scissorEnabled); dbgAssert((nisScissorFade > 0.0f && nisScissorFade <= 1.0f) || !scissorEnabled); glColor3f(MR_LetterboxGrey); glBegin(GL_QUADS); //top grey part glVertex2f(primScreenToGLX(MAIN_WindowWidth), primScreenToGLY(0)); glVertex2f(primScreenToGLX(-1), primScreenToGLY(0)); glVertex2f(primScreenToGLX(-1), GLy); glVertex2f(primScreenToGLX(MAIN_WindowWidth), GLy); //bottom scissor part glVertex2f(primScreenToGLX(-1), oneGLy); glVertex2f(primScreenToGLX(-1), primScreenToGLY(MAIN_WindowHeight)); glVertex2f(primScreenToGLX(MAIN_WindowWidth), primScreenToGLY(MAIN_WindowHeight)); glVertex2f(primScreenToGLX(MAIN_WindowWidth), oneGLy); glEnd(); glEnable(GL_BLEND); glColor4f(0.0f, 0.0f, 0.0f, nisScissorFade); } glBegin(GL_QUADS); //top scissor part glVertex2f(primScreenToGLX(-1), primScreenToGLY(0)); glVertex2f(primScreenToGLX(-1), GLy); glVertex2f(primScreenToGLX(MAIN_WindowWidth), GLy); glVertex2f(primScreenToGLX(MAIN_WindowWidth), primScreenToGLY(0)); //bottom scissor part glVertex2f(primScreenToGLX(-1), oneGLy); glVertex2f(primScreenToGLX(-1), primScreenToGLY(MAIN_WindowHeight)); glVertex2f(primScreenToGLX(MAIN_WindowWidth), primScreenToGLY(MAIN_WindowHeight)); glVertex2f(primScreenToGLX(MAIN_WindowWidth), oneGLy); glEnd(); glDisable(GL_BLEND); } /*----------------------------------------------------------------------------- Name : rndRenderTask Description : Main render task Inputs : void Outputs : Clears frame buffer, renders regions, draws mouse, swaps buffers. Return : void ----------------------------------------------------------------------------*/ #pragma optimize("gy", off) //turn on stack frame (we need ebp for this function) void rndRenderTask(void) { static bool shouldSwap; static sdword index; #ifdef PROFILE_TIMERS static sdword y; #endif taskYield(0); while (1) { taskStackSaveCond(0); primErrorMessagePrint(); speechEventUpdate(); subTitlesUpdate(); #if RND_GL_STATE_DEBUG if (keyIsHit(GKEY) && keyIsStuck(LKEY)) { //if starting a new round of state saving static FILE *f; keyClearSticky(LKEY); rndGLStateSaving = TRUE; if (rndGLStateLogIndex < 10) { sprintf(rndGLStateLogFileName, "gl0%d.state", rndGLStateLogIndex); } else { sprintf(rndGLStateLogFileName, "gl%d.state", rndGLStateLogIndex); } rndGLStateLogIndex++; f = fopen(rndGLStateLogFileName, "wt"); //open the file to clean it out if (f != NULL) { fclose(f); //close the file } } #endif glColor3ub(colRed(RND_StarColor), colGreen(RND_StarColor), colBlue(RND_StarColor)); shouldSwap = feShouldSaveMouseCursor(); if (shouldSwap) { if (RGL) { rglFeature(RGL_SAVEBUFFER_ON); if (mainRasterSkip) { rglFeature(RGL_NOSKIP_RASTER); } } } else { if (RGL) { rglFeature(RGL_SAVEBUFFER_OFF); } if (binkDonePlaying) { if (RGL) { if (mainRasterSkip && gameIsRunning) { rglFeature(RGL_SKIP_RASTER); } else { rglFeature(RGL_NOSKIP_RASTER); } } if (lmActive) { rndClearToBlack(); glClear(GL_DEPTH_BUFFER_BIT); } else { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear the buffers } } } rndScissorEnabled = glIsEnabled(GL_SCISSOR_TEST); //can we do scissoring? primErrorMessagePrint(); //default rendering scheme is primitives on. any //functions which want it off should set it back on when done regFunctionsDraw(); //render all regions primErrorMessagePrint(); rndFrameCount++; //update frame count //draw partial scissor window, if applicable if (nisScissorFade != 0.0f || (nisFullyScissored && !rndScissorEnabled)) { rndDrawScissorBars(rndScissorEnabled); } glDisable(GL_SCISSOR_TEST); //in case the scene was rendered with scissoring, turn it off properlu. /* need to update audio event layer */ soundEventUpdate(); //draw the subtitle text. It's done here because it has to draw over everything for (index = 0; index < SUB_NumberRegions; index++) { if ((!tutPointersDrawnThisFrame) && index == STR_LetterboxBar) { //if this is the subtitle region tutDrawTextPointers(&subRegion[STR_LetterboxBar].rect);//draw any active pointers there may be } if (subRegion[index].bEnabled && subRegion[index].cardIndex > 0) { if (index == STR_NIS) { subTimeElapsed = &thisNisPlaying->timeElapsed; } else { subTimeElapsed = &universe.totaltimeelapsed; } subTitlesDraw(&subRegion[index]); } } rndDrawOnScreenSyncStatus(); // always print sync status, even in HW_Release mode #if RND_VERBOSE_LEVEL >= 1 rndDrawOnScreenDebugInfo(); //draw a bunch of debugging crapola #endif #ifdef PROFILE_TIMERS y = 300; if (rndDisplayCollStats) profTimerStatsPrint(&y); // compiled out automatically if PROFILE_TIMERS not defined #endif // set the cursor type, reset the variables then draw the mouse cursor mouseSelectCursorSetting(); mouseSetCursorSetting(); shouldSwap = feShouldSaveMouseCursor(); if (shouldSwap) { mouseStoreCursorUnder(); } if (!glcActive()) { mouseDraw(); //draw mouse atop everything } if (universePause) { #if 0 if (singlePlayerGame) { static sdword intelliCount; static Node* intelliNode; //count the number of popped up popups intelliCount = 0; intelliNode = poFleetIntelligence.head; while (intelliNode != NULL) { FleetIntelligence* inode = (FleetIntelligence*)listGetStructOfNode(intelliNode); if (inode->showOnce) { intelliCount++; } intelliNode = intelliNode->next; } //don't display plug if a popup has paused the game if (intelliCount == 0) { rndShamelessPlug(); } } else #endif { rndShamelessPlug(); } } //take a screenshot or sequence thereof #if SS_SCREENSHOTS if (keyIsStuck(SCROLLKEY)) { keyClearSticky(SCROLLKEY); #if MAIN_Password if (mainScreenShotsEnabled) #endif //MAIN_Password { rndTakeScreenshot = TRUE; } } else if (keyIsStuck(PAUSEKEY)) { keyClearSticky(PAUSEKEY); if (RGL) { #if SS_VERBOSE_LEVEL >= 1 dbgMessagef("\nMulti-shot end."); #endif rglFeature(RGL_MULTISHOT_END); } } if (rndTakeScreenshot) { static ubyte* buf; rndTakeScreenshot = FALSE; //buf = (ubyte*)malloc(3*MAIN_WindowWidth*MAIN_WindowHeight); buf = (void *)VirtualAlloc(NULL, 3*MAIN_WindowWidth*MAIN_WindowHeight, MEM_COMMIT, PAGE_READWRITE); if (buf != NULL) { BOOL result; glReadPixels(0, 0, MAIN_WindowWidth, MAIN_WindowHeight, GL_RGB, GL_UNSIGNED_BYTE, buf); ssSaveScreenshot(buf); //free(buf); result = VirtualFree(buf, 0, MEM_RELEASE); dbgAssert(result); } } #endif //SS_SCREENSHOTS if (rndFillCounter) { static rectangle r; r.x0 = 0; r.y0 = 0; r.x1 = MAIN_WindowWidth - 1; r.y1 = MAIN_WindowHeight - 1; primRectSolid2(&r, rndFillColour); rndFillCounter--; } #if 0 #if defined (Downloadable) || defined(DLPublicBeta) ; #else { extern bool mainPlayAVIs; if (mainPlayAVIs) { mainPlayAVIs = FALSE; animSmackPlay(-1, (sdword)"Movies\\sierra.smk"); animSmackPlay(-1, (sdword)"Movies\\relicintro.smk"); } } #endif #endif //demo-specific update code if (demDemoPlaying && gameIsRunning) { //if a demo is playing and we're in the actual game if (rndMainViewRender != rndMainViewRenderFunction) { //this was a fake render; don't do a swap or flush goto afterTheSwap; } } if (mainReinitRenderer > 0) { mainReinitRenderer--; if (mainReinitRenderer == 0) { mainReinitRGL(); } } else { if (animaticJustPlayed > 0) { animaticJustPlayed--; } else if (!feDontFlush) { if (glCapFeatureExists(GL_SWAPFRIENDLY)) { if (glcActive()) { if (RGLtype == SWtype) { (void)glcActivate(FALSE); } else { glcPageFlip(FALSE); } } } rndFlush(); } feDontFlush = FALSE; } primErrorMessagePrint(); if (shouldSwap) { mouseRestoreCursorUnder(); } primErrorMessagePrint(); afterTheSwap: #if RND_GL_STATE_DEBUG rndGLStateSaving = FALSE; //done saving the file for now #endif //RND_GL_STATE_DEBUG tutPointersDrawnThisFrame = FALSE; taskStackRestoreCond(); taskYield(0); //hold off to next frame } taskExit(); } #pragma optimize("", on) /*----------------------------------------------------------------------------- Name : rndLightingEnable Description : Enable or disable back-face culling Inputs : bEnable - TRUE enables, FALSE disables Outputs : just calls glEnable/glDisable Return : void ----------------------------------------------------------------------------*/ sdword rndLightingEnable(sdword bEnable) { sdword oldStatus = rndLightingEnabled; if (bEnable) { if (!rndLightingEnabled) { glEnable(GL_LIGHTING); rndLightingEnabled = TRUE; } } else { if (rndLightingEnabled) { glDisable(GL_LIGHTING); rndLightingEnabled = FALSE; } } return oldStatus; } /*----------------------------------------------------------------------------- Name : rndBackFaceCullEnable Description : Enable or disable back-face culling Inputs : bEnable - TRUE enables, FALSE disables Outputs : just calls glEnable/glDisable Return : void ----------------------------------------------------------------------------*/ void rndBackFaceCullEnable(sdword bEnable) { if (bEnable) { glEnable(GL_CULL_FACE); } else { glDisable(GL_CULL_FACE); } } /*----------------------------------------------------------------------------- Name : rndTextureEnable Description : Enable or disable texturing Inputs : bEnable - TRUE enables, FALSE disables Outputs : toggles rndTextureEnabled Return : previous enabled status ----------------------------------------------------------------------------*/ sdword rndTextureEnable(sdword bEnable) { sdword enabled = rndTextureEnabled; if (bEnable) { if (!rndTextureEnabled) { glEnable(GL_TEXTURE_2D); rndTextureEnabled = TRUE; } } else { if (rndTextureEnabled) { glDisable(GL_TEXTURE_2D); rndTextureEnabled = FALSE; } } return(enabled); } /*----------------------------------------------------------------------------- Name : rndTextureEnvironment Description : changes the current texture environment Inputs : mode - one of RTE_Modulate, RTE_Replace, RTE_Decal, RTE_Blend Outputs : changes the current texture environment mode Return : old mode ----------------------------------------------------------------------------*/ sdword rndTextureModeTable[] = { GL_MODULATE, GL_REPLACE, GL_DECAL, GL_BLEND }; sdword rndTextureEnvironment(sdword mode) { sdword oldMode = rndTextureEnviron; if (rndTextureEnviron != mode) { glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, rndTextureModeTable[mode]); rndTextureEnviron = mode; } return(oldMode); } /*----------------------------------------------------------------------------- Name : rndPerspectiveCorrection Description : provides a hint to the GL as to whether to perspectively correct textures Inputs : bEnable - TRUE enables (NICEST), FALSE disables (FASTEST) Outputs : may toggle rndPerspectiveCorrect Return : previous status ----------------------------------------------------------------------------*/ sdword rndPerspectiveCorrection(sdword bEnable) { sdword oldStatus = rndPerspectiveCorrect; #if USE_RND_HINT if (rndHint > 0) { if (rndHint == 1) { glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); rndPerspectiveCorrect = TRUE; } else { glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); rndPerspectiveCorrect = FALSE; } return(oldStatus); } #endif if (mainNoPerspective) { if (RGL) glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); return(oldStatus); } if (bEnable) { if (!rndPerspectiveCorrect) { glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); rndPerspectiveCorrect = TRUE; } } else { if (rndPerspectiveCorrect) { glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); rndPerspectiveCorrect = FALSE; } } return(oldStatus); } /*----------------------------------------------------------------------------- Name : rndNormalizeEnable Description : enables or disables normalization Inputs : bEnable - TRUE enables, FALSE disables Outputs : toggles rndNormalization Return : previous status ----------------------------------------------------------------------------*/ sdword rndNormalizeEnable(sdword bEnable) { sdword oldStatus = rndNormalization; if (bEnable) { glEnable(GL_NORMALIZE); rndNormalization = TRUE; } else { glDisable(GL_NORMALIZE); rndNormalization = FALSE; } return(oldStatus); } /*----------------------------------------------------------------------------- Name : rndHintInc Description : changes the glHint mode of operation Inputs : Outputs : alters rndHint Return : ----------------------------------------------------------------------------*/ void rndHintInc() { #if USE_RND_HINT rndHint++; if (rndHint >= 3) rndHint = 0; #endif } /*----------------------------------------------------------------------------- Name : rndAdditiveBlends Description : enables or disables additive blending mode Inputs : bAdditive - TRUE or FALSE Outputs : Return : previous status ----------------------------------------------------------------------------*/ sdword rndAdditiveBlends(sdword bAdditive) { sdword oldStatus; extern udword gDevcaps2; oldStatus = rndAdditiveBlending; if (bAdditive != rndAdditiveBlending) { rndAdditiveBlending = bAdditive; if (bAdditive) { if (bitTest(gDevcaps2, DEVSTAT2_NO_ADDITIVE)) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glBlendFunc(GL_SRC_ALPHA, GL_ONE); } } else { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } return(oldStatus); } /*----------------------------------------------------------------------------- Name : rndMaterialfv Description : set GL material params Inputs : face - GL_FRONT or GL_FRONT_AND_BACK pname - GL_AMBIENT or GL_DIFFUSE params - RGBA [0..1] Outputs : Return : TRUE if material was updated, FALSE if same as last ----------------------------------------------------------------------------*/ #define V4_EQUAL(A, B) (A[0] != B[0] || A[1] != B[1] || A[2] != B[2] || A[3] != B[3]) ? FALSE : TRUE #define V4_SET(D, S) \ { \ D[0] = S[0]; \ D[1] = S[1]; \ D[2] = S[2]; \ D[3] = S[3]; \ } sdword rndMaterialfv(sdword face, sdword pname, real32* params) { static real32 ambient[2][4] = {{-1.0f, -1.0f, -1.0f, -1.0f},{-1.0f, -1.0f, -1.0f, -1.0f}}; static real32 diffuse[2][4] = {{-1.0f, -1.0f, -1.0f, -1.0f},{-1.0f, -1.0f, -1.0f, -1.0f}}; if (face == -1) { real32 reset[4] = {-1.0f, -1.0f, -1.0f, -1.0f}; V4_SET(ambient[0], reset); V4_SET(ambient[1], reset); V4_SET(diffuse[0], reset); V4_SET(diffuse[1], reset); return TRUE; } if (face == GL_FRONT) { if (pname == GL_AMBIENT) { if (!V4_EQUAL(ambient[0], params)) { V4_SET(ambient[0], params); glMaterialfv(face, pname, params); return TRUE; } } else { if (!V4_EQUAL(diffuse[0], params)) { V4_SET(diffuse[0], params); glMaterialfv(face, pname, params); return TRUE; } } } else { if (pname == GL_AMBIENT) { if (!V4_EQUAL(ambient[0], params) || !V4_EQUAL(ambient[1], params)) { V4_SET(ambient[0], params); V4_SET(ambient[1], params); glMaterialfv(face, pname, params); return TRUE; } } else { if (!V4_EQUAL(diffuse[0], params) || !V4_EQUAL(ambient[1], params)) { V4_SET(diffuse[0], params); V4_SET(diffuse[1], params); glMaterialfv(face, pname, params); return TRUE; } } } return FALSE; } #define vcSub(a,b,c) \ (a)##->x = (b)##->x - (c)##->x; \ (a)##->y = (b)##->y - (c)##->y; \ (a)##->z = (b)##->z - (c)##->z #define vcCrossProduct(result,a,b) \ (result)##->x = ((a)##->y * (b)##->z) - ((a)##->z * (b)##->y); \ (result)##->y = ((a)##->z * (b)##->x) - ((a)##->x * (b)##->z); \ (result)##->z = ((a)##->x * (b)##->y) - ((a)##->y * (b)##->x) #define vcAddToScalarMultiply(dstvec,vec,k) \ (dstvec)##->x += ((vec)##->x * (k)); \ (dstvec)##->y += ((vec)##->y * (k)); \ (dstvec)##->z += ((vec)##->z * (k)) #define vcAddToScalarMultiply(dstvec,vec,k) \ (dstvec)##->x += ((vec)##->x * (k)); \ (dstvec)##->y += ((vec)##->y * (k)); \ (dstvec)##->z += ((vec)##->z * (k)) #define vcScalarMultiply(dstvec,vec,k) \ (dstvec)##->x = (vec)##->x * (k); \ (dstvec)##->y = (vec)##->y * (k); \ (dstvec)##->z = (vec)##->z * (k) #define vcDotProduct(a,b) \ ( ((a)##->x * (b)##->x) + ((a)##->y * (b)##->y) + ((a)##->z * (b)##->z) ) /*----------------------------------------------------------------------------- Name : rndEnvironmentMap Description : calculates parameters for rendering a reflective poly Inputs : vector camera : location of camera vectors A,B,C : vertices of poly Outputs : vectors U,V,W : corners on texture map (.x and .y only!) Return : ----------------------------------------------------------------------------*/ void rndEnvironmentMap(vector* camera, vector* A, vector* B, vector* C, vector* U, vector* V, vector* W) { vector v1,v2,normal; real32 length; //get normal of triangle vcSub(&v1,B,A); vcSub(&v2,C,A); vcCrossProduct(&normal,&v1,&v2); vecNormalizeToLength(&normal, (real32)1.0f); //vertex A: vcSub(&v1,A,camera); //v1 is incoming ray length = vcDotProduct(&v1,&normal); vcAddToScalarMultiply(&v1,&normal,2*length); //possibly "-2*length" //v1 is now reflected ray U->x = atan(v1.y/v1.x); //azimuth U->y = atan(v1.z/(sqrt(v1.x*v1.x + v1.y*v1.y))); //elevation; //vertex B: vcSub(&v1,B,camera); length = vcDotProduct(&v1,&normal); vcAddToScalarMultiply(&v1,&normal,2*length); //possibly "-2*length" V->x = atan(v1.y/v1.x); //azimuth V->y = atan(v1.z/(sqrt(v1.x*v1.x + v1.y*v1.y))); //elevation; //vertex C: vcSub(&v1,C,camera); length = vcDotProduct(&v1,&normal); vcAddToScalarMultiply(&v1,&normal,2*length); //possibly "-2*length" W->x = atan(v1.y/v1.x); //azimuth W->y = atan(v1.z/(sqrt(v1.x*v1.x + v1.y*v1.y))); //elevation; } /*----------------------------------------------------------------------------- Name : rndEnvironmentMapConvex Description : calculates parameters for rendering a reflective poly with a convex or concave surface Inputs : vector camera : location of camera vectors A,B,C : vertices of poly convex: curvature of surface convex < 1 : concave convex = 1 : flat convex > 1 : convex Outputs : vectors U,V,W : corners on texture map (.x and .y only!) Return : ----------------------------------------------------------------------------*/ void rndEnvironmentMapConvex(vector* camera, vector* A, vector* B, vector* C, real32 convex, vector* U, vector* V, vector* W) { vector v1,v2,normal; real32 length; //get normal of triangle vcSub(&v1,B,A); vcSub(&v2,C,A); vcCrossProduct(&normal,&v1,&v2); vecNormalizeToLength(&normal, (real32)1.0f); //vertex A: vcSub(&v1,A,camera); //v1 is incoming ray length = vcDotProduct(&v1,&normal); vcScalarMultiply(&v2,&normal,length); vcAddToScalarMultiply(&v1,&v2,1+convex); //v1 is now reflected ray U->x = atan(v1.y/v1.x); //azimuth U->y = atan(v1.z/(sqrt(v1.x*v1.x + v1.y*v1.y))); //elevation; //vertex B: vcSub(&v1,B,camera); length = vcDotProduct(&v1,&normal); vcScalarMultiply(&v2,&normal,2*length); vcAddToScalarMultiply(&v1,&v2,1+convex); V->x = atan(v1.y/v1.x); //azimuth V->y = atan(v1.z/(sqrt(v1.x*v1.x + v1.y*v1.y))); //elevation; //vertex C: vcSub(&v1,C,camera); length = vcDotProduct(&v1,&normal); vcScalarMultiply(&v2,&normal,2*length); vcAddToScalarMultiply(&v1,&v2,1+convex); W->x = atan(v1.y/v1.x); //azimuth W->y = atan(v1.z/(sqrt(v1.x*v1.x + v1.y*v1.y))); //elevation; } /*----------------------------------------------------------------------------- Name : rndSetScreenFill Description : set a flag to fill the screen entirely with a rectangle of specified colour immediately before the next Flush Inputs : count - number of frames for fill to occur c - color to fill screen with Outputs : globals are set Return : -----------------------------------------------------------------------------*/ void rndSetScreenFill(sdword count, color c) { rndFillCounter = count; rndFillColour = c; } /*----------------------------------------------------------------------------- Name : rndSetClearColor Description : set the background clear color, if such a thing is supported by underlying GL Inputs : c - the color to set Outputs : GL_COLOR_CLEAR_VALUE is perhaps modified Return : ----------------------------------------------------------------------------*/ void rndSetClearColor(color c) { if (glCapFeatureExists(GL_COLOR_CLEAR_VALUE)) { glClearColor(colReal32(colRed(c)), colReal32(colGreen(c)), colReal32(colBlue(c)), 1.0f); } else { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); } } /*----------------------------------------------------------------------------- Name : rndResetGLState Description : reset the GL's state after switching renderers Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void rndResetGLState(void) { trClearCurrent(); rndMaterialfv(-1, 0, NULL); if (rndAdditiveBlending) { glBlendFunc(GL_SRC_ALPHA, GL_ONE); } else { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } rndAdditiveBlends(rndAdditiveBlending); if (rndNormalization) { glEnable(GL_NORMALIZE); } else { glDisable(GL_NORMALIZE); } rndNormalizeEnable(rndNormalization); if (rndPerspectiveCorrect) { glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); } else { glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); } rndPerspectiveCorrection(rndPerspectiveCorrect); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, RTE_Modulate); rndTextureEnviron = RTE_Modulate; if (rndTextureEnabled) { glEnable(GL_TEXTURE_2D); } else { glDisable(GL_TEXTURE_2D); } rndTextureEnable(rndTextureEnabled); rndBackFaceCullEnable(TRUE); if (rndLightingEnabled) { glEnable(GL_LIGHTING); } else { glDisable(GL_LIGHTING); } rndLightingEnable(rndLightingEnabled); rndSetClearColor(colBlack); } /*----------------------------------------------------------------------------- Name : rndComputeOverlap Description : compute amount of overlap of collision spheres wrt ships in renderlist Inputs : ship - ship to test scalar - multiplier for strike craft collision radii Outputs : Return : [0..1] amount of obscuring that's going on ----------------------------------------------------------------------------*/ real32 rndComputeOverlap(Ship* ship, real32 scalar) { Node* objnode; SpaceObj* spaceobj; ShipStaticInfo* shipStaticInfo; Ship* testship; vector vobj; real32 overlap; CollInfo* acoll; CollInfo* bcoll; rectangle arect, brect, crect; real32 aarea, carea; real32 ratio; real32 radius; //looking for our ship objnode = universe.RenderList.head; while (objnode != NULL) { testship = (Ship*)listGetStructOfNode(objnode); if (testship == ship) { break; } objnode = objnode->next; } if (objnode == NULL) { //couldn't find it return 0.0f; } //move backwards computing overlap // objnode = objnode->prev; objnode = objnode->next; overlap = 0.0f; //make screenspace rectangle acoll = &ship->collInfo; radius = scalar * acoll->selCircleRadius; arect.x0 = primGLToScreenX(acoll->selCircleX - radius); arect.x1 = primGLToScreenX(acoll->selCircleX + radius); arect.y0 = primGLToScreenY(acoll->selCircleY + radius); arect.y1 = primGLToScreenY(acoll->selCircleY - radius); aarea = (real32)(2 * (arect.x1 - arect.x0) + 2 * (arect.y1 - arect.y0)); while (objnode != NULL) { spaceobj = (SpaceObj*)listGetStructOfNode(objnode); if (spaceobj->objtype == OBJ_ShipType) { //useful stuff testship = (Ship*)spaceobj; shipStaticInfo = (ShipStaticInfo*)spaceobj->staticinfo; if (ship->staticinfo->shipclass <= CLASS_Frigate) { if (testship->staticinfo->shipclass > CLASS_Frigate) { // don't calculate strikecraft overlapping capital ships goto nextobj; } } #if 0 //not sure what to do about this yet if (ship->collInfo.precise != NULL) { __asm int 3 ; } #endif //position in cameraspace vecSub(vobj, mrCamera->eyeposition, spaceobj->posinfo.position); //make screenspace rectangle bcoll = &testship->collInfo; radius = scalar * bcoll->selCircleRadius; brect.x0 = primGLToScreenX(bcoll->selCircleX - radius); brect.x1 = primGLToScreenX(bcoll->selCircleX + radius); brect.y0 = primGLToScreenY(bcoll->selCircleY + radius); brect.y1 = primGLToScreenY(bcoll->selCircleY - radius); //find union of ab primRectUnion2(&crect, &arect, &brect); if (crect.x0 == crect.x1 || crect.y0 == crect.y1) { carea = 0.0f; } else { carea = (real32)(2 * (crect.x1 - crect.x0) + 2 * (crect.y1 - crect.y0)); } //compute overlap ratio = carea / aarea; overlap += ratio; if (overlap >= 1.0f) { overlap = 1.0f; break; } } // objnode = objnode->prev; nextobj: objnode = objnode->next; } return overlap; } /*----------------------------------------------------------------------------- Name : rndClearToBlack Description : clear the screen to black, guaranteed Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void rndClearToBlack(void) { real32 bgcolor[4]; if (glCapFeatureExists(GL_COLOR_CLEAR_VALUE)) { //get current clearcolor glGetFloatv(GL_COLOR_CLEAR_VALUE, (GLfloat*)bgcolor); //set clearcolor to black glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //clear the screen glClear(GL_COLOR_BUFFER_BIT); //reset clearcolor glClearColor(bgcolor[0], bgcolor[1], bgcolor[2], bgcolor[3]); } else { //clear the screen glClear(GL_COLOR_BUFFER_BIT); } } /*----------------------------------------------------------------------------- Name : rndAllClearToBlack Description : clear all buffers to black Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void rndAllClearToBlack(void) { real32 bgcolor[4]; if (glCapFeatureExists(GL_COLOR_CLEAR_VALUE)) { //get current clearcolor glGetFloatv(GL_COLOR_CLEAR_VALUE, (GLfloat*)bgcolor); //set clearcolor to black glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //clear the screen rndClear(); //reset clearcolor glClearColor(bgcolor[0], bgcolor[1], bgcolor[2], bgcolor[3]); } else { //clear the screen rndClear(); } } /*----------------------------------------------------------------------------- Name : rndClear Description : clear front and back buffers Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void rndClear(void) { sdword i; for (i = 0; i < 3; i++) { glClear(GL_COLOR_BUFFER_BIT); rndFlush(); } if (RGLtype == SWtype) { rglFeature(RGL_SPEEDY); } } /*----------------------------------------------------------------------------- Name : rndFlush Description : flush the GL and swap buffers if necessary Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void rndFlush(void) { glFlush(); primErrorMessagePrint(); if (!RGL) { if (rwglSwapBuffers == NULL || glNT) { SwapBuffers(hGLDeviceContext); } else { rwglSwapBuffers(hGLDeviceContext); } } Sleep(1); }