void R_DrawStretchImage (float x, float y, int w, int h, const image_t *image) { if (!image) return; R_DrawTexture(image->texnum, x * viddef.rx, y * viddef.ry, w * viddef.rx, h * viddef.ry); }
/** * @brief Draws an image or parts of it * @param x X position to draw the image to * @param y Y position to draw the image to * @param[in] image Pointer to the imlage to display */ void R_DrawImage (float x, float y, const image_t *image) { if (!image) return; R_DrawTexture(image->texnum, x * viddef.rx, y * viddef.ry, image->width * viddef.rx, image->height * viddef.ry); }
/** * @brief responsible for drawing the 3d globe on geoscape * param[in] rotate the rotate angle of the globe * param[in] zoom the current globe zoon * param[in] map the prefix of the map to use (image must be at base/pics/menu/\<map\>_[day|night]) * @sa R_DrawFlatGeoscape * @sa R_SphereGenerate */ void R_Draw3DGlobe (const vec2_t pos, const vec2_t size, int day, int second, const vec3_t rotate, float zoom, const char *map, bool disableSolarRender, float ambient, bool overlayNation, bool overlayXVI, bool overlayRadar, image_t *r_xviTexture, image_t *r_radarTexture, bool renderNationGlow) { /* globe scaling */ const float fullscale = zoom / STANDARD_3D_ZOOM; /* lighting colors */ static const vec4_t diffuseLightColor = { 1.75f, 1.75f, 1.75f, 1.0f }; static const vec4_t specularLightColor = { 2.0f, 1.9f, 1.7f, 1.0f }; static const vec4_t darknessLightColor = { 0.0f, 0.0f, 0.0f, 1.0f }; static const vec4_t brightDiffuseLightColor = { 5.0f, 5.0f, 5.0f, 1.0f }; const vec4_t ambientLightColor = { ambient + 0.2f, ambient + 0.2f, ambient + 0.2f, ambient + 0.2f }; /* billboard textures */ image_t *starfield; image_t *halo; image_t *sun; image_t *sunOverlay; /* set distance of the sun and moon to make them static on starfield when * time is stoped. this distance should be used for any celestial body * considered at infinite location (sun, moon) */ static const float celestialDist = 1.37f * SKYBOX_HALFSIZE; static const float moonSize = 0.025f; vec4_t sunPos; vec4_t antiSunPos; vec4_t moonLoc; vec4_t sunLoc; /* normalize */ const float nx = pos[0] * viddef.rx; const float ny = pos[1] * viddef.ry; const float nw = size[0] * viddef.rx; const float nh = size[1] * viddef.ry; /* Earth center is in the middle of node. * Due to Orthographic view, this is also camera position */ const vec3_t earthPos = { nx + nw / 2.0f, ny + nh / 2.0f, 0.0f }; /* estimate the progress through the current season so we can do * smooth transitions between textures. Currently there are 12 * "seasons", because we have one image per Earth-month. */ const float season = (float) (day % DAYS_PER_YEAR) / ((float) (DAYS_PER_YEAR) / (float) (SEASONS_PER_YEAR)); const int currSeason = (int) floorf(season) % SEASONS_PER_YEAR; const int nextSeason = (int) ceilf(season) % SEASONS_PER_YEAR; const float seasonProgress = season - (float) currSeason; /* Compute sun position in absolute frame */ const float q = (day % DAYS_PER_YEAR * SECONDS_PER_DAY + second) * (2.0f * M_PI / (SECONDS_PER_DAY * DAYS_PER_YEAR)); /* sun rotation (year) */ const float a = cos(q) * SIN_ALPHA; /* due to earth obliquity */ const float sqrta = sqrt(0.5f * (1 - a * a)); /* earth rotation (day) */ const float p = (second - SECONDS_PER_DAY / 4) * (2.0f * M_PI / SECONDS_PER_DAY); /* lunar orbit */ const float m = p + (((double)((10 * day % 249) / 10.0f) + ((double)second / (double)SECONDS_PER_DAY)) / 24.9f) * (2.0f * M_PI); glPushMatrix(); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glDisable(GL_LIGHTING); /* draw the starfield, rotating with the planet */ starfield = R_FindImage(va("pics/geoscape/%s_stars", map), it_wrappic); if (starfield != r_noTexture) R_DrawStarfield(starfield->texnum, earthPos, rotate, p); glPopMatrix(); /* set up position vectors for celestial bodies */ Vector4Set(sunPos, cos(p) * sqrta, -sin(p) * sqrta, a, 0); Vector4Set(antiSunPos, -cos(p) * sqrta, sin(p) * sqrta, -a, 0); /* Rotate the sun in the relative frame of player view, to get sun location */ R_RotateCelestialBody(sunPos, sunLoc, rotate, earthPos, 1.0f); /* load sun texture image */ sun = R_FindImage(va("pics/geoscape/%s_sun", map), it_wrappic); sunOverlay = R_FindImage(va("pics/geoscape/%s_sun_overlay", map), it_pic); if (sun != r_noTexture && sunOverlay != r_noTexture && sunLoc[2] > 0 && !disableSolarRender) { const int sunx = earthPos[0] + viddef.rx * (-128.0f + celestialDist * (sunLoc[0] - earthPos[0])); const int suny = earthPos[1] + viddef.ry * (-128.0f + celestialDist * (sunLoc[1] - earthPos[1])); R_DrawTexture(sunOverlay->texnum, sunx, suny, 256.0f * viddef.rx, 256.0f * viddef.ry); R_DrawBuffers(2); R_DrawTexture(sun->texnum, sunx, suny, 256.0 * viddef.rx, 256.0 * viddef.ry); R_DrawBuffers(1); } /* calculate position of the moon (it rotates around earth with a period of * about 24.9 h, and we must take day into account to avoid moon to "jump" * every time the day is changing) */ VectorSet(moonLoc, cos(m) * sqrta, -sin(m) * sqrta, a); R_RotateCelestialBody(moonLoc, moonLoc, rotate, earthPos, celestialDist); /* free last month's texture image */ if (r_globeEarth.season != currSeason) { r_globeEarth.season = currSeason; R_FreeImage(r_globeEarth.texture); } /* load diffuse texture map (with embedded night-glow map as alpha channel) */ r_globeEarth.texture = R_FindImage(va("pics/geoscape/%s/%s_season_%02d", r_config.lodDir, map, currSeason), it_wrappic); if (r_globeEarth.texture == r_noTexture) Com_Error(ERR_FATAL, "Could not find pics/geoscape/%s/%s_season_%02d\n", r_config.lodDir, map, currSeason); /* set up for advanced GLSL rendering if we have the capability */ if (r_programs->integer) { r_globeEarth.glslProgram = r_state.geoscape_program; /* load earth image for the next month so we can blend them */ r_globeEarth.blendTexture = R_FindImage(va("pics/geoscape/%s/%s_season_%02d", r_config.lodDir, map, nextSeason), it_wrappic); if (r_globeEarth.blendTexture == r_noTexture) Com_Error(ERR_FATAL, "Could not find pics/geoscape/%s/%s_season_%02d\n", r_config.lodDir, map, nextSeason); /* load normal map (with embedded gloss map as alpha channel) */ r_globeEarth.normalMap = R_FindImage(va("pics/geoscape/%s/%s_bump", r_config.lodDir, map), it_wrappic); if (r_globeEarth.normalMap == r_noTexture) r_globeEarth.normalMap = nullptr; /* weight the blending based on how much of the month has elapsed */ r_globeEarth.blendScale = seasonProgress; /* set up lights for nighttime city glow */ VectorCopy(antiSunPos, r_globeEarth.nightLightPos); glLightfv(GL_LIGHT1, GL_AMBIENT, darknessLightColor); glLightfv(GL_LIGHT1, GL_DIFFUSE, brightDiffuseLightColor); glLightfv(GL_LIGHT1, GL_SPECULAR, darknessLightColor); r_globeEarth.glowScale = 0.7f; } /* load moon texture image */ r_globeMoon.texture = R_FindImage(va("pics/geoscape/%s_moon", map), it_wrappic); /* globe texture scaling */ glMatrixMode(GL_TEXTURE); glLoadIdentity(); glScalef(2.0f, 1.0f, 1.0f); glMatrixMode(GL_MODELVIEW); /* enable the lighting */ glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLightColor); glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLightColor); glLightfv(GL_LIGHT0, GL_SPECULAR, specularLightColor); /* draw the moon */ if (r_globeMoon.texture != r_noTexture && moonLoc[2] > 0 && !disableSolarRender) R_SphereRender(&r_globeMoon, moonLoc, rotate, moonSize, sunPos); /* activate depth to hide 3D models behind earth */ glEnable(GL_DEPTH_TEST); /* draw the earth */ R_DrawBuffers(2); #if 0 /* old rendering code which doesn't render city lights in FFP */ if (r_programs->integer == 0) /* ignore alpha channel, since the city-light map is stored there */ glBlendFunc(GL_ONE, GL_ZERO); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); if (r_programs->integer == 0) /* restore default blend function */ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); #else /* new which does render city lights in FFP */ if (r_programs->integer == 0) { /* set up rendering of city lights map, which is stored in alpha channel; OpenGL 1.3 required */ R_SelectTexture(&texunit_diffuse); /* select texture to edit texture environment for */ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); /* enable color combiner */ /* setup texture combiner to blend between daylight diffuse map stored in the RGB channels of the diffuse texture * and the monochomatic emission map (which simulates city lights) stored in the alpha channel; * incoming color value is the blend factor. */ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); /* set day color as blending target*/ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); /* set night color as blending source */ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PREVIOUS); /* set incoming color as blending factor */ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); /* set blending mode to interpolation from src1 to src0 */ /* copy alpha from incoming color, bypassing the value read from texture, which is not a "real" alpha anyway */ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); } R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); if (r_programs->integer == 0) { /* disable combiner */ R_SelectTexture(&texunit_diffuse); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } #endif r_globeEarthAtmosphere.texture = R_FindImage(va("pics/geoscape/%s_atmosphere", map), it_wrappic); /* Draw earth atmosphere */ /** @todo render atmosphere glow even when postprocessing is disabled */ if (r_programs->integer && r_postprocess->integer) { r_globeEarthAtmosphere.normalMap = r_globeEarth.normalMap; r_globeEarthAtmosphere.glowScale = 1.0; r_globeEarthAtmosphere.blendScale = -1.0; r_globeEarthAtmosphere.glslProgram = r_state.atmosphere_program; R_SphereRender(&r_globeEarthAtmosphere, earthPos, rotate, fullscale, sunPos); } else { halo = R_FindImage("pics/geoscape/map_earth_halo", it_pic); if (halo != r_noTexture) { /** @todo Replace this magic number with some speaking constant */ const float earthSizeX = fullscale * 20500.0f * viddef.rx; const float earthSizeY = fullscale * 20500.0f * viddef.ry; glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); glDisable(GL_LIGHTING); R_DrawTexture(halo->texnum, earthPos[0] - earthSizeX * 0.5f, earthPos[1] - earthSizeY * 0.5f, earthSizeX, earthSizeY); glEnable(GL_LIGHTING); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } } R_DrawBuffers(1); glDisable(GL_DEPTH_TEST); /* draw nation overlay */ if (overlayNation) { r_globeEarth.overlay = R_FindImage(va("pics/geoscape/%s_nations_overlay", map), it_wrappic); if (r_globeEarth.overlay == r_noTexture) Com_Error(ERR_FATAL, "Could not load geoscape nation overlay image"); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); if (renderNationGlow) { /* draw glowing borders */ r_globeEarth.overlay = R_FindImage(va("pics/geoscape/%s_nations_overlay_glow", map), it_wrappic); if (r_globeEarth.overlay == r_noTexture) Com_Error(ERR_FATAL, "Could not load geoscape nation overlay glow image"); R_DrawBuffers(2); glDisable(GL_LIGHTING); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); glEnable(GL_LIGHTING); R_DrawBuffers(1); } r_globeEarth.overlay = nullptr; } /* draw XVI overlay */ if (overlayXVI) { r_globeEarth.overlay = R_FindImage(va("pics/geoscape/%s_xvi_overlay", map), it_wrappic); r_globeEarth.overlayAlphaMask = r_xviTexture; assert(r_globeEarth.overlayAlphaMask); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); r_globeEarth.overlayAlphaMask = nullptr; r_globeEarth.overlay = nullptr; } /* draw radar overlay */ if (overlayRadar) { r_globeEarth.overlay = r_radarTexture; assert(r_globeEarth.overlay); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); r_globeEarth.overlay = nullptr; } /* disable 3d geoscape lighting */ glDisable(GL_LIGHTING); /* restore the previous matrix */ glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); }