render.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:390k
源码类别:

OpenGL

开发平台:

Visual C++

  1. // render.cpp
  2. //
  3. // Copyright (C) 2001-2008, the Celestia Development Team
  4. // Original version by Chris Laurel <claurel@gmail.com>
  5. //
  6. // This program is free software; you can redistribute it and/or
  7. // modify it under the terms of the GNU General Public License
  8. // as published by the Free Software Foundation; either version 2
  9. // of the License, or (at your option) any later version.
  10. #include <algorithm>
  11. #include <cstdio>
  12. #include <cstring>
  13. #include <cassert>
  14. #include <sstream>
  15. #include <iomanip>
  16. #define DEBUG_COALESCE 0
  17. //#define DEBUG_HDR
  18. #ifdef DEBUG_HDR
  19. //#define DEBUG_HDR_FILE
  20. //#define DEBUG_HDR_ADAPT
  21. //#define DEBUG_HDR_TONEMAP
  22. #endif
  23. #ifdef DEBUG_HDR_FILE
  24. #include <fstream>
  25. std::ofstream hdrlog;
  26. #define HDR_LOG     hdrlog
  27. #else
  28. #define HDR_LOG     cout
  29. #endif
  30. #ifdef USE_HDR
  31. #define BLUR_PASS_COUNT     2
  32. #define BLUR_SIZE           128
  33. #define DEFAULT_EXPOSURE    -23.35f
  34. #define EXPOSURE_HALFLIFE   0.4f
  35. //#define USE_BLOOM_LISTS
  36. #endif
  37. #ifndef _WIN32
  38. #ifndef TARGET_OS_MAC
  39. #include <config.h>
  40. #endif
  41. #endif /* _WIN32 */
  42. #include <celutil/debug.h>
  43. #include <celmath/frustum.h>
  44. #include <celmath/distance.h>
  45. #include <celmath/intersect.h>
  46. #include <celutil/utf8.h>
  47. #include <celutil/util.h>
  48. #include <celutil/timer.h>
  49. #include "gl.h"
  50. #include "astro.h"
  51. #include "glext.h"
  52. #include "vecgl.h"
  53. #include "glshader.h"
  54. #include "shadermanager.h"
  55. #include "spheremesh.h"
  56. #include "lodspheremesh.h"
  57. #include "model.h"
  58. #include "regcombine.h"
  59. #include "vertexprog.h"
  60. #include "texmanager.h"
  61. #include "meshmanager.h"
  62. #include "render.h"
  63. #include "renderinfo.h"
  64. #include "renderglsl.h"
  65. #include "axisarrow.h"
  66. #include "frametree.h"
  67. #include "timelinephase.h"
  68. #include "skygrid.h"
  69. using namespace std;
  70. #define FOV           45.0f
  71. #define NEAR_DIST      0.5f
  72. #define FAR_DIST       1.0e9f
  73. // This should be in the GL headers, but where?
  74. #ifndef GL_COLOR_SUM_EXT
  75. #define GL_COLOR_SUM_EXT 0x8458
  76. #endif
  77. static const float  STAR_DISTANCE_LIMIT  = 1.0e6f;
  78. static const int REF_DISTANCE_TO_SCREEN  = 400; //[mm]
  79. // Contribution from planetshine beyond this distance (in units of object radius)
  80. // is considered insignificant.
  81. static const float PLANETSHINE_DISTANCE_LIMIT_FACTOR = 100.0f;
  82. // Planetshine from objects less than this pixel size is treated as insignificant
  83. // and will be ignored.
  84. static const float PLANETSHINE_PIXEL_SIZE_LIMIT      =   0.1f;
  85. // Distance from the Sun at which comet tails will start to fade out
  86. static const float COMET_TAIL_ATTEN_DIST_SOL = astro::AUtoKilometers(5.0f);
  87. static const int StarVertexListSize = 1024;
  88. // Fractional pixel offset used when rendering text as texture mapped
  89. // quads to ensure consistent mapping of texels to pixels.
  90. static const float PixelOffset = 0.125f;
  91. // These two values constrain the near and far planes of the view frustum
  92. // when rendering planet and object meshes.  The near plane will never be
  93. // closer than MinNearPlaneDistance, and the far plane is set so that far/near
  94. // will not exceed MaxFarNearRatio.
  95. static const float MinNearPlaneDistance = 0.0001f; // km
  96. static const float MaxFarNearRatio      = 2000000.0f;
  97. static const float RenderDistance       = 50.0f;
  98. // Star disc size in pixels
  99. static const float BaseStarDiscSize      = 5.0f;
  100. static const float MaxScaledDiscStarSize = 8.0f;
  101. static const float GlareOpacity = 0.65f;
  102. static const float MinRelativeOccluderRadius = 0.005f;
  103. static const float CubeCornerToCenterDistance = (float) sqrt(3.0);
  104. // The minimum apparent size of an objects orbit in pixels before we display
  105. // a label for it.  This minimizes label clutter.
  106. static const float MinOrbitSizeForLabel = 20.0f;
  107. // The minimum apparent size of a surface feature in pixels before we display
  108. // a label for it.
  109. static const float MinFeatureSizeForLabel = 20.0f;
  110. /* The maximum distance of the observer to the origin of coordinates before
  111.    asterism lines and labels start to linearly fade out (in uLY) */
  112. static const float MaxAsterismLabelsConstDist  = 6.0e6f;
  113. static const float MaxAsterismLinesConstDist   = 6.0e8f;
  114. /* The maximum distance of the observer to the origin of coordinates before
  115.    asterisms labels and lines fade out completely (in uLY) */
  116. static const float MaxAsterismLabelsDist = 2.0e7f;
  117. static const float MaxAsterismLinesDist  = 6.52e10f;
  118. // Maximum size of a solar system in light years. Features beyond this distance
  119. // will not necessarily be rendered correctly. This limit is used for
  120. // visibility culling of solar systems.
  121. static const float MaxSolarSystemSize = 1.0f;
  122. // Static meshes and textures used by all instances of Simulation
  123. static bool commonDataInitialized = false;
  124. static const string EMPTY_STRING("");
  125. LODSphereMesh* g_lodSphere = NULL;
  126. static Texture* normalizationTex = NULL;
  127. static Texture* starTex = NULL;
  128. static Texture* glareTex = NULL;
  129. static Texture* shadowTex = NULL;
  130. static Texture* gaussianDiscTex = NULL;
  131. static Texture* gaussianGlareTex = NULL;
  132. // Shadow textures are scaled down slightly to leave some extra blank pixels
  133. // near the border.  This keeps axis aligned streaks from appearing on hardware
  134. // that doesn't support clamp to border color.
  135. static const float ShadowTextureScale = 15.0f / 16.0f;
  136. static Texture* eclipseShadowTextures[4];
  137. static Texture* shadowMaskTexture = NULL;
  138. static Texture* penumbraFunctionTexture = NULL;
  139. Texture* rectToSphericalTexture = NULL;
  140. static const Color compassColor(0.4f, 0.4f, 1.0f);
  141. static const float CoronaHeight = 0.2f;
  142. static bool buggyVertexProgramEmulation = true;
  143. static const int MaxSkyRings = 32;
  144. static const int MaxSkySlices = 180;
  145. static const int MinSkySlices = 30;
  146. // Size at which the orbit cache will be flushed of old orbit paths
  147. static const unsigned int OrbitCacheCullThreshold = 200;
  148. // Age in frames at which unused orbit paths may be eliminated from the cache
  149. static const uint32 OrbitCacheRetireAge = 16;
  150. Color Renderer::StarLabelColor          (0.471f, 0.356f, 0.682f);
  151. Color Renderer::PlanetLabelColor        (0.407f, 0.333f, 0.964f);
  152. Color Renderer::DwarfPlanetLabelColor   (0.407f, 0.333f, 0.964f);
  153. Color Renderer::MoonLabelColor          (0.231f, 0.733f, 0.792f);
  154. Color Renderer::MinorMoonLabelColor     (0.231f, 0.733f, 0.792f);
  155. Color Renderer::AsteroidLabelColor      (0.596f, 0.305f, 0.164f);
  156. Color Renderer::CometLabelColor         (0.768f, 0.607f, 0.227f);
  157. Color Renderer::SpacecraftLabelColor    (0.93f,  0.93f,  0.93f);
  158. Color Renderer::LocationLabelColor      (0.24f,  0.89f,  0.43f);
  159. Color Renderer::GalaxyLabelColor        (0.0f,   0.45f,  0.5f);
  160. Color Renderer::GlobularLabelColor      (0.8f,   0.45f,  0.5f);
  161. Color Renderer::NebulaLabelColor        (0.541f, 0.764f, 0.278f);
  162. Color Renderer::OpenClusterLabelColor   (0.239f, 0.572f, 0.396f);
  163. Color Renderer::ConstellationLabelColor (0.225f, 0.301f, 0.36f);
  164. Color Renderer::EquatorialGridLabelColor(0.64f,  0.72f,  0.88f);
  165. Color Renderer::PlanetographicGridLabelColor(0.8f, 0.8f, 0.8f);
  166. Color Renderer::GalacticGridLabelColor  (0.88f,  0.72f,  0.64f);
  167. Color Renderer::EclipticGridLabelColor  (0.72f,  0.64f,  0.88f);
  168. Color Renderer::HorizonGridLabelColor   (0.72f,  0.72f,  0.72f);
  169. Color Renderer::StarOrbitColor          (0.5f,   0.5f,   0.8f);
  170. Color Renderer::PlanetOrbitColor        (0.3f,   0.323f, 0.833f);
  171. Color Renderer::DwarfPlanetOrbitColor   (0.3f,   0.323f, 0.833f);
  172. Color Renderer::MoonOrbitColor          (0.08f,  0.407f, 0.392f);
  173. Color Renderer::MinorMoonOrbitColor     (0.08f,  0.407f, 0.392f);
  174. Color Renderer::AsteroidOrbitColor      (0.58f,  0.152f, 0.08f);
  175. Color Renderer::CometOrbitColor         (0.639f, 0.487f, 0.168f);
  176. Color Renderer::SpacecraftOrbitColor    (0.4f,   0.4f,   0.4f);
  177. Color Renderer::SelectionOrbitColor     (1.0f,   0.0f,   0.0f);
  178. Color Renderer::ConstellationColor      (0.0f,   0.24f,  0.36f);
  179. Color Renderer::BoundaryColor           (0.24f,  0.10f,  0.12f);
  180. Color Renderer::EquatorialGridColor     (0.28f,  0.28f,  0.38f);
  181. Color Renderer::PlanetographicGridColor (0.8f,   0.8f,   0.8f);
  182. Color Renderer::PlanetEquatorColor      (0.5f,   1.0f,   1.0f);
  183. Color Renderer::GalacticGridColor       (0.38f,  0.38f,  0.28f);
  184. Color Renderer::EclipticGridColor       (0.38f,  0.28f,  0.38f);
  185. Color Renderer::HorizonGridColor        (0.38f,  0.38f,  0.38f);
  186. Color Renderer::EclipticColor           (0.5f,   0.1f,   0.1f);
  187. Color Renderer::SelectionCursorColor    (1.0f,   0.0f,   0.0f);
  188. // Some useful unit conversions
  189. inline float mmToInches(float mm)
  190. {
  191.     return mm * (1.0f / 25.4f);
  192. }
  193. inline float inchesToMm(float in)
  194. {
  195.     return in * 25.4f;
  196. }
  197. // Fade function for objects that shouldn't be shown when they're too small
  198. // on screen such as orbit paths and some object labels. The will fade linearly
  199. // from invisible at minSize pixels to full visibility at opaqueScale*minSize.
  200. inline float sizeFade(float screenSize, float minScreenSize, float opaqueScale)
  201. {
  202.     return min(1.0f, (screenSize - minScreenSize) / (minScreenSize * (opaqueScale - 1)));
  203. }
  204. // Calculate the cosine of half the maximum field of view. We'll use this for
  205. // fast testing of object visibility.  The function takes the vertical FOV (in
  206. // degrees) as an argument. When computing the view cone, we want the field of
  207. // view as measured on the diagonal between viewport corners.
  208. double computeCosViewConeAngle(double verticalFOV, double width, double height)
  209. {
  210.     double h = tan(degToRad(verticalFOV / 2));
  211.     double diag = sqrt(1.0 + square(h) + square(h * width / height));
  212.     return 1.0 / diag;
  213. }
  214. Renderer::Renderer() :
  215.     context(0),
  216.     windowWidth(0),
  217.     windowHeight(0),
  218.     fov(FOV),
  219.     cosViewConeAngle(computeCosViewConeAngle(fov, 1, 1)),
  220.     screenDpi(96),
  221.     corrFac(1.12f),
  222.     faintestAutoMag45deg(7.0f),
  223.     renderMode(GL_FILL),
  224.     labelMode(NoLabels),
  225.     renderFlags(ShowStars | ShowPlanets),
  226.     orbitMask(Body::Planet | Body::Moon | Body::Stellar),
  227.     ambientLightLevel(0.1f),
  228.     fragmentShaderEnabled(false),
  229.     vertexShaderEnabled(false),
  230.     brightnessBias(0.0f),
  231.     saturationMagNight(1.0f),
  232.     saturationMag(1.0f),
  233.     starStyle(FuzzyPointStars),
  234.     starVertexBuffer(NULL),
  235.     pointStarVertexBuffer(NULL),
  236.     glareVertexBuffer(NULL),
  237.     useVertexPrograms(false),
  238.     useRescaleNormal(false),
  239.     usePointSprite(false),
  240.     textureResolution(medres),
  241.     useNewStarRendering(false),
  242.     frameCount(0),
  243.     lastOrbitCacheFlush(0),
  244.     minOrbitSize(MinOrbitSizeForLabel),
  245.     distanceLimit(1.0e6f),
  246.     minFeatureSize(MinFeatureSizeForLabel),
  247.     locationFilter(~0u),
  248.     colorTemp(NULL),
  249. #ifdef USE_HDR
  250.     sceneTexture(0),
  251.     blurFormat(GL_RGBA),
  252.     useBlendSubtract(true),
  253.     useLuminanceAlpha(false),
  254.     bloomEnabled(true),
  255.     maxBodyMag(100.0f),
  256.     exposure(1.0f),
  257.     exposurePrev(1.0f),
  258.     brightPlus(0.0f),
  259. #endif
  260.     videoSync(false),
  261.     settingsChanged(true),
  262.     objectAnnotationSetOpen(false)
  263. {
  264.     starVertexBuffer = new StarVertexBuffer(2048);
  265.     pointStarVertexBuffer = new PointStarVertexBuffer(2048);
  266.     glareVertexBuffer = new PointStarVertexBuffer(2048);
  267.     skyVertices = new SkyVertex[MaxSkySlices * (MaxSkyRings + 1)];
  268.     skyIndices = new uint32[(MaxSkySlices + 1) * 2 * MaxSkyRings];
  269.     skyContour = new SkyContourPoint[MaxSkySlices + 1];
  270.     colorTemp = GetStarColorTable(ColorTable_Enhanced);
  271. #ifdef DEBUG_HDR_FILE
  272.     HDR_LOG.open("hdr.log", ios_base::app);
  273. #endif
  274. #ifdef USE_HDR
  275.     blurTextures = new Texture*[BLUR_PASS_COUNT];
  276.     blurTempTexture = NULL;
  277.     for (size_t i = 0; i < BLUR_PASS_COUNT; ++i)
  278.     {
  279.         blurTextures[i] = NULL;
  280.     }
  281. #endif
  282. #ifdef USE_BLOOM_LISTS
  283.     for (size_t i = 0; i < (sizeof gaussianLists/sizeof(GLuint)); ++i)
  284.     {
  285.         gaussianLists[i] = 0;
  286.     }
  287. #endif
  288.     for (int i = 0; i < (int) FontCount; i++)
  289.     {
  290.         font[i] = NULL;
  291.     }
  292. }
  293. Renderer::~Renderer()
  294. {
  295.     if (starVertexBuffer != NULL)
  296.         delete starVertexBuffer;
  297.     if (pointStarVertexBuffer != NULL)
  298.         delete pointStarVertexBuffer;
  299.     delete[] skyVertices;
  300.     delete[] skyIndices;
  301.     delete[] skyContour;
  302. #ifdef USE_BLOOM_LISTS
  303.     for (size_t i = 0; i < (sizeof gaussianLists/sizeof(GLuint)); ++i)
  304.     {
  305.         if (gaussianLists[i] != 0)
  306.             glDeleteLists(gaussianLists[i], 1);
  307.     }
  308. #endif
  309. #ifdef USE_HDR
  310.     for (size_t i = 0; i < BLUR_PASS_COUNT; ++i)
  311.     {
  312.         if (blurTextures[i] != NULL)
  313.             delete blurTextures[i];
  314.     }
  315.     delete [] blurTextures;
  316.     if (blurTempTexture)
  317.         delete blurTempTexture;
  318.     if (sceneTexture != 0)
  319.         glDeleteTextures(1, &sceneTexture);
  320. #endif
  321. }
  322. Renderer::DetailOptions::DetailOptions() :
  323.     ringSystemSections(100),
  324.     orbitPathSamplePoints(100),
  325.     shadowTextureSize(256),
  326.     eclipseTextureSize(128)
  327. {
  328. }
  329. static void StarTextureEval(float u, float v, float,
  330.                             unsigned char *pixel)
  331. {
  332.     float r = 1 - (float) sqrt(u * u + v * v);
  333.     if (r < 0)
  334.         r = 0;
  335.     else if (r < 0.5f)
  336.         r = 2.0f * r;
  337.     else
  338.         r = 1;
  339.     int pixVal = (int) (r * 255.99f);
  340.     pixel[0] = pixVal;
  341.     pixel[1] = pixVal;
  342.     pixel[2] = pixVal;
  343. }
  344. static void GlareTextureEval(float u, float v, float,
  345.                              unsigned char *pixel)
  346. {
  347.     float r = 0.9f - (float) sqrt(u * u + v * v);
  348.     if (r < 0)
  349.         r = 0;
  350.     int pixVal = (int) (r * 255.99f);
  351.     pixel[0] = 65;
  352.     pixel[1] = 64;
  353.     pixel[2] = 65;
  354.     pixel[3] = pixVal;
  355. }
  356. static void ShadowTextureEval(float u, float v, float,
  357.                               unsigned char *pixel)
  358. {
  359.     float r = (float) sqrt(u * u + v * v);
  360.     // Leave some white pixels around the edges to the shadow doesn't
  361.     // 'leak'.  We'll also set the maximum mip map level for this texture to 3
  362.     // so we don't have problems with the edge texels at high mip map levels.
  363.     int pixVal = r < 15.0f / 16.0f ? 0 : 255;
  364.     pixel[0] = pixVal;
  365.     pixel[1] = pixVal;
  366.     pixel[2] = pixVal;
  367. }
  368. //! Lookup function for eclipse penumbras--the input is the amount of overlap
  369. //  between the occluder and sun disc, and the output is the fraction of
  370. //  full brightness.
  371. static void PenumbraFunctionEval(float u, float, float,
  372.                                  unsigned char *pixel)
  373. {
  374.     u = (u + 1.0f) * 0.5f;
  375.     // Using the cube root produces a good visual result
  376.     unsigned char pixVal = (unsigned char) (::pow((double) u, 0.33) * 255.99);
  377.     pixel[0] = pixVal;
  378. }
  379. // ShadowTextureFunction is a function object for creating shadow textures
  380. // used for rendering eclipses.
  381. class ShadowTextureFunction : public TexelFunctionObject
  382. {
  383. public:
  384.     ShadowTextureFunction(float _umbra) : umbra(_umbra) {};
  385.     virtual void operator()(float u, float v, float w, unsigned char* pixel);
  386.     float umbra;
  387. };
  388. void ShadowTextureFunction::operator()(float u, float v, float,
  389.                                        unsigned char* pixel)
  390. {
  391.     float r = (float) sqrt(u * u + v * v);
  392.     int pixVal = 255;
  393.     // Leave some white pixels around the edges to the shadow doesn't
  394.     // 'leak'.  We'll also set the maximum mip map level for this texture to 3
  395.     // so we don't have problems with the edge texels at high mip map levels.
  396.     r = r / (15.0f / 16.0f);
  397.     if (r < 1)
  398.     {
  399.         // The pixel value should depend on the area of the sun which is
  400.         // occluded.  We just fudge it here and use the square root of the
  401.         // radius.
  402.         if (r <= umbra)
  403.             pixVal = 0;
  404.         else
  405.             pixVal = (int) (sqrt((r - umbra) / (1 - umbra)) * 255.99f);
  406.     }
  407.     pixel[0] = pixVal;
  408.     pixel[1] = pixVal;
  409.     pixel[2] = pixVal;
  410. };
  411. class ShadowMaskTextureFunction : public TexelFunctionObject
  412. {
  413. public:
  414.     ShadowMaskTextureFunction() {};
  415.     virtual void operator()(float u, float v, float w, unsigned char* pixel);
  416.     float dummy;
  417. };
  418. void ShadowMaskTextureFunction::operator()(float u, float, float,
  419.                                            unsigned char* pixel)
  420. {
  421.     unsigned char a = u > 0.0f ? 255 : 0;
  422.     pixel[0] = a;
  423.     pixel[1] = a;
  424.     pixel[2] = a;
  425.     pixel[3] = a;
  426. }
  427. static void IllumMapEval(float x, float y, float z,
  428.                          unsigned char* pixel)
  429. {
  430.     Vec3f v(x, y, z);
  431.     pixel[0] = 128 + (int) (127 * v.x);
  432.     pixel[1] = 128 + (int) (127 * v.y);
  433.     pixel[2] = 128 + (int) (127 * v.z);
  434. }
  435. #if 0
  436. // Not used yet.
  437. // The RectToSpherical map converts XYZ coordinates to UV coordinates
  438. // via a cube map lookup. However, a lot of GPUs don't support linear
  439. // interpolation of textures with > 8 bits per component, which is
  440. // inadequate precision for storing texture coordinates. To work around
  441. // this, we'll store the u and v texture coordinates with two 8 bit
  442. // coordinates each: rg for u, ba for v. The coordinates are unpacked
  443. // as: u = r * 255/256 + g * 1/255
  444. //     v = b * 255/256 + a * 1/255
  445. // This gives an effective precision of 16 bits for each texture coordinate.
  446. static void RectToSphericalMapEval(float x, float y, float z,
  447.                                    unsigned char* pixel)
  448. {
  449.     // Compute spherical coodinates (r is always 1)
  450.     double phi = asin(y);
  451.     double theta = atan2(z, -x);
  452.    
  453.     // Convert to texture coordinates
  454.     double u = (theta / PI + 1.0) * 0.5;
  455.     double v = (-phi / PI + 0.5);
  456.    
  457.     // Pack texture coordinates in red/green and blue/alpha
  458.     // u = red + green/256
  459.     // v = blue* + alpha/256
  460.     uint16 rg = (uint16) (u * 65535.99);
  461.     uint16 ba = (uint16) (v * 65535.99);
  462.     pixel[0] = rg >> 8;
  463.     pixel[1] = rg & 0xff;
  464.     pixel[2] = ba >> 8;
  465.     pixel[3] = ba & 0xff;
  466. }
  467. #endif
  468. static void BuildGaussianDiscMipLevel(unsigned char* mipPixels,
  469.                                       unsigned int log2size,
  470.                                       float fwhm,
  471.                                       float power)
  472. {
  473.     unsigned int size = 1 << log2size;
  474.     float sigma = fwhm / 2.3548f;
  475.     float isig2 = 1.0f / (2.0f * sigma * sigma);
  476.     float s = 1.0f / (sigma * (float) sqrt(2.0 * PI));
  477.     for (unsigned int i = 0; i < size; i++)
  478.     {
  479.         float y = (float) i - size / 2;
  480.         for (unsigned int j = 0; j < size; j++)
  481.         {
  482.             float x = (float) j - size / 2;
  483.             float r2 = x * x + y * y;
  484.             float f = s * (float) exp(-r2 * isig2) * power;
  485.             mipPixels[i * size + j] = (unsigned char) (255.99f * min(f, 1.0f));
  486.         }
  487.     }
  488. }
  489. static void BuildGlareMipLevel(unsigned char* mipPixels,
  490.                                unsigned int log2size,
  491.                                float scale,
  492.                                float base)
  493. {
  494.     unsigned int size = 1 << log2size;
  495.     for (unsigned int i = 0; i < size; i++)
  496.     {
  497.         float y = (float) i - size / 2;
  498.         for (unsigned int j = 0; j < size; j++)
  499.         {
  500.             float x = (float) j - size / 2;
  501.             float r = (float) sqrt(x * x + y * y);
  502.             float f = (float) pow(base, r * scale);
  503.             mipPixels[i * size + j] = (unsigned char) (255.99f * min(f, 1.0f));
  504.         }
  505.     }
  506. }
  507. #if 0
  508. // An alternate glare function, based roughly on results in Spencer, G. et al,
  509. // 1995, "Physically-Based Glare Effects for Digital Images"
  510. static void BuildGlareMipLevel2(unsigned char* mipPixels,
  511.                                 unsigned int log2size,
  512.                                 float scale)
  513. {
  514.     unsigned int size = 1 << log2size;
  515.     for (unsigned int i = 0; i < size; i++)
  516.     {
  517.         float y = (float) i - size / 2;
  518.         for (unsigned int j = 0; j < size; j++)
  519.         {
  520.             float x = (float) j - size / 2;
  521.             float r = (float) sqrt(x * x + y * y);
  522.             float f = 0.3f / (0.3f + r * r * scale * scale * 100);
  523.             /*
  524.             if (i == 0 || j == 0 || i == size - 1 || j == size - 1)
  525.                 f = 1.0f;
  526.             */
  527.             mipPixels[i * size + j] = (unsigned char) (255.99f * min(f, 1.0f));
  528.         }
  529.     }
  530. }
  531. #endif
  532. static Texture* BuildGaussianDiscTexture(unsigned int log2size)
  533. {
  534.     unsigned int size = 1 << log2size;
  535.     Image* img = new Image(GL_LUMINANCE, size, size, log2size + 1);
  536.     for (unsigned int mipLevel = 0; mipLevel <= log2size; mipLevel++)
  537.     {
  538.         float fwhm = (float) pow(2.0f, (float) (log2size - mipLevel)) * 0.3f;
  539.         BuildGaussianDiscMipLevel(img->getMipLevel(mipLevel),
  540.                                   log2size - mipLevel,
  541.                                   fwhm,
  542.                                   (float) pow(2.0f, (float) (log2size - mipLevel)));
  543.     }
  544.     ImageTexture* texture = new ImageTexture(*img,
  545.                                              Texture::BorderClamp,
  546.                                              Texture::DefaultMipMaps);
  547.     texture->setBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
  548.     delete img;
  549.     return texture;
  550. }
  551. static Texture* BuildGaussianGlareTexture(unsigned int log2size)
  552. {
  553.     unsigned int size = 1 << log2size;
  554.     Image* img = new Image(GL_LUMINANCE, size, size, log2size + 1);
  555.     for (unsigned int mipLevel = 0; mipLevel <= log2size; mipLevel++)
  556.     {
  557.         /*
  558.         // Optional gaussian glare
  559.         float fwhm = (float) pow(2.0f, (float) (log2size - mipLevel)) * 0.15f;
  560.         float power = (float) pow(2.0f, (float) (log2size - mipLevel)) * 0.15f;
  561.         BuildGaussianDiscMipLevel(img->getMipLevel(mipLevel),
  562.                                   log2size - mipLevel,
  563.                                   fwhm,
  564.                                   power);
  565.         */
  566.         BuildGlareMipLevel(img->getMipLevel(mipLevel),
  567.                            log2size - mipLevel,
  568.                            25.0f / (float) pow(2.0f, (float) (log2size - mipLevel)),
  569.                            0.66f);
  570.         /*
  571.         BuildGlareMipLevel2(img->getMipLevel(mipLevel),
  572.                             log2size - mipLevel,
  573.                             1.0f / (float) pow(2.0f, (float) (log2size - mipLevel)));
  574.         */
  575.     }
  576.     ImageTexture* texture = new ImageTexture(*img,
  577.                                              Texture::BorderClamp,
  578.                                              Texture::DefaultMipMaps);
  579.     texture->setBorderColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
  580.     delete img;
  581.     return texture;
  582. }
  583. static int translateLabelModeToClassMask(int labelMode)
  584. {
  585.     int classMask = 0;
  586.     if (labelMode & Renderer::PlanetLabels)
  587.         classMask |= Body::Planet;
  588.     if (labelMode & Renderer::DwarfPlanetLabels)
  589.         classMask |= Body::DwarfPlanet;
  590.     if (labelMode & Renderer::MoonLabels)
  591.         classMask |= Body::Moon;
  592.     if (labelMode & Renderer::MinorMoonLabels)
  593.         classMask |= Body::MinorMoon;
  594.     if (labelMode & Renderer::AsteroidLabels)
  595.         classMask |= Body::Asteroid;
  596.     if (labelMode & Renderer::CometLabels)
  597.         classMask |= Body::Comet;
  598.     if (labelMode & Renderer::SpacecraftLabels)
  599.         classMask |= Body::Spacecraft;
  600.     return classMask;
  601. }
  602. // Depth comparison function for render list entries
  603. bool operator<(const RenderListEntry& a, const RenderListEntry& b)
  604. {
  605.     // Operation is reversed because -z axis points into the screen
  606.     return a.centerZ - a.radius > b.centerZ - b.radius;
  607. }
  608. // Depth comparison for labels
  609. // Note that it's essential to declare this operator as a member
  610. // function of Renderer::Label; if it's not a class member, C++'s
  611. // argument dependent lookup will not find the operator when it's
  612. // used as a predicate for STL algorithms.
  613. bool Renderer::Annotation::operator<(const Annotation& a) const
  614. {
  615.     // Operation is reversed because -z axis points into the screen
  616.     return position.z > a.position.z;
  617. }
  618. // Depth comparison for orbit paths
  619. bool Renderer::OrbitPathListEntry::operator<(const Renderer::OrbitPathListEntry& o) const
  620. {
  621.     // Operation is reversed because -z axis points into the screen
  622.     return centerZ - radius > o.centerZ - o.radius;
  623. }
  624. bool Renderer::init(GLContext* _context,
  625.                     int winWidth, int winHeight,
  626.                     DetailOptions& _detailOptions)
  627. {
  628.     context = _context;
  629.     detailOptions = _detailOptions;
  630.     // Initialize static meshes and textures common to all instances of Renderer
  631.     if (!commonDataInitialized)
  632.     {
  633.         g_lodSphere = new LODSphereMesh();
  634.         starTex = CreateProceduralTexture(64, 64, GL_RGB, StarTextureEval);
  635.         glareTex = LoadTextureFromFile("textures/flare.jpg");
  636.         if (glareTex == NULL)
  637.             glareTex = CreateProceduralTexture(64, 64, GL_RGB, GlareTextureEval);
  638.         // Max mipmap level doesn't work reliably on all graphics
  639.         // cards.  In particular, Rage 128 and TNT cards resort to software
  640.         // rendering when this feature is enabled.  The only workaround is to
  641.         // disable mipmapping completely unless texture border clamping is
  642.         // supported, which solves the problem much more elegantly than all
  643.         // the mipmap level nonsense.
  644.         // shadowTex->setMaxMipMapLevel(3);
  645.         Texture::AddressMode shadowTexAddress = Texture::EdgeClamp;
  646.         Texture::MipMapMode shadowTexMip = Texture::NoMipMaps;
  647.         useClampToBorder = context->extensionSupported("GL_ARB_texture_border_clamp");
  648.         if (useClampToBorder)
  649.         {
  650.             shadowTexAddress = Texture::BorderClamp;
  651.             shadowTexMip = Texture::DefaultMipMaps;
  652.         }
  653.         shadowTex = CreateProceduralTexture(detailOptions.shadowTextureSize,
  654.                                             detailOptions.shadowTextureSize,
  655.                                             GL_RGB,
  656.                                             ShadowTextureEval,
  657.                                             shadowTexAddress, shadowTexMip);
  658.         shadowTex->setBorderColor(Color::White);
  659.         if (gaussianDiscTex == NULL)
  660.             gaussianDiscTex = BuildGaussianDiscTexture(8);
  661.         if (gaussianGlareTex == NULL)
  662.             gaussianGlareTex = BuildGaussianGlareTexture(9);
  663.         // Create the eclipse shadow textures
  664.         {
  665.             for (int i = 0; i < 4; i++)
  666.             {
  667.                 ShadowTextureFunction func(i * 0.25f);
  668.                 eclipseShadowTextures[i] =
  669.                     CreateProceduralTexture(detailOptions.eclipseTextureSize,
  670.                                             detailOptions.eclipseTextureSize,
  671.                                             GL_RGB, func,
  672.                                             shadowTexAddress, shadowTexMip);
  673.                 if (eclipseShadowTextures[i] != NULL)
  674.                 {
  675.                     // eclipseShadowTextures[i]->setMaxMipMapLevel(2);
  676.                     eclipseShadowTextures[i]->setBorderColor(Color::White);
  677.                 }
  678.             }
  679.         }
  680.         // Create the shadow mask texture
  681.         {
  682.             ShadowMaskTextureFunction func;
  683.             shadowMaskTexture = CreateProceduralTexture(128, 2, GL_RGBA, func);
  684.             //shadowMaskTexture->bindName();
  685.         }
  686.         // Create a function lookup table in a texture for use with
  687.         // fragment program eclipse shadows.
  688.         penumbraFunctionTexture = CreateProceduralTexture(512, 1, GL_LUMINANCE,
  689.                                                           PenumbraFunctionEval,
  690.                                                           Texture::EdgeClamp);
  691.         if (context->extensionSupported("GL_ARB_texture_cube_map"))
  692.         {
  693.             normalizationTex = CreateProceduralCubeMap(64, GL_RGB, IllumMapEval);
  694. #if ADVANCED_CLOUD_SHADOWS
  695.             rectToSphericalTexture = CreateProceduralCubeMap(128, GL_RGBA, RectToSphericalMapEval);
  696. #endif
  697.         }
  698. #ifdef USE_HDR
  699.         genSceneTexture();
  700.         genBlurTextures();
  701. #endif
  702.         commonDataInitialized = true;
  703.     }
  704. #if 0
  705.     if (context->extensionSupported("GL_ARB_multisample"))
  706.     {
  707.         int nSamples = 0;
  708.         int sampleBuffers = 0;
  709.         int enabled = (int) glIsEnabled(GL_MULTISAMPLE_ARB);
  710.         glGetIntegerv(GL_SAMPLE_BUFFERS_ARB, &sampleBuffers);
  711.         glGetIntegerv(GL_SAMPLES_ARB, &nSamples);
  712.         clog << "AA samples: " << nSamples
  713.              << ", enabled=" << (int) enabled
  714.              << ", sample buffers=" << (sampleBuffers)
  715.              << "n";
  716.         glEnable(GL_MULTISAMPLE_ARB);
  717.     }
  718. #endif
  719.     if (context->extensionSupported("GL_EXT_rescale_normal"))
  720.     {
  721.         // We need this enabled because we use glScale, but only
  722.         // with uniform scale factors.
  723.         DPRINTF(1, "Renderer: EXT_rescale_normal supported.n");
  724.         useRescaleNormal = true;
  725.         glEnable(GL_RESCALE_NORMAL_EXT);
  726.     }
  727.     if (context->extensionSupported("GL_ARB_point_sprite"))
  728.     {
  729.         DPRINTF(1, "Renderer: point sprites supported.n");
  730.         usePointSprite = true;
  731.     }
  732.     if (context->extensionSupported("GL_EXT_separate_specular_color"))
  733.     {
  734.         glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL_EXT, GL_SEPARATE_SPECULAR_COLOR_EXT);
  735.     }
  736.     // Ugly renderer-specific bug workarounds follow . . .
  737.     char* glRenderer = (char*) glGetString(GL_RENDERER);
  738.     if (glRenderer != NULL)
  739.     {
  740.         // Fog is broken with vertex program emulation in most versions of
  741.         // the GF 1 and 2 drivers; we need to detect this and disable
  742.         // vertex programs which output fog coordinates
  743.         if (strstr(glRenderer, "GeForce3") != NULL ||
  744.             strstr(glRenderer, "GeForce4") != NULL)
  745.         {
  746.             buggyVertexProgramEmulation = false;
  747.         }
  748.         if (strstr(glRenderer, "Savage4") != NULL ||
  749.             strstr(glRenderer, "ProSavage") != NULL)
  750.         {
  751.             // S3 Savage4 drivers appear to rescale normals without reporting
  752.             // EXT_rescale_normal.  Lighting will be messed up unless
  753.             // we set the useRescaleNormal flag.
  754.             useRescaleNormal = true;
  755.         }
  756. #ifdef TARGET_OS_MAC
  757.         if (strstr(glRenderer, "ATI") != NULL ||
  758.             strstr(glRenderer, "GMA 900") != NULL)
  759.         {
  760.             // Some drivers on the Mac appear to limit point sprite size.
  761.             // This causes an abrupt size transition when going from billboards
  762.             // to sprites. Rather than incur overhead accounting for the size limit,
  763.             // do not use sprites on these renderers.
  764.             // Affected cards: ATI (various), etc
  765.             // Renderer strings are not unique.
  766.             usePointSprite = false;
  767.         }
  768. #endif
  769.     }
  770.     // More ugly hacks; according to Matt Craighead at NVIDIA, an NVIDIA
  771.     // OpenGL driver that reports version 1.3.1 or greater will have working
  772.     // fog in emulated vertex programs.
  773.     char* glVersion = (char*) glGetString(GL_VERSION);
  774.     if (glVersion != NULL)
  775.     {
  776.         int major = 0, minor = 0, extra = 0;
  777.         int nScanned = sscanf(glVersion, "%d.%d.%d", &major, &minor, &extra);
  778.         if (nScanned >= 2)
  779.         {
  780.             if (major > 1 || minor > 3 || (minor == 3 && extra >= 1))
  781.                 buggyVertexProgramEmulation = false;
  782.         }
  783.     }
  784. #ifdef USE_HDR
  785.     useBlendSubtract = context->extensionSupported("GL_EXT_blend_subtract");
  786.     Image        *testImg = new Image(GL_LUMINANCE_ALPHA, 1, 1);
  787.     ImageTexture *testTex = new ImageTexture(*testImg,
  788.                                              Texture::EdgeClamp,
  789.                                              Texture::NoMipMaps);
  790.     delete testImg;
  791.     GLint actualTexFormat = 0;
  792.     glEnable(GL_TEXTURE_2D);
  793.     testTex->bind();
  794.     glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &actualTexFormat);
  795.     glBindTexture(GL_TEXTURE_2D, 0);
  796.     glDisable(GL_TEXTURE_2D);
  797.     switch (actualTexFormat)
  798.     {
  799.     case 2:
  800.     case GL_LUMINANCE_ALPHA:
  801.     case GL_LUMINANCE4_ALPHA4:
  802.     case GL_LUMINANCE6_ALPHA2:
  803.     case GL_LUMINANCE8_ALPHA8:
  804.     case GL_LUMINANCE12_ALPHA4:
  805.     case GL_LUMINANCE12_ALPHA12:
  806.     case GL_LUMINANCE16_ALPHA16:
  807.         useLuminanceAlpha = true;
  808.         break;
  809.     default:
  810.         useLuminanceAlpha = false;
  811.         break;
  812.     }
  813.     blurFormat = useLuminanceAlpha ? GL_LUMINANCE_ALPHA : GL_RGBA;
  814.     delete testTex;
  815. #endif
  816.     glLoadIdentity();
  817.     glEnable(GL_CULL_FACE);
  818.     glCullFace(GL_BACK);
  819.     glEnable(GL_COLOR_MATERIAL);
  820.     glEnable(GL_LIGHTING);
  821.     glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
  822.     // LEQUAL rather than LESS required for multipass rendering
  823.     glDepthFunc(GL_LEQUAL);
  824.     resize(winWidth, winHeight);
  825.     return true;
  826. }
  827. void Renderer::resize(int width, int height)
  828. {
  829. #ifdef USE_HDR
  830.     if (width == windowWidth && height == windowHeight)
  831.         return;
  832. #endif
  833.     windowWidth = width;
  834.     windowHeight = height;
  835.     cosViewConeAngle = computeCosViewConeAngle(fov, windowWidth, windowHeight);
  836.     // glViewport(windowWidth, windowHeight);
  837. #ifdef USE_HDR
  838.     if (commonDataInitialized)
  839.     {
  840.         genSceneTexture();
  841.         genBlurTextures();
  842.     }
  843. #endif
  844. }
  845. float Renderer::calcPixelSize(float fovY, float windowHeight)
  846. {
  847.     return 2 * (float) tan(degToRad(fovY / 2.0)) / (float) windowHeight;
  848. }
  849. void Renderer::setFieldOfView(float _fov)
  850. {
  851.     fov = _fov;
  852.     corrFac = (0.12f * fov/FOV * fov/FOV + 1.0f);
  853.     cosViewConeAngle = computeCosViewConeAngle(fov, windowWidth, windowHeight);
  854. }
  855. int Renderer::getScreenDpi() const
  856. {
  857.     return screenDpi;
  858. }
  859. void Renderer::setScreenDpi(int _dpi)
  860. {
  861.     screenDpi = _dpi;
  862. }
  863. void Renderer::setFaintestAM45deg(float _faintestAutoMag45deg)
  864. {
  865.     faintestAutoMag45deg = _faintestAutoMag45deg;
  866.     markSettingsChanged();
  867. }
  868. float Renderer::getFaintestAM45deg() const
  869. {
  870.     return faintestAutoMag45deg;
  871. }
  872. unsigned int Renderer::getResolution() const
  873. {
  874.     return textureResolution;
  875. }
  876. void Renderer::setResolution(unsigned int resolution)
  877. {
  878.     if (resolution < TEXTURE_RESOLUTION)
  879.         textureResolution = resolution;
  880.     markSettingsChanged();
  881. }
  882. TextureFont* Renderer::getFont(FontStyle fs) const
  883. {
  884.     return font[(int) fs];
  885. }
  886. void Renderer::setFont(FontStyle fs, TextureFont* txf)
  887. {
  888.     font[(int) fs] = txf;
  889.     markSettingsChanged();
  890. }
  891. void Renderer::setRenderMode(int _renderMode)
  892. {
  893.     renderMode = _renderMode;
  894.     markSettingsChanged();
  895. }
  896. int Renderer::getRenderFlags() const
  897. {
  898.     return renderFlags;
  899. }
  900. void Renderer::setRenderFlags(int _renderFlags)
  901. {
  902.     renderFlags = _renderFlags;
  903.     markSettingsChanged();
  904. }
  905. int Renderer::getLabelMode() const
  906. {
  907.     return labelMode;
  908. }
  909. void Renderer::setLabelMode(int _labelMode)
  910. {
  911.     labelMode = _labelMode;
  912.     markSettingsChanged();
  913. }
  914. int Renderer::getOrbitMask() const
  915. {
  916.     return orbitMask;
  917. }
  918. void Renderer::setOrbitMask(int mask)
  919. {
  920.     orbitMask = mask;
  921.     markSettingsChanged();
  922. }
  923. const ColorTemperatureTable*
  924. Renderer::getStarColorTable() const
  925. {
  926.     return colorTemp;
  927. }
  928. void
  929. Renderer::setStarColorTable(const ColorTemperatureTable* ct)
  930. {
  931.     colorTemp = ct;
  932.     markSettingsChanged();
  933. }
  934. bool Renderer::getVideoSync() const
  935. {
  936.     return videoSync;
  937. }
  938. void Renderer::setVideoSync(bool sync)
  939. {
  940.     videoSync = sync;
  941.     markSettingsChanged();
  942. }
  943. float Renderer::getAmbientLightLevel() const
  944. {
  945.     return ambientLightLevel;
  946. }
  947. void Renderer::setAmbientLightLevel(float level)
  948. {
  949.     ambientLightLevel = level;
  950.     markSettingsChanged();
  951. }
  952. float Renderer::getMinimumFeatureSize() const
  953. {
  954.     return minFeatureSize;
  955. }
  956. void Renderer::setMinimumFeatureSize(float pixels)
  957. {
  958.     minFeatureSize = pixels;
  959.     markSettingsChanged();
  960. }
  961. float Renderer::getMinimumOrbitSize() const
  962. {
  963.     return minOrbitSize;
  964. }
  965. // Orbits and labels are only rendered when the orbit of the object
  966. // occupies some minimum number of pixels on screen.
  967. void Renderer::setMinimumOrbitSize(float pixels)
  968. {
  969.     minOrbitSize = pixels;
  970.     markSettingsChanged();
  971. }
  972. float Renderer::getDistanceLimit() const
  973. {
  974.     return distanceLimit;
  975. }
  976. void Renderer::setDistanceLimit(float distanceLimit_)
  977. {
  978.     distanceLimit = distanceLimit_;
  979.     markSettingsChanged();
  980. }
  981. bool Renderer::getFragmentShaderEnabled() const
  982. {
  983.     return fragmentShaderEnabled;
  984. }
  985. void Renderer::setFragmentShaderEnabled(bool enable)
  986. {
  987.     fragmentShaderEnabled = enable && fragmentShaderSupported();
  988.     markSettingsChanged();
  989. }
  990. bool Renderer::fragmentShaderSupported() const
  991. {
  992.     return context->bumpMappingSupported();
  993. }
  994. bool Renderer::getVertexShaderEnabled() const
  995. {
  996.     return vertexShaderEnabled;
  997. }
  998. void Renderer::setVertexShaderEnabled(bool enable)
  999. {
  1000.     vertexShaderEnabled = enable && vertexShaderSupported();
  1001.     markSettingsChanged();
  1002. }
  1003. bool Renderer::vertexShaderSupported() const
  1004. {
  1005.     return useVertexPrograms;
  1006. }
  1007. void Renderer::addAnnotation(vector<Annotation>& annotations,
  1008.                              const MarkerRepresentation* markerRep,
  1009.                              const string& labelText,
  1010.                              Color color,
  1011.                              const Point3f& pos,
  1012.                              LabelAlignment halign,
  1013.                              LabelVerticalAlignment valign,
  1014.                              float size)
  1015. {
  1016.     double winX, winY, winZ;
  1017.     int view[4] = { 0, 0, 0, 0 };
  1018.     view[0] = -windowWidth / 2;
  1019.     view[1] = -windowHeight / 2;
  1020.     view[2] = windowWidth;
  1021.     view[3] = windowHeight;
  1022.     float depth = (float) (pos.x * modelMatrix[2] +
  1023.                            pos.y * modelMatrix[6] +
  1024.                            pos.z * modelMatrix[10]);
  1025.     if (gluProject(pos.x, pos.y, pos.z,
  1026.                    modelMatrix,
  1027.                    projMatrix,
  1028.                    (const GLint*) view,
  1029.                    &winX, &winY, &winZ) != GL_FALSE)
  1030.     {
  1031.         Annotation a;
  1032.         a.labelText[0] = '';
  1033.         ReplaceGreekLetterAbbr(a.labelText, MaxLabelLength, labelText.c_str(), labelText.length());
  1034.         a.labelText[MaxLabelLength - 1] = '';
  1035.         a.markerRep = markerRep;
  1036.         a.color = color;
  1037.         a.position = Point3f((float) winX, (float) winY, -depth);
  1038.         a.halign = halign;
  1039.         a.valign = valign;
  1040.         a.size = size;
  1041.         annotations.push_back(a);
  1042.     }
  1043. }
  1044. void Renderer::addForegroundAnnotation(const MarkerRepresentation* markerRep,
  1045.                                        const string& labelText,
  1046.                                        Color color,
  1047.                                        const Point3f& pos,
  1048.                                        LabelAlignment halign,
  1049.                                        LabelVerticalAlignment valign,
  1050.                                        float size)
  1051. {
  1052.     addAnnotation(foregroundAnnotations, markerRep, labelText, color, pos, halign, valign, size);
  1053. }
  1054. void Renderer::addBackgroundAnnotation(const MarkerRepresentation* markerRep,
  1055.                                        const string& labelText,
  1056.                                        Color color,
  1057.                                        const Point3f& pos,
  1058.                                        LabelAlignment halign,
  1059.                                        LabelVerticalAlignment valign,
  1060.                                        float size)
  1061. {
  1062.     addAnnotation(backgroundAnnotations, markerRep, labelText, color, pos, halign, valign, size);
  1063. }
  1064. void Renderer::addSortedAnnotation(const MarkerRepresentation* markerRep,
  1065.                                    const string& labelText,
  1066.                                    Color color,
  1067.                                    const Point3f& pos,
  1068.                                    LabelAlignment halign,
  1069.                                    LabelVerticalAlignment valign,
  1070.                                    float size)
  1071. {
  1072.     double winX, winY, winZ;
  1073.     int view[4] = { 0, 0, 0, 0 };
  1074.     view[0] = -windowWidth / 2;
  1075.     view[1] = -windowHeight / 2;
  1076.     view[2] = windowWidth;
  1077.     view[3] = windowHeight;
  1078.     float depth = (float) (pos.x * modelMatrix[2] +
  1079.                            pos.y * modelMatrix[6] +
  1080.                            pos.z * modelMatrix[10]);
  1081.     if (gluProject(pos.x, pos.y, pos.z,
  1082.                    modelMatrix,
  1083.                    projMatrix,
  1084.                    (const GLint*) view,
  1085.                    &winX, &winY, &winZ) != GL_FALSE)
  1086.     {
  1087.         Annotation a;
  1088.         
  1089.         a.labelText[0] = '';
  1090.         if (markerRep == NULL)
  1091.         {
  1092.             //l.text = ReplaceGreekLetterAbbr(_(text.c_str()));
  1093.             strncpy(a.labelText, labelText.c_str(), MaxLabelLength);
  1094.             a.labelText[MaxLabelLength - 1] = '';
  1095.         }
  1096.         a.markerRep = markerRep;
  1097.         a.color = color;
  1098.         a.position = Point3f((float) winX, (float) winY, -depth);
  1099.         a.halign = halign;
  1100.         a.valign = valign;
  1101.         a.size = size;
  1102.         depthSortedAnnotations.push_back(a);
  1103.     }
  1104. }
  1105. void Renderer::clearAnnotations(vector<Annotation>& annotations)
  1106. {
  1107.     annotations.clear();
  1108. }
  1109. void Renderer::clearSortedAnnotations()
  1110. {
  1111.     depthSortedAnnotations.clear();
  1112. }
  1113. // Return the orientation of the camera used to render the current
  1114. // frame. Available only while rendering a frame.
  1115. Quatf Renderer::getCameraOrientation() const
  1116. {
  1117.     return m_cameraOrientation;
  1118. }
  1119. float Renderer::getNearPlaneDistance() const
  1120. {
  1121.     return depthPartitions[currentIntervalIndex].nearZ;
  1122. }
  1123. void Renderer::beginObjectAnnotations()
  1124. {
  1125.     // It's an error to call beginObjectAnnotations a second time
  1126.     // without first calling end.
  1127.     assert(!objectAnnotationSetOpen);
  1128.     assert(objectAnnotations.empty());
  1129.     objectAnnotations.clear();
  1130.     objectAnnotationSetOpen = true;
  1131. }
  1132. void Renderer::endObjectAnnotations()
  1133. {
  1134.     objectAnnotationSetOpen = false;
  1135.     
  1136.     if (!objectAnnotations.empty())
  1137.     {
  1138.         renderAnnotations(objectAnnotations.begin(),
  1139.                           objectAnnotations.end(),
  1140.                           -depthPartitions[currentIntervalIndex].nearZ,
  1141.                           -depthPartitions[currentIntervalIndex].farZ,
  1142.                           FontNormal);
  1143.         objectAnnotations.clear();
  1144.     }
  1145. }
  1146. void Renderer::addObjectAnnotation(const MarkerRepresentation* markerRep,
  1147.                                    const string& labelText,
  1148.                                    Color color,
  1149.                                    const Point3f& pos)
  1150. {
  1151.     assert(objectAnnotationSetOpen);
  1152.     if (objectAnnotationSetOpen)
  1153.     {
  1154.         double winX, winY, winZ;
  1155.         int view[4] = { 0, 0, 0, 0 };
  1156.         view[0] = -windowWidth / 2;
  1157.         view[1] = -windowHeight / 2;
  1158.         view[2] = windowWidth;
  1159.         view[3] = windowHeight;
  1160.         float depth = (float) (pos.x * modelMatrix[2] +
  1161.                                pos.y * modelMatrix[6] +
  1162.                                pos.z * modelMatrix[10]);
  1163.         if (gluProject(pos.x, pos.y, pos.z,
  1164.                        modelMatrix,
  1165.                        projMatrix,
  1166.                        (const GLint*) view,
  1167.                        &winX, &winY, &winZ) != GL_FALSE)
  1168.         {
  1169.             Annotation a;
  1170.             
  1171.             a.labelText[0] = '';
  1172.             if (!labelText.empty())
  1173.             {
  1174.                 strncpy(a.labelText, labelText.c_str(), MaxLabelLength);
  1175.                 a.labelText[MaxLabelLength - 1] = '';
  1176.             }
  1177.             a.markerRep = markerRep;
  1178.             a.color = color;
  1179.             a.position = Point3f((float) winX, (float) winY, -depth);
  1180.             a.size = 0.0f;
  1181.             objectAnnotations.push_back(a);
  1182.         }
  1183.     }
  1184. }
  1185. static void enableSmoothLines()
  1186. {
  1187.     // glEnable(GL_BLEND);
  1188. #ifdef USE_HDR
  1189.     glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
  1190. #else
  1191.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1192. #endif
  1193.     glEnable(GL_LINE_SMOOTH);
  1194.     glLineWidth(1.5f);
  1195. }
  1196. static void disableSmoothLines()
  1197. {
  1198.     // glDisable(GL_BLEND);
  1199.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  1200.     glDisable(GL_LINE_SMOOTH);
  1201.     glLineWidth(1.0f);
  1202. }
  1203. class OrbitSampler : public OrbitSampleProc
  1204. {
  1205. public:
  1206.     vector<Renderer::OrbitSample>* samples;
  1207.     OrbitSampler(vector<Renderer::OrbitSample>* _samples) : samples(_samples) {};
  1208.     void sample(double t, const Point3d& p)
  1209.     {
  1210.         Renderer::OrbitSample samp;
  1211.         samp.pos = p;
  1212.         samp.t = t;
  1213.         samples->push_back(samp);
  1214.     };
  1215. };
  1216. void renderOrbitColor(const Body *body, bool selected, float opacity)
  1217. {
  1218.     Color orbitColor;
  1219.     if (selected)
  1220.     {
  1221.         // Highlight the orbit of the selected object in red
  1222.         orbitColor = Renderer::SelectionOrbitColor;
  1223.     }
  1224.     else if (body != NULL && body->isOrbitColorOverridden())
  1225.     {
  1226.         orbitColor = body->getOrbitColor();
  1227.     }
  1228.     else
  1229.     {
  1230.         int classification;
  1231.         if (body != NULL)
  1232.             classification = body->getOrbitClassification();
  1233.         else
  1234.             classification = Body::Stellar;
  1235.         switch (classification)
  1236.         {
  1237.         case Body::Moon:
  1238.             orbitColor = Renderer::MoonOrbitColor;
  1239.             break;
  1240.         case Body::MinorMoon:
  1241.             orbitColor = Renderer::MinorMoonOrbitColor;
  1242.             break;
  1243.         case Body::Asteroid:
  1244.             orbitColor = Renderer::AsteroidOrbitColor;
  1245.             break;
  1246.         case Body::Comet:
  1247.             orbitColor = Renderer::CometOrbitColor;
  1248.             break;
  1249.         case Body::Spacecraft:
  1250.             orbitColor = Renderer::SpacecraftOrbitColor;
  1251.             break;
  1252.         case Body::Stellar:
  1253.             orbitColor = Renderer::StarOrbitColor;
  1254.             break;
  1255.         case Body::DwarfPlanet:
  1256.             orbitColor = Renderer::DwarfPlanetOrbitColor;
  1257.             break;
  1258.         case Body::Planet:
  1259.         default:
  1260.             orbitColor = Renderer::PlanetOrbitColor;
  1261.             break;
  1262.         }
  1263.     }
  1264. #ifdef USE_HDR
  1265.     glColor(orbitColor, 1.f - opacity * orbitColor.alpha());
  1266. #else
  1267.     glColor(orbitColor, opacity * orbitColor.alpha());
  1268. #endif
  1269. }
  1270. // Subdivide the orbit into sections and compute a bounding volume for each section. The bounding
  1271. // volumes used are capsules, the set of all points less than some constant distance from a line
  1272. // segment.
  1273. static void computeOrbitSectionBoundingVolumes(Renderer::CachedOrbit& orbit)
  1274. {
  1275.     const unsigned int MinOrbitSections = 6;
  1276.     const unsigned int MinSamplesPerSection = 32;
  1277.     // Determine the number of trajectory samples to include in each bounding volume; typically,
  1278.     // the last volume will contain any leftover samples.
  1279.     unsigned int nSections = max((unsigned int) orbit.trajectory.size() / MinSamplesPerSection, MinOrbitSections);
  1280.     unsigned int samplesPerSection = orbit.trajectory.size() / nSections;
  1281.     if (samplesPerSection <= 1)
  1282.     {
  1283.         if (orbit.trajectory.size() == 0)
  1284.             nSections = 0;
  1285.         else
  1286.             nSections = 1;
  1287.     }
  1288.     for (unsigned int i = 0; i < nSections; i++)
  1289.     {
  1290.         unsigned int nSamples;
  1291.         if (i != nSections - 1)
  1292.             nSamples = samplesPerSection;
  1293.         else
  1294.             nSamples = orbit.trajectory.size() - (nSections - 1) * samplesPerSection;
  1295.         Renderer::OrbitSection section;
  1296.         section.firstSample = samplesPerSection * i;
  1297.         unsigned int lastSample = min((unsigned int) orbit.trajectory.size() - 1, section.firstSample + nSamples + 1);
  1298.         // Set the initial axis and origin of the capsule bounding volume; they will be adjusted
  1299.         // to contain all points in the trajectory. The length of the axis may change, but the
  1300.         // direction will remain the same.
  1301.         Vec3d axis = orbit.trajectory[section.firstSample].pos - orbit.trajectory[lastSample].pos;
  1302.         Point3d orig = orbit.trajectory[section.firstSample].pos;
  1303.         double d = 1.0 / (axis * axis);
  1304.         double minT = 0.0;
  1305.         double maxT = 0.0;
  1306.         double maxDistSquared = 0.0;
  1307.         for (unsigned int j = section.firstSample; j <= lastSample; j++)
  1308.         {
  1309.             Point3d p = orbit.trajectory[j].pos;
  1310.             double t = ((p - orig) * axis) * d;
  1311.             Vec3d pointToAxis = p - (orig + axis * t);
  1312.             double distSquared = pointToAxis * pointToAxis;
  1313.             if (t < minT)
  1314.                 minT = t;
  1315.             if (t > maxT)
  1316.                 maxT = t;
  1317.             if (distSquared > maxDistSquared)
  1318.                 maxDistSquared = distSquared;
  1319.         }
  1320.         section.boundingVolume.origin = orig + axis * minT;
  1321.         section.boundingVolume.axis = axis * (maxT - minT);
  1322.         // Make the bounding volume a bit thicker to avoid roundoff problems, and
  1323.         // to account cases when interpolation adds points slightly outside the
  1324.         // volume defined by the sampled points.
  1325.         section.boundingVolume.radius = sqrt(maxDistSquared) * 1.1f;;
  1326.         orbit.sections.push_back(section);
  1327.     }
  1328. }
  1329. static Point3d cubicInterpolate(const Point3d& p0, const Vec3d& v0,
  1330.                                 const Point3d& p1, const Vec3d& v1,
  1331.                                 double t)
  1332. {
  1333.     return p0 + (((2.0 * (p0 - p1) + v1 + v0) * (t * t * t)) +
  1334.                  ((3.0 * (p1 - p0) - 2.0 * v0 - v1) * (t * t)) +
  1335.                  (v0 * t));
  1336. }
  1337. static int splinesRendered = 0;
  1338. static int orbitsRendered = 0;
  1339. static int orbitsSkipped = 0;
  1340. static int sectionsCulled = 0;
  1341. static Point3d renderOrbitSplineSegment(const Renderer::OrbitSample& p0,
  1342.                                         const Renderer::OrbitSample& p1,
  1343.                                         const Renderer::OrbitSample& p2,
  1344.                                         const Renderer::OrbitSample& p3,
  1345.                                         double nearZ,
  1346.                                         double farZ,
  1347.                                         unsigned int subdivisions,
  1348.                                         int lastOutcode,
  1349.                                         bool drawLastSegment)
  1350. {
  1351.     Vec3d v0 = (p2.pos - p0.pos) * ((p2.t - p1.t) / (p2.t - p0.t));
  1352.     Vec3d v1 = (p3.pos - p1.pos) * ((p2.t - p1.t) / (p3.t - p1.t));
  1353.     double dt = 1.0 / (double) subdivisions;
  1354.     if (drawLastSegment)
  1355.         subdivisions++;
  1356.     splinesRendered++;
  1357.     Point3d lastP = p1.pos;
  1358.     for (unsigned int i = 0; i < subdivisions; i++)
  1359.     {
  1360.         Point3d p = cubicInterpolate(p1.pos, v0, p2.pos, v1, i * dt);
  1361.         int outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
  1362.         if ((outcode | lastOutcode) == 0)
  1363.         {
  1364.             glVertex3d(p.x, p.y, p.z);
  1365.         }
  1366.         else if ((outcode & lastOutcode) == 0)
  1367.         {
  1368.             // Need to clip
  1369.             Point3d q0 = lastP;
  1370.             Point3d q1 = p;
  1371.             if (lastOutcode != 0)
  1372.             {
  1373.                 glBegin(GL_LINE_STRIP);
  1374.                 double t;
  1375.                 if (lastOutcode == 1)
  1376.                     t = (nearZ - lastP.z) / (p.z - lastP.z);
  1377.                 else
  1378.                     t = (farZ - lastP.z) / (p.z - lastP.z);
  1379.                 q0 = lastP + t * (p - lastP);
  1380.             }
  1381.             if (outcode != 0)
  1382.             {
  1383.                 double t;
  1384.                 if (outcode == 1)
  1385.                     t = (nearZ - lastP.z) / (p.z - lastP.z);
  1386.                 else
  1387.                     t = (farZ - lastP.z) / (p.z - lastP.z);
  1388.                 q1 = lastP + t * (p - lastP);
  1389.             }
  1390.             glVertex3d(q0.x, q0.y, q0.z);
  1391.             glVertex3d(q1.x, q1.y, q1.z);
  1392.             if (outcode != 0)
  1393.             {
  1394.                 glEnd();
  1395.             }
  1396.         }
  1397.         lastOutcode = outcode;
  1398.         lastP = p;
  1399.     }
  1400.     return lastP;
  1401. }
  1402. #if 0
  1403. // Not yet used
  1404. static Point3d renderOrbitSplineAdaptive(const Renderer::OrbitSample& p0,
  1405.                                          const Renderer::OrbitSample& p1,
  1406.                                          const Renderer::OrbitSample& p2,
  1407.                                          const Renderer::OrbitSample& p3,
  1408.                                          double nearZ,
  1409.                                          double farZ,
  1410.                                          unsigned int minSubdivisions,
  1411.                                          unsigned int maxSubdivisions,
  1412.                                          int lastOutcode,
  1413.                                          bool drawLastSegment)
  1414. {
  1415.     Vec3d v0 = (p2.pos - p0.pos) * ((p2.t - p1.t) / (p2.t - p0.t));
  1416.     Vec3d v1 = (p3.pos - p1.pos) * ((p2.t - p1.t) / (p3.t - p1.t));
  1417.     double minDt = 1.0 / (double) maxSubdivisions;
  1418.     double maxDt = 1.0 / (double) minSubdivisions;
  1419.     double g = (p2.pos - p1.pos).length() * maxSubdivisions;
  1420.     double t = 0.0;
  1421.     splinesRendered += 10000;
  1422.     Point3d lastP = p1.pos;
  1423.     while (t < 1.0)
  1424.     {
  1425.         t += max(minDt, max(lastP.distanceFromOrigin() / g, maxDt));
  1426.         if (drawLastSegment && t > 1.0)
  1427.             t = 1.0;
  1428.         else
  1429.             break;
  1430.         Point3d p = cubicInterpolate(p1.pos, v0, p2.pos, v1, t);
  1431.         int outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
  1432.         if ((outcode | lastOutcode) == 0)
  1433.         {
  1434.             glVertex3d(p.x, p.y, p.z);
  1435.         }
  1436.         else if ((outcode & lastOutcode) == 0)
  1437.         {
  1438.             // Need to clip
  1439.             Point3d q0 = lastP;
  1440.             Point3d q1 = p;
  1441.             if (lastOutcode != 0)
  1442.             {
  1443.                 glBegin(GL_LINE_STRIP);
  1444.                 double t;
  1445.                 if (lastOutcode == 1)
  1446.                     t = (nearZ - lastP.z) / (p.z - lastP.z);
  1447.                 else
  1448.                     t = (farZ - lastP.z) / (p.z - lastP.z);
  1449.                 q0 = lastP + t * (p - lastP);
  1450.             }
  1451.             if (outcode != 0)
  1452.             {
  1453.                 double t;
  1454.                 if (outcode == 1)
  1455.                     t = (nearZ - lastP.z) / (p.z - lastP.z);
  1456.                 else
  1457.                     t = (farZ - lastP.z) / (p.z - lastP.z);
  1458.                 q1 = lastP + t * (p - lastP);
  1459.             }
  1460.             glVertex3d(q0.x, q0.y, q0.z);
  1461.             glVertex3d(q1.x, q1.y, q1.z);
  1462.             if (outcode != 0)
  1463.             {
  1464.                 glEnd();
  1465.             }
  1466.         }
  1467.         lastOutcode = outcode;
  1468.         lastP = p;
  1469.     }
  1470.     return lastP;
  1471. }
  1472. #endif
  1473. static Point3d renderOrbitSection(const Orbit& orbit,
  1474.                                   Renderer::CachedOrbit& cachedOrbit,
  1475.                                   unsigned int sectionNumber,
  1476.                                   const Mat4d& modelview,
  1477.                                   Point3d lastP,
  1478.                                   int lastOutcode,
  1479.                                   double nearZ,
  1480.                                   double farZ,
  1481.                                   uint32 /* renderFlags */)
  1482. {
  1483.     vector<Renderer::OrbitSample>& trajectory(cachedOrbit.trajectory);
  1484.     unsigned int nPoints = cachedOrbit.trajectory.size();
  1485.     unsigned int firstPoint = cachedOrbit.sections[sectionNumber].firstSample + 1;
  1486.     unsigned int lastPoint;
  1487.     if (sectionNumber != cachedOrbit.sections.size() - 1)
  1488.         lastPoint = cachedOrbit.sections[sectionNumber + 1].firstSample;
  1489.     else
  1490.         lastPoint = nPoints - 1;
  1491.     double sectionRadius = cachedOrbit.sections[sectionNumber].boundingVolume.radius;
  1492.     double minSmoothZ = -sectionRadius * 8;
  1493.     double maxSmoothZ = nearZ + sectionRadius * 8;
  1494.     for (unsigned int i = firstPoint; i <= lastPoint; i++)
  1495.     {
  1496.         Point3d p = trajectory[i].pos * modelview;
  1497.         int outcode;
  1498.         // This segment of the orbit is very close to the camera and may appear
  1499.         // jagged. We'll add extra segments with cubic spline interpolation.
  1500.         // TODO: This calculation should depend on the field of view as well
  1501.         unsigned int splineSubdivisions = 0;
  1502.         if ((p.z > minSmoothZ || lastP.z > minSmoothZ) &&
  1503.             !(p.z > maxSmoothZ && lastP.z > maxSmoothZ))
  1504.         {
  1505.             double distFromEye = distanceToSegment(Point3d(0.0, 0.0, 0.0), lastP, p - lastP);
  1506.             if (distFromEye < sectionRadius)
  1507.                 splineSubdivisions = 64;
  1508.             else if (distFromEye < sectionRadius * 8)
  1509.                 splineSubdivisions = (unsigned int) (sectionRadius / distFromEye * 64);
  1510.         }
  1511.         if (splineSubdivisions > 1)
  1512.         {
  1513.             // Render this part of the orbit as a spline instead of a line segment
  1514.             Renderer::OrbitSample s0, s1, s2, s3;
  1515.             if (i > 1)
  1516.             {
  1517.                 s0 = trajectory[i - 2];
  1518.             }
  1519.             else if (orbit.isPeriodic())
  1520.             {
  1521.                 // Careful: use second to last sample, since first sample is duplicate of last
  1522.                 s0 = trajectory[nPoints - 2];
  1523.                 s0.t -= orbit.getPeriod();
  1524.             }
  1525.             else
  1526.             {
  1527.                 s0 = trajectory[i - 1];
  1528.             }
  1529.             if (i < trajectory.size() - 1)
  1530.             {
  1531.                 s3 = trajectory[i + 1];
  1532.             }
  1533.             else if (orbit.isPeriodic())
  1534.             {
  1535.                 // Careful: use second sample, since first sample is duplicate of last
  1536.                 s3 = trajectory[1];
  1537.                 s3.t += orbit.getPeriod();
  1538.             }
  1539.             else
  1540.             {
  1541.                 s3 = trajectory[i];
  1542.             }
  1543.             s1 = Renderer::OrbitSample(lastP, trajectory[i - 1].t);
  1544.             s2 = Renderer::OrbitSample(p,     trajectory[i].t);
  1545.             s0.pos = s0.pos * modelview;
  1546.             s3.pos = s3.pos * modelview;
  1547.             bool drawLastSegment = i == nPoints - 1;
  1548.             p = renderOrbitSplineSegment(s0, s1, s2, s3,
  1549.                                          nearZ, farZ,
  1550.                                          splineSubdivisions,
  1551.                                          lastOutcode,
  1552.                                          drawLastSegment);
  1553.             outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
  1554.         }
  1555.         else
  1556.         {
  1557.             // Just draw a line segment
  1558.             // Compute the outcode mask for clipping:
  1559.             //   00 = between near and far
  1560.             //   01 = greater than nearZ (nearZ and farZ are always negative)
  1561.             //   10 = less than farZ
  1562.             // Given two outcodes o1 and o2 of line segment endpoints:
  1563.             //   o1 | o2 == 0 means segment lies completely between near and far plans
  1564.             //   o2 & o2 != 0 means segment lies completely outside planes
  1565.             //   else we have to clip the line.
  1566.             outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
  1567.             if ((outcode | lastOutcode) == 0)
  1568.             {
  1569.                 // Segment is completely between near and far planes
  1570.                 glVertex3d(p.x, p.y, p.z);
  1571.             }
  1572.             else if ((outcode & lastOutcode) == 0)
  1573.             {
  1574.                 // Need to clip
  1575.                 Point3d q0 = lastP;
  1576.                 Point3d q1 = p;
  1577.                 // Clip against the enter plane
  1578.                 if (lastOutcode != 0)
  1579.                 {
  1580.                     glBegin(GL_LINE_STRIP);
  1581.                     double t;
  1582.                     if (lastOutcode == 1)
  1583.                         t = (nearZ - lastP.z) / (p.z - lastP.z);
  1584.                     else
  1585.                         t = (farZ - lastP.z) / (p.z - lastP.z);
  1586.                     q0 = lastP + t * (p - lastP);
  1587.                 }
  1588.                 // Clip against the exit plane
  1589.                 if (outcode != 0)
  1590.                 {
  1591.                     double t;
  1592.                     if (outcode == 1)
  1593.                         t = (nearZ - lastP.z) / (p.z - lastP.z);
  1594.                     else
  1595.                         t = (farZ - lastP.z) / (p.z - lastP.z);
  1596.                     q1 = lastP + t * (p - lastP);
  1597.                 }
  1598.                 glVertex3d(q0.x, q0.y, q0.z);
  1599.                 glVertex3d(q1.x, q1.y, q1.z);
  1600.                 if (outcode != 0)
  1601.                 {
  1602.                     glEnd();
  1603.                 }
  1604.             }
  1605.         }
  1606.         lastOutcode = outcode;
  1607.         lastP = p;
  1608.     }
  1609.     return lastP;
  1610. }
  1611. void Renderer::renderOrbit(const OrbitPathListEntry& orbitPath,
  1612.                            double t,
  1613.                            const Quatf& cameraOrientationf,
  1614.                            const Frustum& frustum,
  1615.                            float nearDist,
  1616.                            float farDist)
  1617. {
  1618.     Body* body = orbitPath.body;
  1619.     Quatd cameraOrientation(cameraOrientationf.w, cameraOrientationf.x, cameraOrientationf.y, cameraOrientationf.z);
  1620.     double nearZ = -nearDist;  // negate, becase z is into the screen in camera space
  1621.     double farZ = -farDist;
  1622.     const Orbit* orbit = NULL;
  1623.     if (body != NULL)
  1624.         orbit = body->getOrbit(t);
  1625.     else
  1626.         orbit = orbitPath.star->getOrbit();
  1627.     CachedOrbit* cachedOrbit = NULL;
  1628.     OrbitCache::iterator cached = orbitCache.find(orbit);
  1629.     if (cached != orbitCache.end())
  1630.     {
  1631.         cachedOrbit = cached->second;
  1632.         cachedOrbit->lastUsed = frameCount;
  1633.     }
  1634.     // If it's not in the cache already
  1635.     if (cachedOrbit == NULL)
  1636.     {
  1637.         double startTime = t;
  1638.         int nSamples = detailOptions.orbitPathSamplePoints;
  1639.         // Adjust the number of samples used for aperiodic orbits--these aren't
  1640.         // true orbits, but are sampled trajectories, generally of spacecraft.
  1641.         // Better control is really needed--some sort of adaptive sampling would
  1642.         // be ideal.
  1643.         if (!orbit->isPeriodic())
  1644.         {
  1645.             double begin = 0.0, end = 0.0;
  1646.             orbit->getValidRange(begin, end);
  1647.             if (begin != end)
  1648.             {
  1649.                 startTime = begin;
  1650.                 nSamples = (int) (orbit->getPeriod() * 100.0);
  1651.                 nSamples = max(min(nSamples, 1000), 100);
  1652.             }
  1653.             else
  1654.             {
  1655.                 // If the orbit is aperiodic and doesn't have a
  1656.                 // finite duration, we don't render it. A compromise
  1657.                 // would be to pick some time window centered at the
  1658.                 // current time, but we'd have to pick some arbitrary
  1659.                 // duration.
  1660.                 nSamples = 0;
  1661.             }
  1662.         }
  1663.         cachedOrbit = new CachedOrbit();
  1664.         cachedOrbit->lastUsed = frameCount;
  1665.         OrbitSampler sampler(&cachedOrbit->trajectory);
  1666.         orbit->sample(startTime,
  1667.                       orbit->getPeriod(),
  1668.                       nSamples,
  1669.                       sampler);
  1670.         // Add an extra sample to close a periodic orbit
  1671.         if (orbit->isPeriodic())
  1672.         {
  1673.             if (!cachedOrbit->trajectory.empty())
  1674.             {
  1675.                 double lastSampleTime = cachedOrbit->trajectory[0].t + orbit->getPeriod();
  1676.                 cachedOrbit->trajectory.push_back(OrbitSample(cachedOrbit->trajectory[0].pos, lastSampleTime));
  1677.             }
  1678.         }
  1679.         computeOrbitSectionBoundingVolumes(*cachedOrbit);
  1680.         // If the orbit cache is full, first try and eliminate some old orbits
  1681.         if (orbitCache.size() > OrbitCacheCullThreshold)
  1682.         {
  1683.             // Check for old orbits at most once per frame
  1684.             if (lastOrbitCacheFlush != frameCount)
  1685.             {
  1686.                 for (OrbitCache::iterator iter = orbitCache.begin(); iter != orbitCache.end();)
  1687.                 {
  1688.                     // Tricky code to eliminate a node in the orbit cache without screwing
  1689.                     // up the iterator. Should work in all STL implementations.
  1690.                     if (frameCount - iter->second->lastUsed > OrbitCacheRetireAge)
  1691.                         orbitCache.erase(iter++);
  1692.                     else
  1693.                         ++iter;
  1694.                 }
  1695.                 lastOrbitCacheFlush = frameCount;
  1696.             }
  1697.         }
  1698.         orbitCache[orbit] = cachedOrbit;
  1699.     }
  1700.     vector<OrbitSample>* trajectory = &cachedOrbit->trajectory;
  1701.     // The rest of the function isn't designed for empty trajectories
  1702.     if (trajectory->empty())
  1703.         return;
  1704.     // We perform vertex tranformations on the CPU because double precision is necessary to
  1705.     // render orbits properly. Start by computing the modelview matrix, to transform orbit
  1706.     // vertices into camera space.
  1707.     Mat4d modelview;
  1708.     {
  1709.         Quatd orientation(1.0);
  1710.         if (body != NULL)
  1711.         {
  1712.             orientation = body->getOrbitFrame(t)->getOrientation(t);
  1713.         }
  1714.         // Equivalent to:
  1715.         // glRotate(cameraOrientation);
  1716.         // glTranslate(orbitPath.origin);
  1717.         // glRotate(~orientation);
  1718.         modelview =
  1719.             (orientation).toMatrix4() *
  1720.             Mat4d::translation(Point3d(orbitPath.origin.x, orbitPath.origin.y, orbitPath.origin.z)) *
  1721.             (~cameraOrientation).toMatrix4();
  1722.     }
  1723.     glPushMatrix();
  1724.     glLoadIdentity();
  1725.     bool highlight;
  1726.     if (body != NULL)
  1727.         highlight = highlightObject.body() == body;
  1728.     else
  1729.         highlight = highlightObject.star() == orbitPath.star;
  1730.     renderOrbitColor(body, highlight, orbitPath.opacity);
  1731.     if ((renderFlags & ShowPartialTrajectories) == 0 || orbit->isPeriodic())
  1732.     {
  1733.         // Show the complete trajectory
  1734.         Point3d p;
  1735.         p = (*trajectory)[0].pos * modelview;
  1736.         int outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
  1737.         if (outcode == 0)
  1738.         {
  1739.             glBegin(GL_LINE_STRIP);
  1740.             glVertex3d(p.x, p.y, p.z);
  1741.         }
  1742.         Point3d lastP = p;
  1743.         int lastOutcode = outcode;
  1744.         // The trajectory is subdivided into sections that each contain a number of samples.
  1745.         // Process each section in the trajectory, using its precomputed bounding volume to
  1746.         // quickly check for visibility.
  1747.         for (unsigned int i = 0; i < cachedOrbit->sections.size(); i++)
  1748.         {
  1749.             Capsuled& bv = cachedOrbit->sections[i].boundingVolume;
  1750.             Point3d orig = bv.origin * modelview;
  1751.             Vec3d axis   = bv.axis   * modelview;
  1752.             Capsulef bvf(Point3f((float) orig.x, (float) orig.y, (float) orig.z),
  1753.                          Vec3f((float) axis.x, (float) axis.y, (float) axis.z),
  1754.                          (float) bv.radius);
  1755.             // TODO: Create a fast path for the case when the bounding volume lies completely
  1756.             // within the frustum and clipping can be ignored.
  1757.             if (frustum.testCapsule(bvf) != Frustum::Outside)
  1758.             {
  1759.                 lastP = renderOrbitSection(*orbit,
  1760.                                            *cachedOrbit, i,
  1761.                                            modelview,
  1762.                                            lastP, lastOutcode,
  1763.                                            nearZ, farZ,
  1764.                                            renderFlags);
  1765.                 lastOutcode = (lastP.z > nearZ ? 1 : 0) | (lastP.z < farZ ? 2 : 0);
  1766.             }
  1767.             else
  1768.             {
  1769.                 // The section was culled because it lies completely outside the view frustum,
  1770.                 // but we still need to do some work to keep the begin/end state of the line
  1771.                 // strip current. We just need to process the final point in the section.
  1772.                 unsigned int lastSample;
  1773.                 if (i < cachedOrbit->sections.size() - 1)
  1774.                     lastSample = cachedOrbit->sections[i + 1].firstSample;
  1775.                 else
  1776.                     lastSample = cachedOrbit->trajectory.size() - 1;
  1777.                 p = (*trajectory)[lastSample].pos * modelview;
  1778.                 outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
  1779.                 if ((outcode | lastOutcode) == 0)
  1780.                 {
  1781.                     // Segment is completely between near and far planes
  1782.                     glVertex3d(p.x, p.y, p.z);
  1783.                 }
  1784.                 else if ((outcode & lastOutcode) == 0)
  1785.                 {
  1786.                     // Need to clip
  1787.                     Point3d q0 = lastP;
  1788.                     Point3d q1 = p;
  1789.                     // Clip against the enter plane
  1790.                     if (lastOutcode != 0)
  1791.                     {
  1792.                         glBegin(GL_LINE_STRIP);
  1793.                         double t;
  1794.                         if (lastOutcode == 1)
  1795.                             t = (nearZ - lastP.z) / (p.z - lastP.z);
  1796.                         else
  1797.                             t = (farZ - lastP.z) / (p.z - lastP.z);
  1798.                         q0 = lastP + t * (p - lastP);
  1799.                     }
  1800.                     // Clip against the exit plane
  1801.                     if (outcode != 0)
  1802.                     {
  1803.                         double t;
  1804.                         if (outcode == 1)
  1805.                             t = (nearZ - lastP.z) / (p.z - lastP.z);
  1806.                         else
  1807.                             t = (farZ - lastP.z) / (p.z - lastP.z);
  1808.                         q1 = lastP + t * (p - lastP);
  1809.                     }
  1810.                     glVertex3d(q0.x, q0.y, q0.z);
  1811.                     glVertex3d(q1.x, q1.y, q1.z);
  1812.                     if (outcode != 0)
  1813.                     {
  1814.                         glEnd();
  1815.                     }
  1816.                 }
  1817.                 lastP = p;
  1818.                 lastOutcode = outcode;
  1819.                 sectionsCulled++;
  1820.             }
  1821.         }
  1822.         if (lastOutcode == 0)
  1823.         {
  1824.             glEnd();
  1825.         }
  1826.     }
  1827.     else
  1828.     {
  1829.         double endTime = t;
  1830.         bool endTimeReached = false;
  1831.         Point3d p;
  1832.         p = (*trajectory)[0].pos * modelview;
  1833.         int outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
  1834.         if (outcode == 0)
  1835.         {
  1836.             glBegin(GL_LINE_STRIP);
  1837.             glVertex3d(p.x, p.y, p.z);
  1838.         }
  1839.         Point3d lastP = p;
  1840.         int lastOutcode = outcode;
  1841.         unsigned int nPoints = trajectory->size();
  1842.         for (unsigned int i = 1; i < nPoints && !endTimeReached; i++)
  1843.         {
  1844.             if ((*trajectory)[i].t > endTime)
  1845.             {
  1846.                 p = orbit->positionAtTime(endTime) * modelview;
  1847.                 endTimeReached = true;
  1848.             }
  1849.             else
  1850.             {
  1851.                 p = (*trajectory)[i].pos * modelview;
  1852.             }
  1853.             outcode = (p.z > nearZ ? 1 : 0) | (p.z < farZ ? 2 : 0);
  1854.             if ((outcode | lastOutcode) == 0)
  1855.             {
  1856.                 glVertex3d(p.x, p.y, p.z);
  1857.             }
  1858.             else if ((outcode & lastOutcode) == 0)
  1859.             {
  1860.                 // Need to clip
  1861.                 Point3d p0 = lastP;
  1862.                 Point3d p1 = p;
  1863.                 if (lastOutcode != 0)
  1864.                 {
  1865.                     glBegin(GL_LINE_STRIP);
  1866.                     double t;
  1867.                     if (lastOutcode == 1)
  1868.                         t = (nearZ - lastP.z) / (p.z - lastP.z);
  1869.                     else
  1870.                         t = (farZ - lastP.z) / (p.z - lastP.z);
  1871.                     p0 = lastP + t * (p - lastP);
  1872.                 }
  1873.                 if (outcode != 0)
  1874.                 {
  1875.                     double t;
  1876.                     if (outcode == 1)
  1877.                         t = (nearZ - lastP.z) / (p.z - lastP.z);
  1878.                     else
  1879.                         t = (farZ - lastP.z) / (p.z - lastP.z);
  1880.                     p1 = lastP + t * (p - lastP);
  1881.                 }
  1882.                 glVertex3d(p0.x, p0.y, p0.z);
  1883.                 glVertex3d(p1.x, p1.y, p1.z);
  1884.                 if (outcode != 0)
  1885.                 {
  1886.                     glEnd();
  1887.                 }
  1888.             }
  1889.             lastOutcode = outcode;
  1890.             lastP = p;
  1891.         }
  1892.         if (lastOutcode == 0)
  1893.         {
  1894.             glEnd();
  1895.         }
  1896.     }
  1897.     glPopMatrix();
  1898. }
  1899. // Convert a position in the universal coordinate system to astrocentric
  1900. // coordinates, taking into account possible orbital motion of the star.
  1901. static Point3d astrocentricPosition(const UniversalCoord& pos,
  1902.                                     const Star& star,
  1903.                                     double t)
  1904. {
  1905.     UniversalCoord starPos = star.getPosition(t);
  1906.     Vec3d v = pos - starPos;
  1907.     return Point3d(astro::microLightYearsToKilometers(v.x),
  1908.                    astro::microLightYearsToKilometers(v.y),
  1909.                    astro::microLightYearsToKilometers(v.z));
  1910. }
  1911. void Renderer::autoMag(float& faintestMag)
  1912. {
  1913.       float fieldCorr = 2.0f * FOV/(fov + FOV);
  1914.       faintestMag = (float) (faintestAutoMag45deg * sqrt(fieldCorr));
  1915.       saturationMag = saturationMagNight * (1.0f + fieldCorr * fieldCorr);
  1916. }
  1917. // Set up the light sources for rendering a solar system.  The positions of
  1918. // all nearby stars are converted from universal to viewer-centered
  1919. // coordinates.
  1920. static void
  1921. setupLightSources(const vector<const Star*>& nearStars,
  1922.                   const UniversalCoord& observerPos,
  1923.                   double t,
  1924.                   vector<LightSource>& lightSources)
  1925. {
  1926.     lightSources.clear();
  1927.     for (vector<const Star*>::const_iterator iter = nearStars.begin();
  1928.          iter != nearStars.end(); iter++)
  1929.     {
  1930.         if ((*iter)->getVisibility())
  1931.         {
  1932.             Vec3d v = ((*iter)->getPosition(t) - observerPos) *
  1933.                 astro::microLightYearsToKilometers(1.0);
  1934.             LightSource ls;
  1935.             ls.position = v;
  1936.             ls.luminosity = (*iter)->getLuminosity();
  1937.             ls.radius = (*iter)->getRadius();
  1938.             // If the star is sufficiently cool, change the light color
  1939.             // from white.  Though our sun appears yellow, we still make
  1940.             // it and all hotter stars emit white light, as this is the
  1941.             // 'natural' light to which our eyes are accustomed.  We also
  1942.             // assign a slight bluish tint to light from O and B type stars,
  1943.             // though these will almost never have planets for their light
  1944.             // to shine upon.
  1945.             float temp = (*iter)->getTemperature();
  1946.             if (temp > 30000.0f)
  1947.                 ls.color = Color(0.8f, 0.8f, 1.0f);
  1948.             else if (temp > 10000.0f)
  1949.                 ls.color = Color(0.9f, 0.9f, 1.0f);
  1950.             else if (temp > 5400.0f)
  1951.                 ls.color = Color(1.0f, 1.0f, 1.0f);
  1952.             else if (temp > 3900.0f)
  1953.                 ls.color = Color(1.0f, 0.9f, 0.8f);
  1954.             else if (temp > 2000.0f)
  1955.                 ls.color = Color(1.0f, 0.7f, 0.7f);
  1956.             else
  1957.                 ls.color = Color(1.0f, 0.4f, 0.4f);
  1958.             lightSources.push_back(ls);
  1959.         }
  1960.     }
  1961. }
  1962. // Set up the potential secondary light sources for rendering solar system
  1963. // bodies.
  1964. static void
  1965. setupSecondaryLightSources(vector<SecondaryIlluminator>& secondaryIlluminators,
  1966.                            const vector<LightSource>& primaryIlluminators)
  1967. {
  1968.     float au2 = square(astro::kilometersToAU(1.0f));
  1969.     for (vector<SecondaryIlluminator>::iterator i = secondaryIlluminators.begin();
  1970.          i != secondaryIlluminators.end(); i++)
  1971.     {
  1972.         i->reflectedIrradiance = 0.0f;
  1973.         for (vector<LightSource>::const_iterator j = primaryIlluminators.begin();
  1974.              j != primaryIlluminators.end(); j++)
  1975.         {
  1976.             i->reflectedIrradiance += j->luminosity / ((float) (i->position_v - j->position).lengthSquared() * au2);
  1977.         }
  1978.         i->reflectedIrradiance *= i->body->getAlbedo();
  1979.     }
  1980. }
  1981. // Render an item from the render list
  1982. void Renderer::renderItem(const RenderListEntry& rle,
  1983.                           const Observer& observer,
  1984.                           const Quatf& cameraOrientation,
  1985.                           float nearPlaneDistance,
  1986.                           float farPlaneDistance)
  1987. {
  1988.     switch (rle.renderableType)
  1989.     {
  1990.     case RenderListEntry::RenderableStar:
  1991.         renderStar(*rle.star,
  1992.                    rle.position,
  1993.                    rle.distance,
  1994.                    rle.appMag,
  1995.                    cameraOrientation,
  1996.                    observer.getTime(),
  1997.                    nearPlaneDistance, farPlaneDistance);
  1998.         break;
  1999.     case RenderListEntry::RenderableBody:
  2000.         renderPlanet(*rle.body,
  2001.                      rle.position,
  2002.                      rle.distance,
  2003.                      rle.appMag,
  2004.                      observer,
  2005.                      cameraOrientation,
  2006.                      nearPlaneDistance, farPlaneDistance);
  2007.         break;
  2008.     case RenderListEntry::RenderableCometTail:
  2009.         renderCometTail(*rle.body,
  2010.                         rle.position,
  2011.                         observer.getTime(),
  2012.                         rle.discSizeInPixels);
  2013.         break;
  2014.     case RenderListEntry::RenderableReferenceMark:
  2015.         renderReferenceMark(*rle.refMark,
  2016.                             rle.position,
  2017.                             rle.distance,
  2018.                             observer.getTime(),
  2019.                             nearPlaneDistance);
  2020.         break;
  2021.     default:
  2022.         break;
  2023.     }
  2024. }
  2025. #ifdef USE_HDR
  2026. void Renderer::genBlurTextures()
  2027. {
  2028.     for (size_t i = 0; i < BLUR_PASS_COUNT; ++i)
  2029.     {
  2030.         if (blurTextures[i] != NULL)
  2031.         {
  2032.             delete blurTextures[i];
  2033.             blurTextures[i] = NULL;
  2034.         }
  2035.     }
  2036.     if (blurTempTexture)
  2037.     {
  2038.         delete blurTempTexture;
  2039.         blurTempTexture = NULL;
  2040.     }
  2041.     blurBaseWidth = sceneTexWidth, blurBaseHeight = sceneTexHeight;
  2042.     if (blurBaseWidth > blurBaseHeight)
  2043.     {
  2044.         while (blurBaseWidth > BLUR_SIZE)
  2045.         {
  2046.             blurBaseWidth  >>= 1;
  2047.             blurBaseHeight >>= 1;
  2048.         }
  2049.     }
  2050.     else
  2051.     {
  2052.         while (blurBaseHeight > BLUR_SIZE)
  2053.         {
  2054.             blurBaseWidth  >>= 1;
  2055.             blurBaseHeight >>= 1;
  2056.         }
  2057.     }
  2058.     genBlurTexture(0);
  2059.     genBlurTexture(1);
  2060.     Image *tempImg;
  2061.     ImageTexture *tempTexture;
  2062.     tempImg = new Image(GL_LUMINANCE, blurBaseWidth, blurBaseHeight);
  2063.     tempTexture = new ImageTexture(*tempImg, Texture::EdgeClamp, Texture::DefaultMipMaps);
  2064.     delete tempImg;
  2065.     if (tempTexture && tempTexture->getName() != 0)
  2066.         blurTempTexture = tempTexture;
  2067. }
  2068. void Renderer::genBlurTexture(int blurLevel)
  2069. {
  2070.     Image *img;
  2071.     ImageTexture *texture;
  2072. #ifdef DEBUG_HDR
  2073.     HDR_LOG <<
  2074.         "Window width = "    << windowWidth << ", " <<
  2075.         "Window height = "   << windowHeight << ", " <<
  2076.         "Blur tex width = "  << (blurBaseWidth>>blurLevel) << ", " <<
  2077.         "Blur tex height = " << (blurBaseHeight>>blurLevel) << endl;
  2078. #endif
  2079.     img = new Image(blurFormat,
  2080.                     blurBaseWidth>>blurLevel,
  2081.                     blurBaseHeight>>blurLevel);
  2082.     texture = new ImageTexture(*img,
  2083.                                Texture::EdgeClamp,
  2084.                                Texture::NoMipMaps);
  2085.     delete img;
  2086.     if (texture && texture->getName() != 0)
  2087.         blurTextures[blurLevel] = texture;
  2088. }
  2089. void Renderer::genSceneTexture()
  2090. {
  2091. unsigned int *data;
  2092.     if (sceneTexture != 0)
  2093.         glDeleteTextures(1, &sceneTexture);
  2094.     sceneTexWidth  = 1;
  2095.     sceneTexHeight = 1;
  2096.     while (sceneTexWidth < windowWidth)
  2097.         sceneTexWidth <<= 1;
  2098.     while (sceneTexHeight < windowHeight)
  2099.         sceneTexHeight <<= 1;
  2100.     sceneTexWScale = (windowWidth > 0)  ? (GLfloat)sceneTexWidth  / (GLfloat)windowWidth :
  2101.         1.0f;
  2102.     sceneTexHScale = (windowHeight > 0) ? (GLfloat)sceneTexHeight / (GLfloat)windowHeight :
  2103.         1.0f;
  2104. data = (unsigned int* )malloc(sceneTexWidth*sceneTexHeight*4*sizeof(unsigned int));
  2105.     memset(data, 0, sceneTexWidth*sceneTexHeight*4*sizeof(unsigned int));
  2106.     
  2107.     glGenTextures(1, &sceneTexture);
  2108. glBindTexture(GL_TEXTURE_2D, sceneTexture);
  2109.     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
  2110.     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
  2111.     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
  2112.     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
  2113. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, sceneTexWidth, sceneTexHeight, 0,
  2114.                  GL_RGBA, GL_UNSIGNED_BYTE, data);
  2115.     
  2116. free(data);
  2117. #ifdef DEBUG_HDR
  2118.     static int genSceneTexCounter = 1;
  2119.     HDR_LOG <<
  2120.         "[" << genSceneTexCounter++ << "] " <<
  2121.         "Window width = "  << windowWidth << ", " <<
  2122.         "Window height = " << windowHeight << ", " <<
  2123.         "Tex width = "  << sceneTexWidth << ", " <<
  2124.         "Tex height = " << sceneTexHeight << endl;
  2125. #endif
  2126. }
  2127. void Renderer::renderToBlurTexture(int blurLevel)
  2128. {
  2129.     if (blurTextures[blurLevel] == NULL)
  2130.         return;
  2131.     GLsizei blurTexWidth  = blurBaseWidth>>blurLevel;
  2132.     GLsizei blurTexHeight = blurBaseHeight>>blurLevel;
  2133.     GLsizei blurDrawWidth = (GLfloat)windowWidth/(GLfloat)sceneTexWidth * blurTexWidth;
  2134.     GLsizei blurDrawHeight = (GLfloat)windowHeight/(GLfloat)sceneTexHeight * blurTexHeight;
  2135.     GLfloat blurWScale = 1.f;
  2136.     GLfloat blurHScale = 1.f;
  2137.     GLfloat savedWScale = 1.f;
  2138.     GLfloat savedHScale = 1.f;
  2139.     glPushAttrib(GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
  2140.     glClearColor(0, 0, 0, 1.f);
  2141.     glViewport(0, 0, blurDrawWidth, blurDrawHeight);
  2142.     glBindTexture(GL_TEXTURE_2D, sceneTexture);
  2143.     if (useBlendSubtract)
  2144.     {
  2145.         glBegin(GL_QUADS);
  2146.         drawBlendedVertices(0.0f, 0.0f, 1.0f);
  2147.         glEnd();
  2148.         // Do not need to scale alpha so mask it off
  2149.         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
  2150.         glEnable(GL_BLEND);
  2151.         savedWScale = sceneTexWScale;
  2152.         savedHScale = sceneTexHScale;
  2153.         // Remove ldr part of image
  2154.         {
  2155.             const GLfloat bias  = -0.5f;
  2156.             glBlendFunc(GL_ONE, GL_ONE);
  2157.             glx::glBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
  2158.             glColor4f(-bias, -bias, -bias, 0.0f);
  2159.             glDisable(GL_TEXTURE_2D);
  2160.             glBegin(GL_QUADS);
  2161.             glVertex2f(0.0f,           0.0f);
  2162.             glVertex2f(1.f, 0.0f);
  2163.             glVertex2f(1.f, 1.f);
  2164.             glVertex2f(0.0f,           1.f);
  2165.             glEnd();
  2166.             glEnable(GL_TEXTURE_2D);
  2167.             blurTextures[blurLevel]->bind();
  2168.             glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
  2169.                              blurTexWidth, blurTexHeight, 0);
  2170.         }
  2171.         // Scale back up hdr part
  2172.         {
  2173.             glx::glBlendEquationEXT(GL_FUNC_ADD_EXT);
  2174.             glBlendFunc(GL_DST_COLOR, GL_ONE);
  2175.             glBegin(GL_QUADS);
  2176.             drawBlendedVertices(0.f, 0.f, 1.f); //x2
  2177.             drawBlendedVertices(0.f, 0.f, 1.f); //x2
  2178.             glEnd();
  2179.         }
  2180.         glDisable(GL_BLEND);
  2181.         if (!useLuminanceAlpha)
  2182.         {
  2183.             blurTempTexture->bind();
  2184.             glCopyTexImage2D(GL_TEXTURE_2D, blurLevel, GL_LUMINANCE, 0, 0,
  2185.                              blurTexWidth, blurTexHeight, 0);
  2186.             // Erase color, replace with luminance image
  2187.             glBegin(GL_QUADS);
  2188.             glColor4f(0.f, 0.f, 0.f, 1.f);
  2189.             glVertex2f(0.0f, 0.0f);
  2190.             glVertex2f(1.0f, 0.0f);
  2191.             glVertex2f(1.0f, 1.0f);
  2192.             glVertex2f(0.0f, 1.0f);
  2193.             glEnd();
  2194.             glBegin(GL_QUADS);
  2195.             drawBlendedVertices(0.f, 0.f, 1.f);
  2196.             glEnd();
  2197.         }
  2198.         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  2199.         blurTextures[blurLevel]->bind();
  2200.         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
  2201.                          blurTexWidth, blurTexHeight, 0);
  2202.     }
  2203.     else
  2204.     {
  2205.         // GL_EXT_blend_subtract not supported
  2206.         // Use compatible (but slow) glPixelTransfer instead
  2207.         glBegin(GL_QUADS);
  2208.         drawBlendedVertices(0.0f, 0.0f, 1.0f);
  2209.         glEnd();
  2210.         savedWScale = sceneTexWScale;
  2211.         savedHScale = sceneTexHScale;
  2212.         sceneTexWScale = blurWScale;
  2213.         sceneTexHScale = blurHScale;
  2214.         blurTextures[blurLevel]->bind();
  2215.         glPixelTransferf(GL_RED_SCALE,   8.f);
  2216.         glPixelTransferf(GL_GREEN_SCALE, 8.f);
  2217.         glPixelTransferf(GL_BLUE_SCALE,  8.f);
  2218.         glPixelTransferf(GL_RED_BIAS,   -0.5f);
  2219.         glPixelTransferf(GL_GREEN_BIAS, -0.5f);
  2220.         glPixelTransferf(GL_BLUE_BIAS,  -0.5f);
  2221.         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
  2222.                          blurTexWidth, blurTexHeight, 0);
  2223.         glPixelTransferf(GL_RED_SCALE,   1.f);
  2224.         glPixelTransferf(GL_GREEN_SCALE, 1.f);
  2225.         glPixelTransferf(GL_BLUE_SCALE,  1.f);
  2226.         glPixelTransferf(GL_RED_BIAS,    0.f);
  2227.         glPixelTransferf(GL_GREEN_BIAS,  0.f);
  2228.         glPixelTransferf(GL_BLUE_BIAS,   0.f);
  2229.     }
  2230.     glClear(GL_COLOR_BUFFER_BIT);
  2231.     glEnable(GL_BLEND);
  2232.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  2233.     GLfloat xdelta = 1.0f / (GLfloat)blurTexWidth;
  2234.     GLfloat ydelta = 1.0f / (GLfloat)blurTexHeight;
  2235.     blurWScale = ((GLfloat)blurTexWidth / (GLfloat)blurDrawWidth);
  2236.     blurHScale = ((GLfloat)blurTexHeight / (GLfloat)blurDrawHeight);
  2237.     sceneTexWScale = blurWScale;
  2238.     sceneTexHScale = blurHScale;
  2239.     // Butterworth low pass filter to reduce flickering dots
  2240.     {
  2241.         glBegin(GL_QUADS);
  2242.         drawBlendedVertices(0.0f,    0.0f, .5f*1.f);
  2243.         drawBlendedVertices(-xdelta, 0.0f, .5f*0.333f);
  2244.         drawBlendedVertices( xdelta, 0.0f, .5f*0.25f);
  2245.         glEnd();
  2246.         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
  2247.                          blurTexWidth, blurTexHeight, 0);
  2248.         glBegin(GL_QUADS);
  2249.         drawBlendedVertices(0.0f, -ydelta, .5f*0.667f);
  2250.         drawBlendedVertices(0.0f,  ydelta, .5f*0.333f);
  2251.         glEnd();
  2252.         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
  2253.                          blurTexWidth, blurTexHeight, 0);
  2254.         glClear(GL_COLOR_BUFFER_BIT);
  2255.     }
  2256.     // Gaussian blur
  2257.     switch (blurLevel)
  2258.     {
  2259. /*
  2260.     case 0:
  2261.         drawGaussian3x3(xdelta, ydelta, blurTexWidth, blurTexHeight, 1.f);
  2262.         break;
  2263. */
  2264. #ifdef TARGET_OS_MAC
  2265.     case 0:
  2266.         drawGaussian5x5(xdelta, ydelta, blurTexWidth, blurTexHeight, 1.f);
  2267.         break;
  2268.     case 1:
  2269.         drawGaussian9x9(xdelta, ydelta, blurTexWidth, blurTexHeight, .3f);
  2270.         break;
  2271. #else
  2272.     // Gamma correct: windows=(mac^1.8)^(1/2.2)
  2273.     case 0:
  2274.         drawGaussian5x5(xdelta, ydelta, blurTexWidth, blurTexHeight, 1.f);
  2275.         break;
  2276.     case 1:
  2277.         drawGaussian9x9(xdelta, ydelta, blurTexWidth, blurTexHeight, .373f);
  2278.         break;
  2279. #endif
  2280.     default:
  2281.         break;
  2282.     }
  2283.     blurTextures[blurLevel]->bind();
  2284.     glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
  2285.                      blurTexWidth, blurTexHeight, 0);
  2286.     glDisable(GL_BLEND);
  2287.     glClear(GL_COLOR_BUFFER_BIT);
  2288.     glPopAttrib();
  2289.     sceneTexWScale = savedWScale;
  2290.     sceneTexHScale = savedHScale;
  2291. }
  2292. void Renderer::renderToTexture(const Observer& observer,
  2293.                                const Universe& universe,
  2294.                                float faintestMagNight,
  2295.                                const Selection& sel)
  2296. {
  2297.     if (sceneTexture == 0)
  2298.         return;
  2299.     glPushAttrib(GL_COLOR_BUFFER_BIT);
  2300.     draw(observer, universe, faintestMagNight, sel);
  2301.     glBindTexture(GL_TEXTURE_2D, sceneTexture);
  2302.     glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0,
  2303.                      sceneTexWidth, sceneTexHeight, 0);
  2304.     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  2305.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  2306.     glPopAttrib();
  2307. }
  2308. void Renderer::drawSceneTexture()
  2309. {
  2310.     if (sceneTexture == 0)
  2311.         return;
  2312.     glBindTexture(GL_TEXTURE_2D, sceneTexture);
  2313.     glBegin(GL_QUADS);
  2314.     drawBlendedVertices(0.0f, 0.0f, 1.0f);
  2315.     glEnd();
  2316. }
  2317. void Renderer::drawBlendedVertices(float xdelta, float ydelta, float blend)
  2318. {
  2319.     glColor4f(1.0f, 1.0f, 1.0f, blend);
  2320.     glTexCoord2i(0, 0); glVertex2f(xdelta,                ydelta);
  2321.     glTexCoord2i(1, 0); glVertex2f(sceneTexWScale+xdelta, ydelta);
  2322.     glTexCoord2i(1, 1); glVertex2f(sceneTexWScale+xdelta, sceneTexHScale+ydelta);
  2323.     glTexCoord2i(0, 1); glVertex2f(xdelta,                sceneTexHScale+ydelta);
  2324. }
  2325. void Renderer::drawGaussian3x3(float xdelta, float ydelta, GLsizei width, GLsizei height, float blend)
  2326. {
  2327. #ifdef USE_BLOOM_LISTS
  2328.     if (gaussianLists[0] == 0)
  2329.     {
  2330.         gaussianLists[0] = glGenLists(1);
  2331.         glNewList(gaussianLists[0], GL_COMPILE);
  2332. #endif
  2333.         glBegin(GL_QUADS);
  2334.         drawBlendedVertices(0.0f, 0.0f, blend);
  2335.         drawBlendedVertices(-xdelta, 0.0f, 0.25f*blend);
  2336.         drawBlendedVertices( xdelta, 0.0f, 0.20f*blend);
  2337.         glEnd();
  2338.         // Take result of horiz pass and apply vertical pass
  2339.         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
  2340.                          width, height, 0);
  2341.         glBegin(GL_QUADS);
  2342.         drawBlendedVertices(0.0f, -ydelta, 0.429f);
  2343.         drawBlendedVertices(0.0f,  ydelta, 0.300f);
  2344.         glEnd();
  2345. #ifdef USE_BLOOM_LISTS
  2346.         glEndList();
  2347.     }
  2348.     glCallList(gaussianLists[0]);
  2349. #endif
  2350. }
  2351. void Renderer::drawGaussian5x5(float xdelta, float ydelta, GLsizei width, GLsizei height, float blend)
  2352. {
  2353. #ifdef USE_BLOOM_LISTS
  2354.     if (gaussianLists[1] == 0)
  2355.     {
  2356.         gaussianLists[1] = glGenLists(1);
  2357.         glNewList(gaussianLists[1], GL_COMPILE);
  2358. #endif
  2359.         glBegin(GL_QUADS);
  2360.         drawBlendedVertices(0.0f, 0.0f, blend);
  2361.         drawBlendedVertices(-xdelta,      0.0f, 0.475f*blend);
  2362.         drawBlendedVertices( xdelta,      0.0f, 0.475f*blend);
  2363.         drawBlendedVertices(-2.0f*xdelta, 0.0f, 0.075f*blend);
  2364.         drawBlendedVertices( 2.0f*xdelta, 0.0f, 0.075f*blend);
  2365.         glEnd();
  2366.         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,
  2367.                          width, height, 0);
  2368.         glBegin(GL_QUADS);
  2369.         drawBlendedVertices(0.0f, -ydelta,      0.475f);
  2370.         drawBlendedVertices(0.0f,  ydelta,      0.475f);
  2371.         drawBlendedVertices(0.0f, -2.0f*ydelta, 0.075f);
  2372.         drawBlendedVertices(0.0f,  2.0f*ydelta, 0.075f);
  2373.         glEnd();
  2374. #ifdef USE_BLOOM_LISTS
  2375.         glEndList();
  2376.     }
  2377.     glCallList(gaussianLists[1]);
  2378. #endif
  2379. }
  2380. void Renderer::drawGaussian9x9(float xdelta, float ydelta, GLsizei width, GLsizei height, float blend)
  2381. {
  2382. #ifdef USE_BLOOM_LISTS
  2383.     if (gaussianLists[2] == 0)
  2384.     {
  2385.         gaussianLists[2] = glGenLists(1);
  2386.         glNewList(gaussianLists[2], GL_COMPILE);
  2387. #endif
  2388.         glBegin(GL_QUADS);
  2389.         drawBlendedVertices(0.0f, 0.0f, blend);
  2390.         drawBlendedVertices(-xdelta,      0.0f, 0.632f*blend);
  2391.         drawBlendedVertices( xdelta,      0.0f, 0.632f*blend);
  2392.         drawBlendedVertices(-2.0f*xdelta, 0.0f, 0.159f*blend);
  2393.         drawBlendedVertices( 2.0f*xdelta, 0.0f, 0.159f*blend);
  2394.         drawBlendedVertices(-3.0f*xdelta, 0.0f, 0.016f*blend);
  2395.         drawBlendedVertices( 3.0f*xdelta, 0.0f, 0.016f*blend);
  2396.         glEnd();
  2397.         glCopyTexImage2D(GL_TEXTURE_2D, 0, blurFormat, 0, 0,