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

OpenGL

开发平台:

Visual C++

  1.                          width, height, 0);
  2.         glBegin(GL_QUADS);
  3.         drawBlendedVertices(0.0f, -ydelta,      0.632f);
  4.         drawBlendedVertices(0.0f,  ydelta,      0.632f);
  5.         drawBlendedVertices(0.0f, -2.0f*ydelta, 0.159f);
  6.         drawBlendedVertices(0.0f,  2.0f*ydelta, 0.159f);
  7.         drawBlendedVertices(0.0f, -3.0f*ydelta, 0.016f);
  8.         drawBlendedVertices(0.0f,  3.0f*ydelta, 0.016f);
  9.         glEnd();
  10. #ifdef USE_BLOOM_LISTS
  11.         glEndList();
  12.     }
  13.     glCallList(gaussianLists[2]);
  14. #endif
  15. }
  16. void Renderer::drawBlur()
  17. {
  18.     blurTextures[0]->bind();
  19.     glBegin(GL_QUADS);
  20.     drawBlendedVertices(0.0f, 0.0f, 1.0f);
  21.     glEnd();
  22.     blurTextures[1]->bind();
  23.     glBegin(GL_QUADS);
  24.     drawBlendedVertices(0.0f, 0.0f, 1.0f);
  25.     glEnd();
  26. }
  27. bool Renderer::getBloomEnabled()
  28. {
  29.     return bloomEnabled;
  30. }
  31. void Renderer::setBloomEnabled(bool aBloomEnabled)
  32. {
  33.     bloomEnabled = aBloomEnabled;
  34. }
  35. void Renderer::increaseBrightness()
  36. {
  37.     brightPlus += 1.0f;
  38. }
  39. void Renderer::decreaseBrightness()
  40. {
  41.     brightPlus -= 1.0f;
  42. }
  43. float Renderer::getBrightness()
  44. {
  45.     return brightPlus;
  46. }
  47. #endif // USE_HDR
  48. void Renderer::render(const Observer& observer,
  49.                       const Universe& universe,
  50.                       float faintestMagNight,
  51.                       const Selection& sel)
  52. {
  53.     glMatrixMode(GL_PROJECTION);
  54.     glLoadIdentity();
  55. #ifdef USE_HDR
  56.     renderToTexture(observer, universe, faintestMagNight, sel);
  57.     //------------- Post processing from here ------------//
  58.     glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT);
  59.     glEnable(GL_TEXTURE_2D);
  60.     glDisable(GL_BLEND);
  61.     glDisable(GL_LIGHTING);
  62.     glDisable(GL_DEPTH_TEST);
  63.     glDepthMask(GL_FALSE);
  64.     glMatrixMode(GL_PROJECTION);
  65.     glPushMatrix();
  66.     glLoadIdentity();
  67.     glOrtho( 0.0, 1.0, 0.0, 1.0, -1.0, 1.0 );
  68.     glMatrixMode (GL_MODELVIEW);
  69.     glPushMatrix();
  70.     glLoadIdentity();
  71.     if (bloomEnabled)
  72.     {
  73.         renderToBlurTexture(0);
  74.         renderToBlurTexture(1);
  75. //        renderToBlurTexture(2);
  76.     }
  77.     drawSceneTexture();
  78.     glEnable(GL_BLEND);
  79.     glBlendFunc(GL_ONE, GL_ONE);
  80. #ifdef HDR_COMPRESS
  81.     // Assume luminance 1.0 mapped to 128 previously
  82.     // Compositing a 2nd copy doubles 128->255
  83.     drawSceneTexture();
  84. #endif
  85.     if (bloomEnabled)
  86.     {
  87.         drawBlur();
  88.     }
  89.     glMatrixMode(GL_PROJECTION);
  90.     glPopMatrix();
  91.     glMatrixMode(GL_MODELVIEW);
  92.     glPopMatrix();
  93.     glPopAttrib();
  94. #else
  95.     draw(observer, universe, faintestMagNight, sel);
  96. #endif
  97. }
  98. void Renderer::draw(const Observer& observer,
  99.                     const Universe& universe,
  100.                     float faintestMagNight,
  101.                     const Selection& sel)
  102. {
  103.     // Get the observer's time
  104.     double now = observer.getTime();
  105.     realTime = observer.getRealTime();
  106.     frameCount++;
  107.     settingsChanged = false;
  108.     // Compute the size of a pixel
  109.     setFieldOfView(radToDeg(observer.getFOV()));
  110.     pixelSize = calcPixelSize(fov, (float) windowHeight);
  111.     // Set up the projection we'll use for rendering stars.
  112.     gluPerspective(fov,
  113.                    (float) windowWidth / (float) windowHeight,
  114.                    NEAR_DIST, FAR_DIST);
  115.     // Set the modelview matrix
  116.     glMatrixMode(GL_MODELVIEW);
  117.     // Get the displayed surface texture set to use from the observer
  118.     displayedSurface = observer.getDisplayedSurface();
  119.     locationFilter = observer.getLocationFilter();
  120.     if (usePointSprite && getGLContext()->getVertexProcessor() != NULL)
  121.     {
  122.         useNewStarRendering = true;
  123.     }
  124.     else
  125.     {
  126.         useNewStarRendering = false;
  127.     }
  128.     // Highlight the selected object
  129.     highlightObject = sel;
  130.     m_cameraOrientation = observer.getOrientationf();
  131.     // Get the view frustum used for culling in camera space.
  132.     float viewAspectRatio = (float) windowWidth / (float) windowHeight;
  133.     Frustum frustum(degToRad(fov),
  134.                     viewAspectRatio,
  135.                     MinNearPlaneDistance);
  136.     // Get the transformed frustum, used for culling in the astrocentric coordinate
  137.     // system.
  138.     Frustum xfrustum(degToRad(fov),
  139.                      viewAspectRatio,
  140.                      MinNearPlaneDistance);
  141.     xfrustum.transform(conjugate(observer.getOrientationf()).toMatrix3());
  142.     // Set up the camera for star rendering; the units of this phase
  143.     // are light years.
  144.     Point3f observerPosLY = (Point3f) observer.getPosition();
  145.     observerPosLY.x *= 1e-6f;
  146.     observerPosLY.y *= 1e-6f;
  147.     observerPosLY.z *= 1e-6f;
  148.     glPushMatrix();
  149.     glRotate(m_cameraOrientation);
  150.     // Get the model matrix *before* translation.  We'll use this for
  151.     // positioning star and planet labels.
  152.     glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
  153.     glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
  154.     clearSortedAnnotations();
  155.     // Put all solar system bodies into the render list.  Stars close and
  156.     // large enough to have discernible surface detail are also placed in
  157.     // renderList.
  158.     renderList.clear();
  159.     orbitPathList.clear();
  160.     lightSourceList.clear();
  161.     secondaryIlluminators.clear();
  162.     // See if we want to use AutoMag.
  163.     if ((renderFlags & ShowAutoMag) != 0)
  164.     {
  165.         autoMag(faintestMag);
  166.     }
  167.     else
  168.     {
  169.         faintestMag = faintestMagNight;
  170.         saturationMag = saturationMagNight;
  171.     }
  172.     faintestPlanetMag = faintestMag;
  173. #ifdef USE_HDR
  174.     float maxBodyMagPrev = saturationMag;
  175.     maxBodyMag = min(maxBodyMag, saturationMag);
  176.     vector<RenderListEntry>::iterator closestBody;
  177.     const Star *brightestStar = NULL;
  178.     bool foundClosestBody   = false;
  179.     bool foundBrightestStar = false;
  180. #endif
  181.     if (renderFlags & ShowPlanets)
  182.     {
  183.         nearStars.clear();
  184.         universe.getNearStars(observer.getPosition(), 1.0f, nearStars);
  185.         // Set up direct light sources (i.e. just stars at the moment)
  186.         setupLightSources(nearStars, observer.getPosition(), now, lightSourceList);
  187.         // Traverse the frame trees of each nearby solar system and
  188.         // build the list of objects to be rendered.
  189.         for (vector<const Star*>::const_iterator iter = nearStars.begin();
  190.              iter != nearStars.end(); iter++)
  191.         {
  192.             const Star* sun = *iter;
  193.             SolarSystem* solarSystem = universe.getSolarSystem(sun);
  194.             if (solarSystem != NULL)
  195.             {
  196.                 FrameTree* solarSysTree = solarSystem->getFrameTree();
  197.                 if (solarSysTree != NULL)
  198.                 {
  199.                     if (solarSysTree->updateRequired())
  200.                     {
  201.                         // Tree has changed, so we must recompute bounding spheres.
  202.                         solarSysTree->recomputeBoundingSphere();
  203.                         solarSysTree->markUpdated();
  204.                     }
  205.                     // Compute the position of the observer in astrocentric coordinates
  206.                     Point3d astrocentricObserverPos = astrocentricPosition(observer.getPosition(), *sun, now);
  207.                     // Build render lists for bodies and orbits paths
  208.                     buildRenderLists(astrocentricObserverPos,
  209.                                      xfrustum,
  210.                                      Vec3d(0.0, 0.0, -1.0) * observer.getOrientation().toMatrix3(),
  211.                                      Vec3d(0.0, 0.0, 0.0),
  212.                                      solarSysTree,
  213.                                      observer,
  214.                                      now);
  215.                     if (renderFlags & ShowOrbits)
  216.                     {
  217.                         buildOrbitLists(astrocentricObserverPos,
  218.                                         observer.getOrientationf(),
  219.                                         xfrustum,
  220.                                         solarSysTree,
  221.                                         now);
  222.                     }
  223.                 }
  224.             }
  225.             addStarOrbitToRenderList(*sun, observer, now);
  226.         }
  227.         if ((labelMode & (BodyLabelMask)) != 0)
  228.         {
  229.             buildLabelLists(xfrustum, now);
  230.         }
  231.         starTex->bind();
  232.     }
  233.     setupSecondaryLightSources(secondaryIlluminators, lightSourceList);
  234. #ifdef USE_HDR
  235.     Mat3f viewMat = conjugate(observer.getOrientationf()).toMatrix3();
  236.     float maxSpan = (float) sqrt(square((float) windowWidth) +
  237.                                  square((float) windowHeight));
  238.     float nearZcoeff = (float) cos(degToRad(fov / 2)) *
  239.         ((float) windowHeight / maxSpan);
  240.     // Remove objects from the render list that lie completely outside the
  241.     // view frustum.
  242.     vector<RenderListEntry>::iterator notCulled = renderList.begin();
  243.     for (vector<RenderListEntry>::iterator iter = renderList.begin();
  244.          iter != renderList.end(); iter++)
  245.     {
  246.         Point3f center = iter->position * viewMat;
  247.         bool convex = true;
  248.         float radius = 1.0f;
  249.         float cullRadius = 1.0f;
  250.         float cloudHeight = 0.0f;
  251.         switch (iter->renderableType)
  252.         {
  253.         case RenderListEntry::RenderableStar:
  254.             continue;
  255.         case RenderListEntry::RenderableCometTail:
  256.             radius = iter->radius;
  257.             cullRadius = radius;
  258.             convex = false;
  259.             break;
  260.         case RenderListEntry::RenderableReferenceMark:
  261.             radius = iter->radius;
  262.             cullRadius = radius;
  263.             convex = false;
  264.             break;
  265.         case RenderListEntry::RenderableBody:
  266.         default:
  267.             radius = iter->body->getBoundingRadius();
  268.             if (iter->body->getRings() != NULL)
  269.             {
  270.                 radius = iter->body->getRings()->outerRadius;
  271.                 convex = false;
  272.             }
  273.             if (!iter->body->isEllipsoid())
  274.                 convex = false;
  275.             cullRadius = radius;
  276.             if (iter->body->getAtmosphere() != NULL)
  277.             {
  278.                 cullRadius += iter->body->getAtmosphere()->height;
  279.                 cloudHeight = max(iter->body->getAtmosphere()->cloudHeight,
  280.                                   iter->body->getAtmosphere()->mieScaleHeight * (float) -log(AtmosphereExtinctionThreshold));
  281.             }
  282.             break;
  283.         }
  284.         // Test the object's bounding sphere against the view frustum
  285.         if (frustum.testSphere(center, cullRadius) != Frustum::Outside)
  286.         {
  287.             float nearZ = center.distanceFromOrigin() - radius;
  288.             nearZ = -nearZ * nearZcoeff;
  289.             
  290.             if (nearZ > -MinNearPlaneDistance)
  291.                 iter->nearZ = -max(MinNearPlaneDistance, radius / 2000.0f);
  292.             else
  293.                 iter->nearZ = nearZ;
  294.             
  295.             if (!convex)
  296.             {
  297.                 iter->farZ = center.z - radius;
  298.                 if (iter->farZ / iter->nearZ > MaxFarNearRatio * 0.5f)
  299.                     iter->nearZ = iter->farZ / (MaxFarNearRatio * 0.5f);
  300.             }
  301.             else
  302.             {
  303.                 // Make the far plane as close as possible
  304.                 float d = center.distanceFromOrigin();
  305.                 
  306.                 // Account for ellipsoidal objects
  307.                 float eradius = radius;
  308.                 if (iter->body != NULL)
  309.                 {
  310.                     Vec3f semiAxes = iter->body->getSemiAxes();
  311.                     float minSemiAxis = min(semiAxes.x, min(semiAxes.y, semiAxes.z));
  312.                     eradius *= minSemiAxis / radius;
  313.                 }
  314.                 if (d > eradius)
  315.                 {
  316.                     iter->farZ = iter->centerZ - iter->radius;
  317.                 }
  318.                 else
  319.                 {
  320.                     // We're inside the bounding sphere (and, if the planet
  321.                     // is spherical, inside the planet.)
  322.                     iter->farZ = iter->nearZ * 2.0f;
  323.                 }
  324.                 
  325.                 if (cloudHeight > 0.0f)
  326.                 {
  327.                     // If there's a cloud layer, we need to move the
  328.                     // far plane out so that the clouds aren't clipped
  329.                     float cloudLayerRadius = eradius + cloudHeight;
  330.                     iter->farZ -= (float) sqrt(square(cloudLayerRadius) -
  331.                                                square(eradius));
  332.                 }
  333.             }
  334.             
  335.             *notCulled = *iter;
  336.             notCulled++;
  337.             maxBodyMag = min(maxBodyMag, iter->appMag);
  338.             foundClosestBody = true;
  339.         }
  340.     }
  341.     renderList.resize(notCulled - renderList.begin());
  342.     saturationMag = maxBodyMag;
  343. #endif // USE_HDR
  344.     Color skyColor(0.0f, 0.0f, 0.0f);
  345.     // Scan through the render list to see if we're inside a planetary
  346.     // atmosphere.  If so, we need to adjust the sky color as well as the
  347.     // limiting magnitude of stars (so stars aren't visible in the daytime
  348.     // on planets with thick atmospheres.)
  349.     if ((renderFlags & ShowAtmospheres) != 0)
  350.     {
  351.         for (vector<RenderListEntry>::iterator iter = renderList.begin();
  352.              iter != renderList.end(); iter++)
  353.         {
  354.             if (iter->renderableType == RenderListEntry::RenderableBody && iter->body->getAtmosphere() != NULL)
  355.             {
  356.                 // Compute the density of the atmosphere, and from that
  357.                 // the amount light scattering.  It's complicated by the
  358.                 // possibility that the planet is oblate and a simple distance
  359.                 // to sphere calculation will not suffice.
  360.                 const Atmosphere* atmosphere = iter->body->getAtmosphere();
  361.                 float radius = iter->body->getRadius();
  362.                 Vec3f semiAxes = iter->body->getSemiAxes() * (1.0f / radius);
  363.                 Vec3f recipSemiAxes(1.0f / semiAxes.x,
  364.                                     1.0f / semiAxes.y,
  365.                                     1.0f / semiAxes.z);
  366.                 Mat3f A = Mat3f::scaling(recipSemiAxes);
  367.                 Vec3f eyeVec = iter->position - Point3f(0.0f, 0.0f, 0.0f);
  368.                 eyeVec *= (1.0f / radius);
  369.                 // Compute the orientation of the planet before axial rotation
  370.                 Quatd qd = iter->body->getEclipticToEquatorial(now);
  371.                 Quatf q((float) qd.w, (float) qd.x, (float) qd.y, (float) qd.z);
  372.                 eyeVec = eyeVec * conjugate(q).toMatrix3();
  373.                 // ellipDist is not the true distance from the surface unless
  374.                 // the planet is spherical.  The quantity that we do compute
  375.                 // is the distance to the surface along a line from the eye
  376.                 // position to the center of the ellipsoid.
  377.                 float ellipDist = (float) sqrt((eyeVec * A) * (eyeVec * A)) - 1.0f;
  378.                 if (ellipDist < atmosphere->height / radius &&
  379.                     atmosphere->height > 0.0f)
  380.                 {
  381.                     float density = 1.0f - ellipDist /
  382.                         (atmosphere->height / radius);
  383.                     if (density > 1.0f)
  384.                         density = 1.0f;
  385.                     Vec3f sunDir = iter->sun;
  386.                     Vec3f normal = Point3f(0.0f, 0.0f, 0.0f) - iter->position;
  387.                     sunDir.normalize();
  388.                     normal.normalize();
  389. #ifdef USE_HDR
  390.                     // Ignore magnitude of planet underneath when lighting atmosphere
  391.                     // Could be changed to simulate light pollution, etc
  392.                     maxBodyMag = maxBodyMagPrev;
  393.                     saturationMag = maxBodyMag;
  394. #endif
  395.                     float illumination = Math<float>::clamp((sunDir * normal) + 0.2f);
  396.                     float lightness = illumination * density;
  397.                     faintestMag = faintestMag  - 15.0f * lightness;
  398.                     saturationMag = saturationMag - 15.0f * lightness;
  399.                 }
  400.             }
  401.         }
  402.     }
  403.     // Now we need to determine how to scale the brightness of stars.  The
  404.     // brightness will be proportional to the apparent magnitude, i.e.
  405.     // a logarithmic function of the stars apparent brightness.  This mimics
  406.     // the response of the human eye.  We sort of fudge things here and
  407.     // maintain a minimum range of six magnitudes between faintest visible
  408.     // and saturation; this keeps stars from popping in or out as the sun
  409.     // sets or rises.
  410. #ifdef USE_HDR
  411.     brightnessScale = 1.0f / (faintestMag -  saturationMag);
  412. #else
  413.     if (faintestMag - saturationMag >= 6.0f)
  414.         brightnessScale = 1.0f / (faintestMag -  saturationMag);
  415.     else
  416.         brightnessScale = 0.1667f;
  417. #endif
  418. #ifdef USE_HDR
  419.     exposurePrev = exposure;
  420.     float exposureNow = 1.f / (1.f+exp((faintestMag - saturationMag + DEFAULT_EXPOSURE)/2.f));
  421.     exposure = exposurePrev + (exposureNow - exposurePrev) * (1.f - exp(-1.f/(15.f * EXPOSURE_HALFLIFE)));
  422.     brightnessScale /= exposure;
  423. #endif
  424. #ifdef DEBUG_HDR_TONEMAP
  425.     HDR_LOG <<
  426. //        "brightnessScale = " << brightnessScale <<
  427.         "faint = "    << faintestMag << ", " <<
  428.         "sat = "      << saturationMag << ", " <<
  429.         "exposure = " << (exposure+brightPlus) << endl;
  430. #endif
  431. #ifdef HDR_COMPRESS
  432.     ambientColor = Color(ambientLightLevel*.5f, ambientLightLevel*.5f, ambientLightLevel*.5f);
  433. #else
  434.     ambientColor = Color(ambientLightLevel, ambientLightLevel, ambientLightLevel);
  435. #endif
  436.     // Create the ambient light source.  For realistic scenes in space, this
  437.     // should be black.
  438.     glAmbientLightColor(ambientColor);
  439. #ifdef USE_HDR
  440.     glClearColor(skyColor.red(), skyColor.green(), skyColor.blue(), 0.0f);
  441. #else
  442.     glClearColor(skyColor.red(), skyColor.green(), skyColor.blue(), 1);
  443. #endif
  444.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  445.     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  446.     glDisable(GL_LIGHTING);
  447.     glDepthMask(GL_FALSE);
  448.     glEnable(GL_BLEND);
  449.     glEnable(GL_TEXTURE_2D);
  450.     
  451.     // Render sky grids first--these will always be in the background
  452.     {
  453.         glDisable(GL_TEXTURE_2D);
  454.         if ((renderFlags & ShowSmoothLines) != 0)
  455.             enableSmoothLines();
  456.         renderSkyGrids(observer);
  457.         if ((renderFlags & ShowSmoothLines) != 0)
  458.             disableSmoothLines();
  459.         glEnable(GL_BLEND);
  460.         glEnable(GL_TEXTURE_2D);
  461.     }
  462.     // Render deep sky objects
  463.     if ((renderFlags & (ShowGalaxies | 
  464. ShowGlobulars |
  465.                         ShowNebulae |
  466.                         ShowOpenClusters)) != 0 &&
  467.         universe.getDSOCatalog() != NULL)
  468.     {
  469.         renderDeepSkyObjects(universe, observer, faintestMag);
  470.     }
  471.     // Translate the camera before rendering the stars
  472.     glPushMatrix();
  473.     // Render stars
  474. #ifdef USE_HDR
  475.     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
  476. #endif
  477.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  478.     if ((renderFlags & ShowStars) != 0 && universe.getStarCatalog() != NULL)
  479.     {
  480.         // Disable multisample rendering when drawing point stars
  481.         bool toggleAA = (starStyle == Renderer::PointStars && glIsEnabled(GL_MULTISAMPLE_ARB));
  482.         if (toggleAA)
  483.             glDisable(GL_MULTISAMPLE_ARB);
  484.         if (useNewStarRendering)
  485.             renderPointStars(*universe.getStarCatalog(), faintestMag, observer);
  486.         else
  487.             renderStars(*universe.getStarCatalog(), faintestMag, observer);
  488.         if (toggleAA)
  489.             glEnable(GL_MULTISAMPLE_ARB);
  490.     }
  491. #ifdef USE_HDR
  492.     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  493. #endif
  494.     glTranslatef(-observerPosLY.x, -observerPosLY.y, -observerPosLY.z);
  495.     // Render asterisms
  496.     if ((renderFlags & ShowDiagrams) != 0 && universe.getAsterisms() != NULL)
  497.     {
  498.         /* We'll linearly fade the lines as a function of the observer's
  499.            distance to the origin of coordinates: */
  500.         float opacity = 1.0f;
  501.         float dist = observerPosLY.distanceFromOrigin() * 1e6f;
  502.         if (dist > MaxAsterismLinesConstDist)
  503.         {
  504.             opacity = clamp((MaxAsterismLinesConstDist - dist) /
  505.                             (MaxAsterismLinesDist - MaxAsterismLinesConstDist) + 1);
  506.         }
  507.         glColor(ConstellationColor, opacity);
  508.         glDisable(GL_TEXTURE_2D);
  509.         if ((renderFlags & ShowSmoothLines) != 0)
  510.             enableSmoothLines();
  511.         AsterismList* asterisms = universe.getAsterisms();
  512.         for (AsterismList::const_iterator iter = asterisms->begin();
  513.              iter != asterisms->end(); iter++)
  514.         {
  515.             Asterism* ast = *iter;
  516. if (ast->getActive())
  517.             {
  518. if (ast->isColorOverridden())
  519. glColor(ast->getOverrideColor(), opacity);
  520. else
  521. glColor(ConstellationColor, opacity);
  522. for (int i = 0; i < ast->getChainCount(); i++)
  523. {
  524. const Asterism::Chain& chain = ast->getChain(i);
  525. glBegin(GL_LINE_STRIP);
  526. for (Asterism::Chain::const_iterator iter = chain.begin();
  527.  iter != chain.end(); iter++)
  528. glVertex(*iter);
  529. glEnd();
  530. }
  531. }
  532.         }
  533.         if ((renderFlags & ShowSmoothLines) != 0)
  534.             disableSmoothLines();
  535.     }
  536.     if ((renderFlags & ShowBoundaries) != 0)
  537.     {
  538.         /* We'll linearly fade the boundaries as a function of the
  539.            observer's distance to the origin of coordinates: */
  540.         float opacity = 1.0f;
  541.         float dist = observerPosLY.distanceFromOrigin() * 1e6f;
  542.         if (dist > MaxAsterismLabelsConstDist)
  543.         {
  544.             opacity = clamp((MaxAsterismLabelsConstDist - dist) /
  545.                             (MaxAsterismLabelsDist - MaxAsterismLabelsConstDist) + 1);
  546.         }
  547.         glColor(BoundaryColor, opacity);
  548.         glDisable(GL_TEXTURE_2D);
  549.         if ((renderFlags & ShowSmoothLines) != 0)
  550.             enableSmoothLines();
  551.         if (universe.getBoundaries() != NULL)
  552.             universe.getBoundaries()->render();
  553.         if ((renderFlags & ShowSmoothLines) != 0)
  554.             disableSmoothLines();
  555.     }
  556.     // Render star and deep sky object labels
  557.     renderBackgroundAnnotations(FontNormal);
  558.     // Render constellations labels
  559.     if ((labelMode & ConstellationLabels) != 0 && universe.getAsterisms() != NULL)
  560.     {
  561.         labelConstellations(*universe.getAsterisms(), observer);
  562.         renderBackgroundAnnotations(FontLarge);
  563.     }
  564.     // Pop observer translation
  565.     glPopMatrix();
  566.     if ((renderFlags & ShowMarkers) != 0)
  567.     {
  568.         renderMarkers(*universe.getMarkers(),
  569.                       observer.getPosition(),
  570.                observer.getOrientation(),
  571.                       now);
  572.         
  573.         // Render background markers; rendering of other markers is deferred until
  574.         // solar system objects are rendered.
  575.         renderBackgroundAnnotations(FontNormal);
  576.     }    
  577.     // Draw the selection cursor
  578.     bool selectionVisible = false;
  579.     if (!sel.empty() && (renderFlags & ShowMarkers))
  580.     {
  581.         UniversalCoord uc = sel.getPosition(now);
  582.         Vec3d offset = (uc - observer.getPosition()) * astro::microLightYearsToKilometers(1.0);
  583.         static MarkerRepresentation cursorRep(MarkerRepresentation::Crosshair);
  584.         selectionVisible = xfrustum.testSphere(Point3d(0, 0, 0) + offset, sel.radius()) != Frustum::Outside;
  585.         if (selectionVisible)
  586.         {
  587.             double distance = offset.length();
  588.             float symbolSize = (float) (sel.radius() / distance) / pixelSize;
  589.             // Modify the marker position so that it is always in front of the marked object.
  590.             double boundingRadius;
  591.             if (sel.body() != NULL)
  592.                 boundingRadius = sel.body()->getBoundingRadius();
  593.             else
  594.                 boundingRadius = sel.radius();                
  595.             offset *= (1.0 - boundingRadius * 1.01 / distance);
  596.             // The selection cursor is only partially visible when the selected object is obscured. To implement
  597.             // this behavior we'll draw two markers at the same position: one that's always visible, and another one
  598.             // that's depth sorted. When the selection is occluded, only the foreground marker is visible. Otherwise,
  599.             // both markers are drawn and cursor appears much brighter as a result.
  600.             if (distance < astro::lightYearsToKilometers(1.0))
  601.             {
  602.                 addSortedAnnotation(&cursorRep, EMPTY_STRING, Color(SelectionCursorColor, 1.0f),
  603.                                     Point3f((float) offset.x, (float) offset.y, (float) offset.z),
  604.                                     AlignLeft, VerticalAlignTop, symbolSize);
  605.             }
  606.             else
  607.             {
  608.                 addAnnotation(backgroundAnnotations, &cursorRep, EMPTY_STRING, Color(SelectionCursorColor, 1.0f),
  609.                               Point3f((float) offset.x, (float) offset.y, (float) offset.z),
  610.                               AlignLeft, VerticalAlignTop, symbolSize);
  611.             }
  612.             Color occludedCursorColor(SelectionCursorColor.red(), SelectionCursorColor.green() + 0.3f, SelectionCursorColor.blue());
  613.             addAnnotation(foregroundAnnotations,
  614.                           &cursorRep, EMPTY_STRING, Color(occludedCursorColor, 0.4f),
  615.                           Point3f((float) offset.x, (float) offset.y, (float) offset.z),
  616.                           AlignLeft, VerticalAlignTop, symbolSize);
  617.         }
  618.     }
  619.     glPolygonMode(GL_FRONT, (GLenum) renderMode);
  620.     glPolygonMode(GL_BACK, (GLenum) renderMode);
  621.     {
  622.         Mat3f viewMat = conjugate(observer.getOrientationf()).toMatrix3();
  623.         // Remove objects from the render list that lie completely outside the
  624.         // view frustum.
  625. #ifdef USE_HDR
  626.         maxBodyMag = maxBodyMagPrev;
  627.         float starMaxMag = maxBodyMagPrev;
  628.         notCulled = renderList.begin();
  629. #else
  630.         vector<RenderListEntry>::iterator notCulled = renderList.begin();
  631. #endif
  632.         for (vector<RenderListEntry>::iterator iter = renderList.begin();
  633.              iter != renderList.end(); iter++)
  634.         {
  635. #ifdef USE_HDR
  636.             switch (iter->renderableType)
  637.             {
  638.             case RenderListEntry::RenderableStar:
  639.                 break;
  640.             default:
  641.                 *notCulled = *iter;
  642.                 notCulled++;
  643.                 continue;
  644.             }
  645. #endif
  646.             Point3f center = iter->position * viewMat;
  647.             bool convex = true;
  648.             float radius = 1.0f;
  649.             float cullRadius = 1.0f;
  650.             float cloudHeight = 0.0f;
  651. #ifndef USE_HDR
  652.             switch (iter->renderableType)
  653.             {
  654.             case RenderListEntry::RenderableStar:
  655.                 radius = iter->star->getRadius();
  656.                 cullRadius = radius * (1.0f + CoronaHeight);
  657.                 break;
  658.             case RenderListEntry::RenderableCometTail:
  659.                 radius = iter->radius;
  660.                 cullRadius = radius;
  661.                 convex = false;
  662.                 break;
  663.             case RenderListEntry::RenderableBody:
  664.                 radius = iter->body->getBoundingRadius();
  665.                 if (iter->body->getRings() != NULL)
  666.                 {
  667.                     radius = iter->body->getRings()->outerRadius;
  668.                     convex = false;
  669.                 }
  670.                 if (!iter->body->isEllipsoid())
  671.                     convex = false;
  672.                 cullRadius = radius;
  673.                 if (iter->body->getAtmosphere() != NULL)
  674.                 {
  675.                     cullRadius += iter->body->getAtmosphere()->height;
  676.                     cloudHeight = max(iter->body->getAtmosphere()->cloudHeight,
  677.                                       iter->body->getAtmosphere()->mieScaleHeight * (float) -log(AtmosphereExtinctionThreshold));
  678.                 }
  679.                 break;
  680.             case RenderListEntry::RenderableReferenceMark:
  681.                 radius = iter->radius;
  682.                 cullRadius = radius;
  683.                 convex = false;
  684.                 break;
  685.             default:
  686.                 break;
  687.             }
  688. #else
  689.             radius = iter->star->getRadius();
  690.             cullRadius = radius * (1.0f + CoronaHeight);
  691. #endif // USE_HDR
  692.             // Test the object's bounding sphere against the view frustum
  693.             if (frustum.testSphere(center, cullRadius) != Frustum::Outside)
  694.             {
  695.                 float nearZ = center.distanceFromOrigin() - radius;
  696. #ifdef USE_HDR
  697.                 nearZ = -nearZ * nearZcoeff;
  698. #else
  699.                 float maxSpan = (float) sqrt(square((float) windowWidth) +
  700.                                              square((float) windowHeight));
  701.                 nearZ = -nearZ * (float) cos(degToRad(fov / 2)) *
  702.                     ((float) windowHeight / maxSpan);
  703. #endif
  704.                 if (nearZ > -MinNearPlaneDistance)
  705.                     iter->nearZ = -max(MinNearPlaneDistance, radius / 2000.0f);
  706.                 else
  707.                     iter->nearZ = nearZ;
  708.                 if (!convex)
  709.                 {
  710.                     iter->farZ = center.z - radius;
  711.                     if (iter->farZ / iter->nearZ > MaxFarNearRatio * 0.5f)
  712.                         iter->nearZ = iter->farZ / (MaxFarNearRatio * 0.5f);
  713.                 }
  714.                 else
  715.                 {
  716.                     // Make the far plane as close as possible
  717.                     float d = center.distanceFromOrigin();
  718.                     // Account for ellipsoidal objects
  719.                     float eradius = radius;
  720.                     if (iter->renderableType == RenderListEntry::RenderableBody)
  721.                     {
  722.                         Vec3f semiAxes = iter->body->getSemiAxes();
  723.                         float minSemiAxis = min(semiAxes.x, min(semiAxes.y, semiAxes.z));
  724.                         eradius *= minSemiAxis / radius;
  725.                     }
  726.                     if (d > eradius)
  727.                     {
  728.                         iter->farZ = iter->centerZ - iter->radius;
  729.                     }
  730.                     else
  731.                     {
  732.                         // We're inside the bounding sphere (and, if the planet
  733.                         // is spherical, inside the planet.)
  734.                         iter->farZ = iter->nearZ * 2.0f;
  735.                     }
  736.                     if (cloudHeight > 0.0f)
  737.                     {
  738.                         // If there's a cloud layer, we need to move the
  739.                         // far plane out so that the clouds aren't clipped
  740.                         float cloudLayerRadius = eradius + cloudHeight;
  741.                         iter->farZ -= (float) sqrt(square(cloudLayerRadius) -
  742.                                                    square(eradius));
  743.                     }
  744.                 }
  745.                 *notCulled = *iter;
  746.                 notCulled++;
  747. #ifdef USE_HDR
  748.                 if (iter->discSizeInPixels > 1.0f &&
  749.                     iter->appMag < starMaxMag)
  750.                 {
  751.                     starMaxMag = iter->appMag;
  752.                     brightestStar = iter->star;
  753.                     foundBrightestStar = true;
  754.                 }
  755. #endif
  756.             }
  757.         }
  758.         renderList.resize(notCulled - renderList.begin());
  759.         // The calls to buildRenderLists/renderStars filled renderList
  760.         // with visible bodies.  Sort it front to back, then
  761.         // render each entry in reverse order (TODO: convenient, but not
  762.         // ideal for performance; should render opaque objects front to
  763.         // back, then translucent objects back to front. However, the
  764.         // amount of overdraw in Celestia is typically low.)
  765.         sort(renderList.begin(), renderList.end());
  766.         // Sort the annotations
  767.         sort(depthSortedAnnotations.begin(), depthSortedAnnotations.end());
  768.         // Sort the orbit paths
  769.         sort(orbitPathList.begin(), orbitPathList.end());
  770.         int nEntries = renderList.size();
  771. #ifdef USE_HDR
  772.         // Compute 1 eclipse between eye - closest body - brightest star
  773.         // This prevents an eclipsed star from increasing exposure
  774.         bool eyeNotEclipsed = true;
  775.         closestBody = renderList.empty() ? renderList.end() : renderList.begin();
  776.         if (foundClosestBody &&
  777.             closestBody != renderList.end() &&
  778.             closestBody->renderableType == RenderListEntry::RenderableBody &&
  779.             closestBody->body && brightestStar)
  780.         {
  781.             const Body *body = closestBody->body;
  782.             double scale = astro::microLightYearsToKilometers(1.0);
  783.             Point3d posBody = body->getAstrocentricPosition(now);
  784.             Point3d posStar;
  785.             Point3d posEye = astrocentricPosition(observer.getPosition(), *brightestStar, now);
  786.             if (body->getSystem() &&
  787.                 body->getSystem()->getStar() &&
  788.                 body->getSystem()->getStar() != brightestStar)
  789.             {
  790.                 UniversalCoord center = body->getSystem()->getStar()->getPosition(now);
  791.                 Vec3d v = brightestStar->getPosition(now) - center;
  792.                 posStar = Point3d(v.x, v.y, v.z);
  793.             }
  794.             else
  795.             {
  796.                 posStar = brightestStar->getPosition(now);
  797.             }
  798.             posStar.x /= scale;
  799.             posStar.y /= scale;
  800.             posStar.z /= scale;
  801.             Vec3d lightToBodyDir = posBody - posStar;
  802.             Vec3d bodyToEyeDir = posEye - posBody;
  803.             if (lightToBodyDir * bodyToEyeDir > 0.0)
  804.             {
  805.                 double dist = distance(posEye,
  806.                                        Ray3d(posBody, lightToBodyDir));
  807.                 if (dist < body->getRadius())
  808.                     eyeNotEclipsed = false;
  809.             }
  810.         }
  811.         if (eyeNotEclipsed)
  812.         {
  813.             maxBodyMag = min(maxBodyMag, starMaxMag);
  814.         }
  815. #endif
  816.         // Since we're rendering objects of a huge range of sizes spread over
  817.         // vast distances, we can't just rely on the hardware depth buffer to
  818.         // handle hidden surface removal without a little help. We'll partition
  819.         // the depth buffer into spans that can be rendered without running
  820.         // into terrible depth buffer precision problems. Typically, each body
  821.         // with an apparent size greater than one pixel is allocated its own
  822.         // depth buffer interval. However, this will not correctly handle
  823.         // overlapping objects.  If two objects overlap in depth, we must
  824.         // assign them to the same interval.
  825.         depthPartitions.clear();
  826.         int nIntervals = 0;
  827.         float prevNear = -1e12f;  // ~ 1 light year
  828.         if (nEntries > 0)
  829.             prevNear = renderList[nEntries - 1].farZ * 1.01f;
  830.         int i;
  831.         // Completely partition the depth buffer. Scan from back to front
  832.         // through all the renderable items that passed the culling test.
  833.         for (i = nEntries - 1; i >= 0; i--)
  834.         {
  835.             // Only consider renderables that will occupy more than one pixel.
  836.             if (renderList[i].discSizeInPixels > 1)
  837.             {
  838.                 if (nIntervals == 0 || renderList[i].farZ >= depthPartitions[nIntervals - 1].nearZ)
  839.                 {
  840.                     // This object spans a depth interval that's disjoint with
  841.                     // the current interval, so create a new one for it, and
  842.                     // another interval to fill the gap between the last
  843.                     // interval.
  844.                     DepthBufferPartition partition;
  845.                     partition.index = nIntervals;
  846.                     partition.nearZ = renderList[i].farZ;
  847.                     partition.farZ = prevNear;
  848.                     // Omit null intervals
  849.                     // TODO: Is this necessary? Shouldn't the >= test prevent this?
  850.                     if (partition.nearZ != partition.farZ)
  851.                     {
  852.                         depthPartitions.push_back(partition);
  853.                         nIntervals++;
  854.                     }
  855.                     partition.index = nIntervals;
  856.                     partition.nearZ = renderList[i].nearZ;
  857.                     partition.farZ = renderList[i].farZ;
  858.                     depthPartitions.push_back(partition);
  859.                     nIntervals++;
  860.                     prevNear = partition.nearZ;
  861.                 }
  862.                 else
  863.                 {
  864.                     // This object overlaps the current span; expand the
  865.                     // interval so that it completely contains the object.
  866.                     DepthBufferPartition& partition = depthPartitions[nIntervals - 1];
  867.                     partition.nearZ = max(partition.nearZ, renderList[i].nearZ);
  868.                     partition.farZ = min(partition.farZ, renderList[i].farZ);
  869.                     prevNear = partition.nearZ;
  870.                 }
  871.             }
  872.         }
  873.         // Scan the list of orbit paths and find the closest one. We'll need
  874.         // adjust the nearest interval to accommodate it.
  875.         float zNearest = prevNear;
  876.         for (i = 0; i < (int) orbitPathList.size(); i++)
  877.         {
  878.             const OrbitPathListEntry& o = orbitPathList[i];
  879.             float minNearDistance = min(-o.radius * 0.0001f, o.centerZ + o.radius);
  880.             if (minNearDistance > zNearest)
  881.                 zNearest = minNearDistance;
  882.         }
  883.         
  884.         // Adjust the nearest interval to include the closest marker (if it's
  885.         // closer to the observer than anything else
  886.         if (!depthSortedAnnotations.empty())
  887.         {
  888.             // Factor of 0.999 makes sure ensures that the near plane does not fall
  889.             // exactly at the marker's z coordinate (in which case the marker 
  890.             // would be susceptible to being clipped.) 
  891.             if (-depthSortedAnnotations[0].position.z > zNearest)
  892.                 zNearest = -depthSortedAnnotations[0].position.z * 0.999f;
  893.         }
  894.         
  895. #if DEBUG_COALESCE
  896.         clog << "nEntries: " << nEntries << ",   zNearest: " << zNearest << ",   prevNear: " << prevNear << "n";
  897. #endif
  898.         // If the nearest distance wasn't set, nothing should appear
  899.         // in the frontmost depth buffer interval (so we can set the near plane
  900.         // of the front interval to whatever we want as long as it's less than
  901.         // the far plane distance.
  902.         if (zNearest == prevNear)
  903.             zNearest = 0.0f;
  904.         // Add one last interval for the span from 0 to the front of the
  905.         // nearest object
  906.         {
  907.             // TODO: closest object may not be at entry 0, since objects are
  908.             // sorted by far distance.
  909.             float closest = zNearest;
  910.             if (nEntries > 0)
  911.             {
  912.                 closest = max(closest, renderList[0].nearZ);
  913.                 // Setting a the near plane distance to zero results in unreliable rendering, even
  914.                 // if we don't care about the depth buffer. Compromise and set the near plane
  915.                 // distance to a small fraction of distance to the nearest object.
  916.                 if (closest == 0.0f)
  917.                 {
  918.                     closest = renderList[0].nearZ * 0.01f;
  919.                 }
  920.             }
  921.             DepthBufferPartition partition;
  922.             partition.index = nIntervals;
  923.             partition.nearZ = closest;
  924.             partition.farZ = prevNear;
  925.             depthPartitions.push_back(partition);
  926.             nIntervals++;
  927.         }
  928.         // If orbits are enabled, adjust the farthest partition so that it
  929.         // can contain the orbit.
  930.         if (!orbitPathList.empty())
  931.         {
  932.             depthPartitions[0].farZ = min(depthPartitions[0].farZ,
  933.                                            orbitPathList[orbitPathList.size() - 1].centerZ -
  934.                                            orbitPathList[orbitPathList.size() - 1].radius);
  935.         }
  936.         // We want to avoid overpartitioning the depth buffer. In this stage, we coalesce
  937.         // partitions that have small spans in the depth buffer.
  938.         // TODO: Implement this step!
  939.         vector<Annotation>::iterator annotation = depthSortedAnnotations.begin();
  940.         // Render everything that wasn't culled.
  941.         float intervalSize = 1.0f / (float) max(1, nIntervals);
  942.         i = nEntries - 1;
  943.         for (int interval = 0; interval < nIntervals; interval++)
  944.         {
  945.             currentIntervalIndex = interval;
  946.             beginObjectAnnotations();
  947.             float nearPlaneDistance = -depthPartitions[interval].nearZ;
  948.             float farPlaneDistance  = -depthPartitions[interval].farZ;
  949.             // Set the depth range for this interval--each interval is allocated an
  950.             // equal section of the depth buffer.
  951.             glDepthRange(1.0f - (float) (interval + 1) * intervalSize,
  952.                          1.0f - (float) interval * intervalSize);
  953.             // Set up a perspective projection using the current interval's near and
  954.             // far clip planes.
  955.             glMatrixMode(GL_PROJECTION);
  956.             glLoadIdentity();
  957.             gluPerspective(fov,
  958.                            (float) windowWidth / (float) windowHeight,
  959.                            nearPlaneDistance,
  960.                            farPlaneDistance);
  961.             glMatrixMode(GL_MODELVIEW);
  962.             Frustum intervalFrustum(degToRad(fov),
  963.                                     (float) windowWidth / (float) windowHeight,
  964.                                     -depthPartitions[interval].nearZ,
  965.                                     -depthPartitions[interval].farZ);
  966. #if DEBUG_COALESCE
  967.             clog << "interval: " << interval <<
  968.                     ", near: " << -depthPartitions[interval].nearZ <<
  969.                     ", far: " << -depthPartitions[interval].farZ <<
  970.                     "n";
  971. #endif
  972.             int firstInInterval = i;
  973.             // Render just the opaque objects in the first pass
  974.             while (i >= 0 && renderList[i].farZ < depthPartitions[interval].nearZ)
  975.             {
  976.                 // This interval should completely contain the item
  977.                 // Unless it's just a point?
  978.                 //assert(renderList[i].nearZ <= depthPartitions[interval].near);
  979. #if DEBUG_COALESCE
  980.                 switch (renderList[i].renderableType)
  981.                 {
  982.                 case RenderListEntry::RenderableBody:
  983.                     if (renderList[i].discSizeInPixels > 1)
  984.                     {
  985.                         clog << renderList[i].body->getName() << "n";
  986.                     }
  987.                     else
  988.                     {
  989.                         clog << "point: " << renderList[i].body->getName() << "n";
  990.                     }
  991.                     break;
  992.                         
  993.                 case RenderListEntry::RenderableStar:
  994.                     if (renderList[i].discSizeInPixels > 1)
  995.                     {
  996.                         clog << "Starn";
  997.                     }
  998.                     else
  999.                     {
  1000.                         clog << "point: " << "Star" << "n";
  1001.                     }
  1002.                     break;
  1003.                         
  1004.                 default:
  1005.                     break;
  1006.                 }
  1007. #endif
  1008.                 // Treat objects that are smaller than one pixel as transparent and render
  1009.                 // them in the second pass.
  1010.                 if (renderList[i].isOpaque && renderList[i].discSizeInPixels > 1.0f)
  1011.                     renderItem(renderList[i], observer, m_cameraOrientation, nearPlaneDistance, farPlaneDistance);
  1012.                 i--;
  1013.             }
  1014.             // Render orbit paths
  1015.             if (!orbitPathList.empty())
  1016.             {
  1017.                 glDisable(GL_LIGHTING);
  1018.                 glDisable(GL_TEXTURE_2D);
  1019.                 glEnable(GL_DEPTH_TEST);
  1020.                 glDepthMask(GL_FALSE);
  1021. #ifdef USE_HDR
  1022.                 glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
  1023. #else
  1024.                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1025. #endif
  1026.                 if ((renderFlags & ShowSmoothLines) != 0)
  1027.                 {
  1028.                     enableSmoothLines();
  1029.                 }
  1030.                 // Scan through the list of orbits and render any that overlap this interval
  1031.                 for (vector<OrbitPathListEntry>::const_iterator orbitIter = orbitPathList.begin();
  1032.                      orbitIter != orbitPathList.end(); orbitIter++)
  1033.                 {
  1034.                     // Test for overlap
  1035.                     float nearZ = -orbitIter->centerZ - orbitIter->radius;
  1036.                     float farZ = -orbitIter->centerZ + orbitIter->radius;
  1037.                     // Don't render orbits when they're completely outside this
  1038.                     // depth interval. Also, don't render an orbit in this
  1039.                     // interval if it is vastly larger than the interval
  1040.                     // range; otherwise, the GPU will have precision troubles
  1041.                     // when clipping, producing visual artifacts. The factor
  1042.                     // of 1e5 may need some tuning.
  1043.                     if (nearZ < farPlaneDistance && farZ > nearPlaneDistance &&
  1044.                         orbitIter->radius < 1.0e8f * (farPlaneDistance - nearPlaneDistance))
  1045.                     {
  1046. #ifdef DEBUG_COALESCE
  1047.                         switch (interval % 6)
  1048.                         {
  1049.                             case 0: glColor4f(1.0f, 0.0f, 0.0f, 1.0f); break;
  1050.                             case 1: glColor4f(1.0f, 1.0f, 0.0f, 1.0f); break;
  1051.                             case 2: glColor4f(0.0f, 1.0f, 0.0f, 1.0f); break;
  1052.                             case 3: glColor4f(0.0f, 1.0f, 1.0f, 1.0f); break;
  1053.                             case 4: glColor4f(0.0f, 0.0f, 1.0f, 1.0f); break;
  1054.                             case 5: glColor4f(1.0f, 0.0f, 1.0f, 1.0f); break;
  1055.                             default: glColor4f(1.0f, 1.0f, 1.0f, 1.0f); break;
  1056.                         }
  1057. #endif
  1058.                         orbitsRendered++;
  1059.                         renderOrbit(*orbitIter, now, m_cameraOrientation, intervalFrustum, nearPlaneDistance, farPlaneDistance);
  1060. #if DEBUG_COALESCE
  1061.                         if (highlightObject.body() == orbitIter->body)
  1062.                         {
  1063.                             clog << "orbit, radius=" << orbitIter->radius << "n";
  1064.                         }
  1065. #endif
  1066.                     }
  1067.                     else
  1068.                         orbitsSkipped++;
  1069.                 }
  1070.                 if ((renderFlags & ShowSmoothLines) != 0)
  1071.                     disableSmoothLines();
  1072.                 glDepthMask(GL_FALSE);
  1073.             }
  1074.             // Render transparent objects in the second pass
  1075.             i = firstInInterval;
  1076.             while (i >= 0 && renderList[i].farZ < depthPartitions[interval].nearZ)
  1077.             {
  1078.                 if (!renderList[i].isOpaque || renderList[i].discSizeInPixels <= 1.0f)
  1079.                     renderItem(renderList[i], observer, m_cameraOrientation, nearPlaneDistance, farPlaneDistance);
  1080.                 i--;
  1081.             }
  1082.             // Render annotations in this interval
  1083.             if ((renderFlags & ShowSmoothLines) != 0)
  1084.                 enableSmoothLines();
  1085.             annotation = renderSortedAnnotations(annotation, -depthPartitions[interval].nearZ, -depthPartitions[interval].farZ, FontNormal);
  1086.             endObjectAnnotations();
  1087.             if ((renderFlags & ShowSmoothLines) != 0)
  1088.                 disableSmoothLines();
  1089.             glDisable(GL_DEPTH_TEST);
  1090.         }
  1091. #if 0
  1092.         // TODO: Debugging output for new orbit code; remove when development is complete
  1093.         clog << "orbits: " << orbitsRendered
  1094.              << ", splines: " << splinesRendered
  1095.              << ", skipped: " << orbitsSkipped
  1096.              << ", sections culled: " << sectionsCulled
  1097.              << ", nIntervals: " << nIntervals << "n";
  1098. #endif
  1099.         splinesRendered = 0;
  1100.         orbitsRendered = 0;
  1101.         orbitsSkipped = 0;
  1102.         sectionsCulled = 0;
  1103.         // reset the depth range
  1104.         glDepthRange(0, 1);
  1105.     }
  1106.     
  1107.     renderForegroundAnnotations(FontNormal);
  1108.     glMatrixMode(GL_PROJECTION);
  1109.     glLoadIdentity();
  1110.     gluPerspective(fov,
  1111.                    (float) windowWidth / (float) windowHeight,
  1112.                    NEAR_DIST, FAR_DIST);
  1113.     glMatrixMode(GL_MODELVIEW);
  1114.     if (!selectionVisible && (renderFlags & ShowMarkers))
  1115.         renderSelectionPointer(observer, now, xfrustum, sel);
  1116.     // Pop camera orientation matrix
  1117.     glPopMatrix();
  1118.     glEnable(GL_TEXTURE_2D);
  1119.     glDisable(GL_LIGHTING);
  1120.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1121.     glPolygonMode(GL_FRONT, GL_FILL);
  1122.     glPolygonMode(GL_BACK, GL_FILL);
  1123.     
  1124.     glDisable(GL_BLEND);
  1125.     glDepthMask(GL_TRUE);
  1126.     glEnable(GL_LIGHTING);
  1127. #if 0
  1128.     int errCode = glGetError();
  1129.     if (errCode != GL_NO_ERROR)
  1130.     {
  1131.         cout << "glError: " << (char*) gluErrorString(errCode) << 'n';
  1132.     }
  1133. #endif
  1134.     if (videoSync && glx::glXWaitVideoSyncSGI != NULL)
  1135.     {
  1136.         unsigned int count;
  1137.         glx::glXGetVideoSyncSGI(&count);
  1138.         glx::glXWaitVideoSyncSGI(2, (count+1) & 1, &count);
  1139.     }
  1140. }
  1141. static void renderRingSystem(float innerRadius,
  1142.                              float outerRadius,
  1143.                              float beginAngle,
  1144.                              float endAngle,
  1145.                              unsigned int nSections)
  1146. {
  1147.     float angle = endAngle - beginAngle;
  1148.     glBegin(GL_QUAD_STRIP);
  1149.     for (unsigned int i = 0; i <= nSections; i++)
  1150.     {
  1151.         float t = (float) i / (float) nSections;
  1152.         float theta = beginAngle + t * angle;
  1153.         float s = (float) sin(theta);
  1154.         float c = (float) cos(theta);
  1155.         glTexCoord2f(0, 0.5f);
  1156.         glVertex3f(c * innerRadius, 0, s * innerRadius);
  1157.         glTexCoord2f(1, 0.5f);
  1158.         glVertex3f(c * outerRadius, 0, s * outerRadius);
  1159.     }
  1160.     glEnd();
  1161. }
  1162. // If the an object occupies a pixel or less of screen space, we don't
  1163. // render its mesh at all and just display a starlike point instead.
  1164. // Switching between the particle and mesh renderings of an object is
  1165. // jarring, however . . . so we'll blend in the particle view of the
  1166. // object to smooth things out, making it dimmer as the disc size exceeds the
  1167. // max disc size.
  1168. void Renderer::renderObjectAsPoint_nosprite(Point3f position,
  1169.                                             float radius,
  1170.                                             float appMag,
  1171.                                             float _faintestMag,
  1172.                                             float discSizeInPixels,
  1173.                                             Color color,
  1174.                                             const Quatf& cameraOrientation,
  1175.                                             bool useHalos)
  1176. {
  1177.     float maxDiscSize = 1.0f;
  1178.     float maxBlendDiscSize = maxDiscSize + 3.0f;
  1179.     float discSize = 1.0f;
  1180.     if (discSizeInPixels < maxBlendDiscSize || useHalos)
  1181.     {
  1182.         float fade = 1.0f;
  1183.         if (discSizeInPixels > maxDiscSize)
  1184.         {
  1185.             fade = (maxBlendDiscSize - discSizeInPixels) /
  1186.                 (maxBlendDiscSize - maxDiscSize - 1.0f);
  1187.             if (fade > 1)
  1188.                 fade = 1;
  1189.         }
  1190. #ifdef USE_HDR
  1191.         float fieldCorr = 2.0f * FOV/(fov + FOV);
  1192.         float satPoint = saturationMagNight * (1.0f + fieldCorr * fieldCorr);
  1193. #else
  1194.         float satPoint = saturationMag;
  1195. #endif
  1196.         float a = (_faintestMag - appMag) * brightnessScale + brightnessBias;
  1197.         if (starStyle == ScaledDiscStars && a > 1.0f)
  1198.             discSize = min(discSize * (2.0f * a - 1.0f), maxDiscSize);
  1199.         a = clamp(a) * fade;
  1200.         // We scale up the particle by a factor of 1.6 (at fov = 45deg)
  1201.         // so that it's more visible--the texture we use has fuzzy edges,
  1202.         // and if we render it in just one pixel, it's likely to disappear.
  1203.         Mat3f m = cameraOrientation.toMatrix3();
  1204.         Point3f center = position;
  1205.         // Offset the glare sprite so that it lies in front of the object
  1206.         Vec3f direction(center.x, center.y, center.z);
  1207.         direction.normalize();
  1208.         // Position the sprite on the the line between the viewer and the
  1209.         // object, and on a plane normal to the view direction.
  1210.         center = center + direction * (radius / ((Vec3f(0, 0, 1.0f) * m) * direction));
  1211.         float centerZ = (center * m.transpose()).z;
  1212.         float size = discSize * pixelSize * 1.6f * centerZ / corrFac;
  1213.         Vec3f v0 = Vec3f(-1, -1, 0) * m;
  1214.         Vec3f v1 = Vec3f( 1, -1, 0) * m;
  1215.         Vec3f v2 = Vec3f( 1,  1, 0) * m;
  1216.         Vec3f v3 = Vec3f(-1,  1, 0) * m;
  1217.         glEnable(GL_DEPTH_TEST);
  1218.         starTex->bind();
  1219.         glColor(color, a);
  1220.         glBegin(GL_QUADS);
  1221.         glTexCoord2f(0, 1);
  1222.         glVertex(center + (v0 * size));
  1223.         glTexCoord2f(1, 1);
  1224.         glVertex(center + (v1 * size));
  1225.         glTexCoord2f(1, 0);
  1226.         glVertex(center + (v2 * size));
  1227.         glTexCoord2f(0, 0);
  1228.         glVertex(center + (v3 * size));
  1229.         glEnd();
  1230.         // If the object is brighter than magnitude 1, add a halo around it to
  1231.         // make it appear more brilliant.  This is a hack to compensate for the
  1232.         // limited dynamic range of monitors.
  1233.         if (useHalos && appMag < satPoint)
  1234.         {
  1235.             float dist = center.distanceFromOrigin();
  1236.             float s    = dist * 0.001f * (3 - (appMag - satPoint)) * 2;
  1237.             if (s > size * 3)
  1238.                 size = s * 2.0f/(1.0f + FOV/fov);
  1239.             else
  1240.                 size = size * 3;
  1241.             float realSize = discSizeInPixels * pixelSize * dist;
  1242.             if (size < realSize * 6)
  1243.                 size = realSize * 6;
  1244.             a = GlareOpacity * clamp((appMag - satPoint) * -0.8f);
  1245.             gaussianGlareTex->bind();
  1246.             glColor(color, a);
  1247.             glBegin(GL_QUADS);
  1248.             glTexCoord2f(0, 1);
  1249.             glVertex(center + (v0 * size));
  1250.             glTexCoord2f(1, 1);
  1251.             glVertex(center + (v1 * size));
  1252.             glTexCoord2f(1, 0);
  1253.             glVertex(center + (v2 * size));
  1254.             glTexCoord2f(0, 0);
  1255.             glVertex(center + (v3 * size));
  1256.             glEnd();
  1257.         }
  1258.         glDisable(GL_DEPTH_TEST);
  1259.     }
  1260. }
  1261. // If the an object occupies a pixel or less of screen space, we don't
  1262. // render its mesh at all and just display a starlike point instead.
  1263. // Switching between the particle and mesh renderings of an object is
  1264. // jarring, however . . . so we'll blend in the particle view of the
  1265. // object to smooth things out, making it dimmer as the disc size exceeds the
  1266. // max disc size.
  1267. void Renderer::renderObjectAsPoint(Point3f position,
  1268.                                    float radius,
  1269.                                    float appMag,
  1270.                                    float _faintestMag,
  1271.                                    float discSizeInPixels,
  1272.                                    Color color,
  1273.                                    const Quatf& cameraOrientation,
  1274.                                    bool useHalos,
  1275.                                    bool emissive)
  1276. {
  1277.     float maxDiscSize = (starStyle == ScaledDiscStars) ? MaxScaledDiscStarSize : 1.0f;
  1278.     float maxBlendDiscSize = maxDiscSize + 3.0f;
  1279.     bool useScaledDiscs = starStyle == ScaledDiscStars;
  1280.     if (discSizeInPixels < maxBlendDiscSize || useHalos)
  1281.     {
  1282.         float alpha = 1.0f;
  1283.         float fade = 1.0f;
  1284.         float size = BaseStarDiscSize;
  1285. #ifdef USE_HDR
  1286.         float fieldCorr = 2.0f * FOV/(fov + FOV);
  1287.         float satPoint = saturationMagNight * (1.0f + fieldCorr * fieldCorr);
  1288.         satPoint += brightPlus;
  1289. #else
  1290.         float satPoint = _faintestMag - (1.0f - brightnessBias) / brightnessScale;
  1291. #endif
  1292.         if (discSizeInPixels > maxDiscSize)
  1293.         {
  1294.             fade = (maxBlendDiscSize - discSizeInPixels) /
  1295.                 (maxBlendDiscSize - maxDiscSize);
  1296.             if (fade > 1)
  1297.                 fade = 1;
  1298.         }
  1299.         alpha = (_faintestMag - appMag) * brightnessScale * 2.0f + brightnessBias;
  1300.         float pointSize = size;
  1301.         float glareSize = 0.0f;
  1302.         float glareAlpha = 0.0f;
  1303.         if (useScaledDiscs)
  1304.         {
  1305.             if (alpha < 0.0f)
  1306.             {
  1307.                 alpha = 0.0f;
  1308.             }
  1309.             else if (alpha > 1.0f)
  1310.             {
  1311.                 float discScale = min(MaxScaledDiscStarSize, (float) pow(2.0f, 0.3f * (satPoint - appMag)));
  1312.                 pointSize *= max(1.0f, discScale);
  1313.                 glareAlpha = min(0.5f, discScale / 4.0f);
  1314.                 if (discSizeInPixels > MaxScaledDiscStarSize)
  1315.                 {
  1316.                     glareAlpha = min(glareAlpha,
  1317.                                      (MaxScaledDiscStarSize - discSizeInPixels) / MaxScaledDiscStarSize + 1.0f);
  1318.                 }
  1319.                 glareSize = pointSize * 3.0f;
  1320.                 alpha = 1.0f;
  1321.             }
  1322.         }
  1323.         else
  1324.         {
  1325.             if (alpha < 0.0f)
  1326.             {
  1327.                 alpha = 0.0f;
  1328.             }
  1329.             else if (alpha > 1.0f)
  1330.             {
  1331.                 float discScale = min(100.0f, satPoint - appMag + 2.0f);
  1332.                 glareAlpha = min(GlareOpacity, (discScale - 2.0f) / 4.0f);
  1333.                 glareSize = pointSize * discScale * 2.0f ;
  1334.                 if (emissive)
  1335.                     glareSize = max(glareSize, pointSize * discSizeInPixels * 3.0f);
  1336.             }
  1337.         }
  1338.         alpha *= fade;
  1339.         if (!emissive)
  1340.         {
  1341.             glareSize = max(glareSize, pointSize * discSizeInPixels * 3.0f);
  1342.             glareAlpha *= fade;
  1343.         }
  1344.         Mat3f m = cameraOrientation.toMatrix3();
  1345.         Point3f center = position;
  1346.         // Offset the glare sprite so that it lies in front of the object
  1347.         Vec3f direction(center.x, center.y, center.z);
  1348.         direction.normalize();
  1349.         // Position the sprite on the the line between the viewer and the
  1350.         // object, and on a plane normal to the view direction.
  1351.         center = center + direction * (radius / ((Vec3f(0, 0, 1.0f) * m) * direction));
  1352.         glEnable(GL_DEPTH_TEST);
  1353. #if !defined(NO_MAX_POINT_SIZE)
  1354.         // TODO: OpenGL appears to limit the max point size unless we
  1355.         // actually set up a shader that writes the pointsize values. To get
  1356.         // around this, we'll use billboards.
  1357.         Vec3f v0 = Vec3f(-1, -1, 0) * m;
  1358.         Vec3f v1 = Vec3f( 1, -1, 0) * m;
  1359.         Vec3f v2 = Vec3f( 1,  1, 0) * m;
  1360.         Vec3f v3 = Vec3f(-1,  1, 0) * m;
  1361.         float distanceAdjust = pixelSize * center.distanceFromOrigin() * 0.5f;
  1362.         if (starStyle == PointStars)
  1363.         {
  1364.             glDisable(GL_TEXTURE_2D);
  1365.             glBegin(GL_POINTS);
  1366.             glColor(color, alpha);
  1367.             glVertex(center);
  1368.             glEnd();
  1369.             glEnable(GL_TEXTURE_2D);
  1370.         }
  1371.         else
  1372.         {
  1373.             gaussianDiscTex->bind();
  1374.             pointSize *= distanceAdjust;
  1375.             glBegin(GL_QUADS);
  1376.             glColor(color, alpha);
  1377.             glTexCoord2f(0, 1);
  1378.             glVertex(center + (v0 * pointSize));
  1379.             glTexCoord2f(1, 1);
  1380.             glVertex(center + (v1 * pointSize));
  1381.             glTexCoord2f(1, 0);
  1382.             glVertex(center + (v2 * pointSize));
  1383.             glTexCoord2f(0, 0);
  1384.             glVertex(center + (v3 * pointSize));
  1385.             glEnd();
  1386.         }
  1387.         // If the object is brighter than magnitude 1, add a halo around it to
  1388.         // make it appear more brilliant.  This is a hack to compensate for the
  1389.         // limited dynamic range of monitors.
  1390.         //
  1391.         // TODO: Stars look fine but planets look unrealistically bright
  1392.         // with halos.
  1393.         if (useHalos && glareAlpha > 0.0f)
  1394.         {
  1395.             gaussianGlareTex->bind();
  1396.             glareSize *= distanceAdjust;
  1397.             glBegin(GL_QUADS);
  1398.             glColor(color, glareAlpha);
  1399.             glTexCoord2f(0, 1);
  1400.             glVertex(center + (v0 * glareSize));
  1401.             glTexCoord2f(1, 1);
  1402.             glVertex(center + (v1 * glareSize));
  1403.             glTexCoord2f(1, 0);
  1404.             glVertex(center + (v2 * glareSize));
  1405.             glTexCoord2f(0, 0);
  1406.             glVertex(center + (v3 * glareSize));
  1407.             glEnd();
  1408.         }
  1409. #else
  1410.         // Disabled because of point size limits
  1411.         glEnable(GL_POINT_SPRITE_ARB);
  1412.         glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
  1413.         gaussianDiscTex->bind();
  1414.         glColor(color, alpha);
  1415.         glPointSize(pointSize);
  1416.         glBegin(GL_POINTS);
  1417.         glVertex(center);
  1418.         glEnd();
  1419.         // If the object is brighter than magnitude 1, add a halo around it to
  1420.         // make it appear more brilliant.  This is a hack to compensate for the
  1421.         // limited dynamic range of monitors.
  1422.         //
  1423.         // TODO: Stars look fine but planets look unrealistically bright
  1424.         // with halos.
  1425.         if (useHalos && glareAlpha > 0.0f)
  1426.         {
  1427.             gaussianGlareTex->bind();
  1428.             glColor(color, glareAlpha);
  1429.             glPointSize(glareSize);
  1430.             glBegin(GL_POINTS);
  1431.             glVertex(center);
  1432.             glEnd();
  1433.         }
  1434.         glDisable(GL_POINT_SPRITE_ARB);
  1435.         glDisable(GL_DEPTH_TEST);
  1436. #endif // NO_MAX_POINT_SIZE
  1437.     }
  1438. }
  1439. static void renderBumpMappedMesh(const GLContext& context,
  1440.                                  Texture& baseTexture,
  1441.                                  Texture& bumpTexture,
  1442.                                  Vec3f lightDirection,
  1443.                                  Quatf orientation,
  1444.                                  Color ambientColor,
  1445.                                  const Frustum& frustum,
  1446.                                  float lod)
  1447. {
  1448.     // We're doing our own per-pixel lighting, so disable GL's lighting
  1449.     glDisable(GL_LIGHTING);
  1450.     // Render the base texture on the first pass . . .  The color
  1451.     // should have already been set up by the caller.
  1452.     g_lodSphere->render(context,
  1453.                         LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  1454.                         frustum, lod,
  1455.                         &baseTexture);
  1456.     // The 'default' light vector for the bump map is (0, 0, 1).  Determine
  1457.     // a rotation transformation that will move the sun direction to
  1458.     // this vector.
  1459.     Quatf lightOrientation = Quatf::vecToVecRotation(Vec3f(0.0f, 0.0f, 1.0f), lightDirection);
  1460.     glEnable(GL_BLEND);
  1461.     glBlendFunc(GL_DST_COLOR, GL_ZERO);
  1462.     // Set up the bump map with one directional light source
  1463.     SetupCombinersBumpMap(bumpTexture, *normalizationTex, ambientColor);
  1464.     // The second set texture coordinates will contain the light
  1465.     // direction in tangent space.  We'll generate the texture coordinates
  1466.     // from the surface normals using GL_NORMAL_MAP_EXT and then
  1467.     // use the texture matrix to rotate them into tangent space.
  1468.     // This method of generating tangent space light direction vectors
  1469.     // isn't as general as transforming the light direction by an
  1470.     // orthonormal basis for each mesh vertex, but it works well enough
  1471.     // for spheres illuminated by directional light sources.
  1472.     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
  1473.     // Set up GL_NORMAL_MAP_EXT texture coordinate generation.  This
  1474.     // mode is part of the cube map extension.
  1475.     glEnable(GL_TEXTURE_GEN_R);
  1476.     glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
  1477.     glEnable(GL_TEXTURE_GEN_S);
  1478.     glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
  1479.     glEnable(GL_TEXTURE_GEN_T);
  1480.     glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
  1481.     // Set up the texture transformation--the light direction and the
  1482.     // viewer orientation both need to be considered.
  1483.     glMatrixMode(GL_TEXTURE);
  1484.     glScalef(-1.0f, 1.0f, 1.0f);
  1485.     glRotate(lightOrientation * ~orientation);
  1486.     glMatrixMode(GL_MODELVIEW);
  1487.     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  1488.     g_lodSphere->render(context,
  1489.                         LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  1490.                         frustum, lod,
  1491.                         &bumpTexture);
  1492.     // Reset the second texture unit
  1493.     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
  1494.     glMatrixMode(GL_TEXTURE);
  1495.     glLoadIdentity();
  1496.     glMatrixMode(GL_MODELVIEW);
  1497.     glDisable(GL_TEXTURE_GEN_R);
  1498.     glDisable(GL_TEXTURE_GEN_S);
  1499.     glDisable(GL_TEXTURE_GEN_T);
  1500.     DisableCombiners();
  1501.     glDisable(GL_BLEND);
  1502. }
  1503. static void renderSmoothMesh(const GLContext& context,
  1504.                              Texture& baseTexture,
  1505.                              Vec3f lightDirection,
  1506.                              Quatf orientation,
  1507.                              Color ambientColor,
  1508.                              float lod,
  1509.                              const Frustum& frustum,
  1510.                              bool invert = false)
  1511. {
  1512.     Texture* textures[4];
  1513.     // We're doing our own per-pixel lighting, so disable GL's lighting
  1514.     glDisable(GL_LIGHTING);
  1515.     // The 'default' light vector for the bump map is (0, 0, 1).  Determine
  1516.     // a rotation transformation that will move the sun direction to
  1517.     // this vector.
  1518.     Quatf lightOrientation = Quatf::vecToVecRotation(Vec3f(0.0f, 0.0f, 1.0f), lightDirection);
  1519.     SetupCombinersSmooth(baseTexture, *normalizationTex, ambientColor, invert);
  1520.     // The second set texture coordinates will contain the light
  1521.     // direction in tangent space.  We'll generate the texture coordinates
  1522.     // from the surface normals using GL_NORMAL_MAP_EXT and then
  1523.     // use the texture matrix to rotate them into tangent space.
  1524.     // This method of generating tangent space light direction vectors
  1525.     // isn't as general as transforming the light direction by an
  1526.     // orthonormal basis for each mesh vertex, but it works well enough
  1527.     // for spheres illuminated by directional light sources.
  1528.     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
  1529.     // Set up GL_NORMAL_MAP_EXT texture coordinate generation.  This
  1530.     // mode is part of the cube map extension.
  1531.     glEnable(GL_TEXTURE_GEN_R);
  1532.     glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
  1533.     glEnable(GL_TEXTURE_GEN_S);
  1534.     glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
  1535.     glEnable(GL_TEXTURE_GEN_T);
  1536.     glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
  1537.     // Set up the texture transformation--the light direction and the
  1538.     // viewer orientation both need to be considered.
  1539.     glMatrixMode(GL_TEXTURE);
  1540.     glRotate(lightOrientation * ~orientation);
  1541.     glMatrixMode(GL_MODELVIEW);
  1542.     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  1543.     textures[0] = &baseTexture;
  1544.     g_lodSphere->render(context,
  1545.                         LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  1546.                         frustum, lod,
  1547.                         textures, 1);
  1548.     // Reset the second texture unit
  1549.     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
  1550.     glMatrixMode(GL_TEXTURE);
  1551.     glLoadIdentity();
  1552.     glMatrixMode(GL_MODELVIEW);
  1553.     glDisable(GL_TEXTURE_GEN_R);
  1554.     glDisable(GL_TEXTURE_GEN_S);
  1555.     glDisable(GL_TEXTURE_GEN_T);
  1556.     DisableCombiners();
  1557. }
  1558. // Used to sort light sources in order of decreasing irradiance
  1559. struct LightIrradiancePredicate
  1560. {
  1561.     int unused;
  1562.     LightIrradiancePredicate() {};
  1563.     bool operator()(const DirectionalLight& l0,
  1564.                     const DirectionalLight& l1) const
  1565.     {
  1566.         return (l0.irradiance > l1.irradiance);
  1567.     }
  1568. };
  1569. void renderAtmosphere(const Atmosphere& atmosphere,
  1570.                       Point3f center,
  1571.                       float radius,
  1572.                       const Vec3f& sunDirection,
  1573.                       Color ambientColor,
  1574.                       float fade,
  1575.                       bool lit)
  1576. {
  1577.     if (atmosphere.height == 0.0f)
  1578.         return;
  1579.     glDepthMask(GL_FALSE);
  1580.     Vec3f eyeVec = center - Point3f(0.0f, 0.0f, 0.0f);
  1581.     double centerDist = eyeVec.length();
  1582.     // double surfaceDist = (double) centerDist - (double) radius;
  1583.     Vec3f normal = eyeVec;
  1584.     normal = normal / (float) centerDist;
  1585.     float tangentLength = (float) sqrt(square(centerDist) - square(radius));
  1586.     float atmRadius = tangentLength * radius / (float) centerDist;
  1587.     float atmOffsetFromCenter = square(radius) / (float) centerDist;
  1588.     Point3f atmCenter = center - atmOffsetFromCenter * normal;
  1589.     Vec3f uAxis, vAxis;
  1590.     if (abs(normal.x) < abs(normal.y) && abs(normal.x) < abs(normal.z))
  1591.     {
  1592.         uAxis = Vec3f(1, 0, 0) ^ normal;
  1593.         uAxis.normalize();
  1594.     }
  1595.     else if (abs(eyeVec.y) < abs(normal.z))
  1596.     {
  1597.         uAxis = Vec3f(0, 1, 0) ^ normal;
  1598.         uAxis.normalize();
  1599.     }
  1600.     else
  1601.     {
  1602.         uAxis = Vec3f(0, 0, 1) ^ normal;
  1603.         uAxis.normalize();
  1604.     }
  1605.     vAxis = uAxis ^ normal;
  1606.     float height = atmosphere.height / radius;
  1607.     glBegin(GL_QUAD_STRIP);
  1608.     int divisions = 180;
  1609.     for (int i = 0; i <= divisions; i++)
  1610.     {
  1611.         float theta = (float) i / (float) divisions * 2 * (float) PI;
  1612.         Vec3f v = (float) cos(theta) * uAxis + (float) sin(theta) * vAxis;
  1613.         Point3f base = atmCenter + v * atmRadius;
  1614.         Vec3f toCenter = base - center;
  1615.         float cosSunAngle = (toCenter * sunDirection) / radius;
  1616.         float brightness = 1.0f;
  1617.         float botColor[3];
  1618.         float topColor[3];
  1619.         botColor[0] = atmosphere.lowerColor.red();
  1620.         botColor[1] = atmosphere.lowerColor.green();
  1621.         botColor[2] = atmosphere.lowerColor.blue();
  1622.         topColor[0] = atmosphere.upperColor.red();
  1623.         topColor[1] = atmosphere.upperColor.green();
  1624.         topColor[2] = atmosphere.upperColor.blue();
  1625.         if (cosSunAngle < 0.2f && lit)
  1626.         {
  1627.             if (cosSunAngle < -0.2f)
  1628.             {
  1629.                 brightness = 0;
  1630.             }
  1631.             else
  1632.             {
  1633.                 float t = (0.2f + cosSunAngle) * 2.5f;
  1634.                 brightness = t;
  1635.                 botColor[0] = Mathf::lerp(t, 1.0f, botColor[0]);
  1636.                 botColor[1] = Mathf::lerp(t, 0.3f, botColor[1]);
  1637.                 botColor[2] = Mathf::lerp(t, 0.0f, botColor[2]);
  1638.                 topColor[0] = Mathf::lerp(t, 1.0f, topColor[0]);
  1639.                 topColor[1] = Mathf::lerp(t, 0.3f, topColor[1]);
  1640.                 topColor[2] = Mathf::lerp(t, 0.0f, topColor[2]);
  1641.             }
  1642.         }
  1643.         glColor4f(botColor[0], botColor[1], botColor[2],
  1644.                   0.85f * fade * brightness + ambientColor.red());
  1645.         glVertex(base - toCenter * height * 0.05f);
  1646.         glColor4f(topColor[0], topColor[1], topColor[2], 0.0f);
  1647.         glVertex(base + toCenter * height);
  1648.     }
  1649.     glEnd();
  1650. }
  1651. static Vec3f ellipsoidTangent(const Vec3f& recipSemiAxes,
  1652.                               const Vec3f& w,
  1653.                               const Vec3f& e,
  1654.                               const Vec3f& e_,
  1655.                               float ee)
  1656. {
  1657.     // We want to find t such that -E(1-t) + Wt is the direction of a ray
  1658.     // tangent to the ellipsoid.  A tangent ray will intersect the ellipsoid
  1659.     // at exactly one point.  Finding the intersection between a ray and an
  1660.     // ellipsoid ultimately requires using the quadratic formula, which has
  1661.     // one solution when the discriminant (b^2 - 4ac) is zero.  The code below
  1662.     // computes the value of t that results in a discriminant of zero.
  1663.     Vec3f w_(w.x * recipSemiAxes.x, w.y * recipSemiAxes.y, w.z * recipSemiAxes.z);
  1664.     float ww = w_ * w_;
  1665.     float ew = w_ * e_;
  1666.     // Before elimination of terms:
  1667.     // float a =  4 * square(ee + ew) - 4 * (ee + 2 * ew + ww) * (ee - 1.0f);
  1668.     // float b = -8 * ee * (ee + ew)  - 4 * (-2 * (ee + ew) * (ee - 1.0f));
  1669.     // float c =  4 * ee * ee         - 4 * (ee * (ee - 1.0f));
  1670.     // Simplify the below expression and eliminate the ee^2 terms; this
  1671.     // prevents precision errors, as ee tends to be a very large value.
  1672.     //T a =  4 * square(ee + ew) - 4 * (ee + 2 * ew + ww) * (ee - 1);
  1673.     //float a =  4 * square(ee + ew) - 4 * (ee + 2 * ew + ww) * (ee - 1.0f);
  1674.     float a =  4 * (square(ew) - ee * ww + ee + 2 * ew + ww);
  1675.     float b = -8 * (ee + ew);
  1676.     float c =  4 * ee;
  1677.     float t = 0.0f;
  1678.     float discriminant = b * b - 4 * a * c;
  1679.     if (discriminant < 0.0f)
  1680.         t = (-b + (float) sqrt(-discriminant)) / (2 * a); // Bad!
  1681.     else
  1682.         t = (-b + (float) sqrt(discriminant)) / (2 * a);
  1683.     // V is the direction vector.  We now need the point of intersection,
  1684.     // which we obtain by solving the quadratic equation for the ray-ellipse
  1685.     // intersection.  Since we already know that the discriminant is zero,
  1686.     // the solution is just -b/2a
  1687.     Vec3f v = -e * (1 - t) + w * t;
  1688.     Vec3f v_(v.x * recipSemiAxes.x, v.y * recipSemiAxes.y, v.z * recipSemiAxes.z);
  1689.     float a1 = v_ * v_;
  1690.     float b1 = 2.0f * v_ * e_;
  1691.     float t1 = -b1 / (2 * a1);
  1692.     return e + v * t1;
  1693. }
  1694. void Renderer::renderEllipsoidAtmosphere(const Atmosphere& atmosphere,
  1695.                                          Point3f center,
  1696.                                          const Quatf& orientation,
  1697.                                          Vec3f semiAxes,
  1698.                                          const Vec3f& sunDirection,
  1699.                                          const LightingState& ls,
  1700.                                          float pixSize,
  1701.                                          bool lit)
  1702. {
  1703.     if (atmosphere.height == 0.0f)
  1704.         return;
  1705.     glDepthMask(GL_FALSE);
  1706.     // Gradually fade in the atmosphere if it's thickness on screen is just
  1707.     // over one pixel.
  1708.     float fade = clamp(pixSize - 2);
  1709.     Mat3f rot = orientation.toMatrix3();
  1710.     Mat3f irot = conjugate(orientation).toMatrix3();
  1711.     Point3f eyePos(0.0f, 0.0f, 0.0f);
  1712.     float radius = max(semiAxes.x, max(semiAxes.y, semiAxes.z));
  1713.     Vec3f eyeVec = center - eyePos;
  1714.     eyeVec = eyeVec * irot;
  1715.     double centerDist = eyeVec.length();
  1716.     float height = atmosphere.height / radius;
  1717.     Vec3f recipSemiAxes(1.0f / semiAxes.x, 1.0f / semiAxes.y, 1.0f / semiAxes.z);
  1718.     Vec3f recipAtmSemiAxes = recipSemiAxes / (1.0f + height);
  1719.     Mat3f A = Mat3f::scaling(recipAtmSemiAxes);
  1720.     Mat3f A1 = Mat3f::scaling(recipSemiAxes);
  1721.     // ellipDist is not the true distance from the surface unless the
  1722.     // planet is spherical.  Computing the true distance requires finding
  1723.     // the roots of a sixth degree polynomial, and isn't actually what we
  1724.     // want anyhow since the atmosphere region is just the planet ellipsoid
  1725.     // multiplied by a uniform scale factor.  The value that we do compute
  1726.     // is the distance to the surface along a line from the eye position to
  1727.     // the center of the ellipsoid.
  1728.     float ellipDist = (float) sqrt((eyeVec * A1) * (eyeVec * A1)) - 1.0f;
  1729.     bool within = ellipDist < height;
  1730.     // Adjust the tesselation of the sky dome/ring based on distance from the
  1731.     // planet surface.
  1732.     int nSlices = MaxSkySlices;
  1733.     if (ellipDist < 0.25f)
  1734.     {
  1735.         nSlices = MinSkySlices + max(0, (int) ((ellipDist / 0.25f) * (MaxSkySlices - MinSkySlices)));
  1736.         nSlices &= ~1;
  1737.     }
  1738.     int nRings = min(1 + (int) pixSize / 5, 6);
  1739.     int nHorizonRings = nRings;
  1740.     if (within)
  1741.         nRings += 12;
  1742.     float horizonHeight = height;
  1743.     if (within)
  1744.     {
  1745.         if (ellipDist <= 0.0f)
  1746.             horizonHeight = 0.0f;
  1747.         else
  1748.             horizonHeight *= max((float) pow(ellipDist / height, 0.33f), 0.001f);
  1749.     }
  1750.     Vec3f e = -eyeVec;
  1751.     Vec3f e_(e.x * recipSemiAxes.x, e.y * recipSemiAxes.y, e.z * recipSemiAxes.z);
  1752.     float ee = e_ * e_;
  1753.     // Compute the cosine of the altitude of the sun.  This is used to compute
  1754.     // the degree of sunset/sunrise coloration.
  1755.     float cosSunAltitude = 0.0f;
  1756.     {
  1757.         // Check for a sun either directly behind or in front of the viewer
  1758.         float cosSunAngle = (float) ((sunDirection * e) / centerDist);
  1759.         if (cosSunAngle < -1.0f + 1.0e-6f)
  1760.         {
  1761.             cosSunAltitude = 0.0f;
  1762.         }
  1763.         else if (cosSunAngle > 1.0f - 1.0e-6f)
  1764.         {
  1765.             cosSunAltitude = 0.0f;
  1766.         }
  1767.         else
  1768.         {
  1769.             Point3f tangentPoint = center +
  1770.                 ellipsoidTangent(recipSemiAxes,
  1771.                                  (-sunDirection * irot) * (float) centerDist,
  1772.                                  e, e_, ee) * rot;
  1773.             Vec3f tangentDir = tangentPoint - eyePos;
  1774.             tangentDir.normalize();
  1775.             cosSunAltitude = sunDirection * tangentDir;
  1776.         }
  1777.     }
  1778.     Vec3f normal = eyeVec;
  1779.     normal = normal / (float) centerDist;
  1780.     Vec3f uAxis, vAxis;
  1781.     if (abs(normal.x) < abs(normal.y) && abs(normal.x) < abs(normal.z))
  1782.     {
  1783.         uAxis = Vec3f(1, 0, 0) ^ normal;
  1784.         uAxis.normalize();
  1785.     }
  1786.     else if (abs(eyeVec.y) < abs(normal.z))
  1787.     {
  1788.         uAxis = Vec3f(0, 1, 0) ^ normal;
  1789.         uAxis.normalize();
  1790.     }
  1791.     else
  1792.     {
  1793.         uAxis = Vec3f(0, 0, 1) ^ normal;
  1794.         uAxis.normalize();
  1795.     }
  1796.     vAxis = uAxis ^ normal;
  1797.     // Compute the contour of the ellipsoid
  1798.     int i;
  1799.     for (i = 0; i <= nSlices; i++)
  1800.     {
  1801.         // We want rays with an origin at the eye point and tangent to the the
  1802.         // ellipsoid.
  1803.         float theta = (float) i / (float) nSlices * 2 * (float) PI;
  1804.         Vec3f w = (float) cos(theta) * uAxis + (float) sin(theta) * vAxis;
  1805.         w = w * (float) centerDist;
  1806.         Vec3f toCenter = ellipsoidTangent(recipSemiAxes, w, e, e_, ee);
  1807.         skyContour[i].v = toCenter * rot;
  1808.         skyContour[i].centerDist = skyContour[i].v.length();
  1809.         skyContour[i].eyeDir = skyContour[i].v + (center - eyePos);
  1810.         skyContour[i].eyeDist = skyContour[i].eyeDir.length();
  1811.         skyContour[i].eyeDir.normalize();
  1812.         float skyCapDist = (float) sqrt(square(skyContour[i].eyeDist) +
  1813.                                         square(horizonHeight * radius));
  1814.         skyContour[i].cosSkyCapAltitude = skyContour[i].eyeDist /
  1815.             skyCapDist;
  1816.     }
  1817.     Vec3f botColor(atmosphere.lowerColor.red(),
  1818.                    atmosphere.lowerColor.green(),
  1819.                    atmosphere.lowerColor.blue());
  1820.     Vec3f topColor(atmosphere.upperColor.red(),
  1821.                    atmosphere.upperColor.green(),
  1822.                    atmosphere.upperColor.blue());
  1823.     Vec3f sunsetColor(atmosphere.sunsetColor.red(),
  1824.                       atmosphere.sunsetColor.green(),
  1825.                       atmosphere.sunsetColor.blue());
  1826.     if (within)
  1827.     {
  1828.         Vec3f skyColor(atmosphere.skyColor.red(),
  1829.                        atmosphere.skyColor.green(),
  1830.                        atmosphere.skyColor.blue());
  1831.         if (ellipDist < 0.0f)
  1832.             topColor = skyColor;
  1833.         else
  1834.             topColor = skyColor + (topColor - skyColor) * (ellipDist / height);
  1835.     }
  1836.     if (ls.nLights == 0 && lit)
  1837.     {
  1838.         Vec3f black(0.0f, 0.0f, 0.0f);
  1839.         botColor = topColor = sunsetColor = black;
  1840.     }
  1841.     Vec3f zenith = (skyContour[0].v + skyContour[nSlices / 2].v);
  1842.     zenith.normalize();
  1843.     zenith *= skyContour[0].centerDist * (1.0f + horizonHeight * 2.0f);
  1844.     float minOpacity = within ? (1.0f - ellipDist / height) * 0.75f : 0.0f;
  1845.     float sunset = cosSunAltitude < 0.9f ? 0.0f : (cosSunAltitude - 0.9f) * 10.0f;
  1846.     // Build the list of vertices
  1847.     SkyVertex* vtx = skyVertices;
  1848.     for (i = 0; i <= nRings; i++)
  1849.     {
  1850.         float h = min(1.0f, (float) i / (float) nHorizonRings);
  1851.         float hh = (float) sqrt(h);
  1852.         float u = i <= nHorizonRings ? 0.0f :
  1853.             (float) (i - nHorizonRings) / (float) (nRings - nHorizonRings);
  1854.         float r = Mathf::lerp(h, 1.0f - (horizonHeight * 0.05f), 1.0f + horizonHeight);
  1855.         float atten = 1.0f - hh;
  1856.         for (int j = 0; j < nSlices; j++)
  1857.         {
  1858.             Vec3f v;
  1859.             if (i <= nHorizonRings)
  1860.                 v = skyContour[j].v * r;
  1861.             else
  1862.                 v = (skyContour[j].v * (1.0f - u) + zenith * u) * r;
  1863.             Point3f p = center + v;
  1864.             Vec3f viewDir(p.x, p.y, p.z);
  1865.             viewDir.normalize();
  1866.             float cosSunAngle = viewDir * sunDirection;
  1867.             float cosAltitude = viewDir * skyContour[j].eyeDir;
  1868.             float brightness = 1.0f;
  1869.             float coloration = 0.0f;
  1870.             if (lit)
  1871.             {
  1872.                 if (sunset > 0.0f && cosSunAngle > 0.7f && cosAltitude > 0.98f)
  1873.                 {
  1874.                     coloration =  (1.0f / 0.30f) * (cosSunAngle - 0.70f);
  1875.                     coloration *= 50.0f * (cosAltitude - 0.98f);
  1876.                     coloration *= sunset;
  1877.                 }
  1878.                 cosSunAngle = (skyContour[j].v * sunDirection) / skyContour[j].centerDist;
  1879.                 if (cosSunAngle > -0.2f)
  1880.                 {
  1881.                     if (cosSunAngle < 0.3f)
  1882.                         brightness = (cosSunAngle + 0.2f) * 2.0f;
  1883.                     else
  1884.                         brightness = 1.0f;
  1885.                 }
  1886.                 else
  1887.                 {
  1888.                     brightness = 0.0f;
  1889.                 }
  1890.             }
  1891.             vtx->x = p.x;
  1892.             vtx->y = p.y;
  1893.             vtx->z = p.z;
  1894. #if 0
  1895.             // Better way of generating sky color gradients--based on
  1896.             // altitude angle.
  1897.             if (!within)
  1898.             {
  1899.                 hh = (1.0f - cosAltitude) / (1.0f - skyContour[j].cosSkyCapAltitude);
  1900.             }
  1901.             else
  1902.             {
  1903.                 float top = pow((ellipDist / height), 0.125f) * skyContour[j].cosSkyCapAltitude;
  1904.                 if (cosAltitude < top)
  1905.                     hh = 1.0f;
  1906.                 else
  1907.                     hh = (1.0f - cosAltitude) / (1.0f - top);
  1908.             }
  1909.             hh = sqrt(hh);
  1910.             //hh = (float) pow(hh, 0.25f);
  1911. #endif
  1912.             atten = 1.0f - hh;
  1913.             Vec3f color = (1.0f - hh) * botColor + hh * topColor;
  1914.             brightness *= minOpacity + (1.0f - minOpacity) * fade * atten;
  1915.             if (coloration != 0.0f)
  1916.                 color = (1.0f - coloration) * color + coloration * sunsetColor;
  1917. #ifdef HDR_COMPRESS
  1918.             brightness *= 0.5f;
  1919. #endif
  1920.             Color(brightness * color.x,
  1921.                   brightness * color.y,
  1922.                   brightness * color.z,
  1923.                   fade * (minOpacity + (1.0f - minOpacity)) * atten).get(vtx->color);
  1924.             vtx++;
  1925.         }
  1926.     }
  1927.     // Create the index list
  1928.     int index = 0;
  1929.     for (i = 0; i < nRings; i++)
  1930.     {
  1931.         int baseVertex = i * nSlices;
  1932.         for (int j = 0; j < nSlices; j++)
  1933.         {
  1934.             skyIndices[index++] = baseVertex + j;
  1935.             skyIndices[index++] = baseVertex + nSlices + j;
  1936.         }
  1937.         skyIndices[index++] = baseVertex;
  1938.         skyIndices[index++] = baseVertex + nSlices;
  1939.     }
  1940.     glEnableClientState(GL_VERTEX_ARRAY);
  1941.     glVertexPointer(3, GL_FLOAT, sizeof(SkyVertex), &skyVertices[0].x);
  1942.     glEnableClientState(GL_COLOR_ARRAY);
  1943.     glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(SkyVertex),
  1944.                    static_cast<void*>(&skyVertices[0].color));
  1945.     for (i = 0; i < nRings; i++)
  1946.     {
  1947.         glDrawElements(GL_QUAD_STRIP,
  1948.                        (nSlices + 1) * 2,
  1949.                        GL_UNSIGNED_INT,
  1950.                        &skyIndices[(nSlices + 1) * 2 * i]);
  1951.     }
  1952.     glDisableClientState(GL_COLOR_ARRAY);
  1953. }
  1954. void renderCompass(Point3f center,
  1955.                    const Quatf& orientation,
  1956.                    Vec3f semiAxes,
  1957.                    float pixelSize)
  1958. {
  1959.     Mat3f rot = orientation.toMatrix3();
  1960.     Mat3f irot = conjugate(orientation).toMatrix3();
  1961.     Point3f eyePos(0.0f, 0.0f, 0.0f);
  1962.     float radius = max(semiAxes.x, max(semiAxes.y, semiAxes.z));
  1963.     Vec3f eyeVec = center - eyePos;
  1964.     eyeVec = eyeVec * irot;
  1965.     double centerDist = eyeVec.length();
  1966.     float height = 1.0f / radius;
  1967.     Vec3f recipSemiAxes(1.0f / semiAxes.x,
  1968.                         1.0f / semiAxes.y,
  1969.                         1.0f / semiAxes.z);
  1970.     Vec3f recipAtmSemiAxes = recipSemiAxes / (1.0f + height);
  1971.     Mat3f A = Mat3f::scaling(recipAtmSemiAxes);
  1972.     Mat3f A1 = Mat3f::scaling(recipSemiAxes);
  1973.     const int nCompassPoints = 16;
  1974.     Vec3f compassPoints[nCompassPoints];
  1975.     // ellipDist is not the true distance from the surface unless the
  1976.     // planet is spherical.  Computing the true distance requires finding
  1977.     // the roots of a sixth degree polynomial, and isn't actually what we
  1978.     // want anyhow since the atmosphere region is just the planet ellipsoid
  1979.     // multiplied by a uniform scale factor.  The value that we do compute
  1980.     // is the distance to the surface along a line from the eye position to
  1981.     // the center of the ellipsoid.
  1982.     /*float ellipDist = (float) sqrt((eyeVec * A1) * (eyeVec * A1)) - 1.0f; Unused*/
  1983.     Vec3f e = -eyeVec;
  1984.     Vec3f e_(e.x * recipSemiAxes.x, e.y * recipSemiAxes.y, e.z * recipSemiAxes.z);
  1985.     float ee = e_ * e_;
  1986.     Vec3f normal = eyeVec;
  1987.     normal = normal / (float) centerDist;
  1988.     Vec3f uAxis, vAxis;
  1989.     Vec3f northPole(0.0f, 1.0f, 0.0f);
  1990.     vAxis = normal ^ northPole;
  1991.     vAxis.normalize();
  1992.     uAxis = vAxis ^ normal;
  1993.     // Compute the compass points
  1994.     int i;
  1995.     for (i = 0; i < nCompassPoints; i++)
  1996.     {
  1997.         // We want rays with an origin at the eye point and tangent to the the
  1998.         // ellipsoid.
  1999.         float theta = (float) i / (float) nCompassPoints * 2 * (float) PI;
  2000.         Vec3f w = (float) cos(theta) * uAxis + (float) sin(theta) * vAxis;
  2001.         w = w * (float) centerDist;
  2002.         Vec3f toCenter = ellipsoidTangent(recipSemiAxes, w, e, e_, ee);
  2003.         compassPoints[i] = toCenter * rot;
  2004.     }
  2005.     glColor(compassColor);
  2006.     glBegin(GL_LINES);
  2007.     glDisable(GL_LIGHTING);
  2008.     for (i = 0; i < nCompassPoints; i++)
  2009.     {
  2010.         float distance = (center + compassPoints[i]).distanceFromOrigin();
  2011.         float length = distance * pixelSize * 8.0f;
  2012.         if (i % 4 == 0)
  2013.             length *= 3.0f;
  2014.         else if (i % 2 == 0)
  2015.             length *= 2.0f;
  2016.         glVertex(center + compassPoints[i]);
  2017.         glVertex(center + compassPoints[i] * (1.0f + length));
  2018.     }
  2019.     glEnd();
  2020. }
  2021. static void setupNightTextureCombine()
  2022. {
  2023.     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
  2024.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_EXT);
  2025.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_ONE_MINUS_SRC_COLOR);
  2026.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
  2027.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
  2028.     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
  2029. }
  2030. static void setupBumpTexenv()
  2031. {
  2032.     // Set up the texenv_combine extension to do DOT3 bump mapping.
  2033.     // No support for ambient light yet.
  2034.     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
  2035.     // The primary color contains the light direction in surface
  2036.     // space, and texture0 is a normal map.  The lighting is
  2037.     // calculated by computing the dot product.
  2038.     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_DOT3_RGB_ARB);
  2039.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_EXT);
  2040.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
  2041.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
  2042.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
  2043.     // In the final stage, modulate the lighting value by the
  2044.     // base texture color.
  2045.     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
  2046.     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
  2047.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
  2048.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
  2049.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
  2050.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
  2051.     glEnable(GL_TEXTURE_2D);
  2052.     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  2053. }
  2054. #if 0
  2055. static void setupBumpTexenvAmbient(Color ambientColor)
  2056. {
  2057.     float texenvConst[4];
  2058.     texenvConst[0] = ambientColor.red();
  2059.     texenvConst[1] = ambientColor.green();
  2060.     texenvConst[2] = ambientColor.blue();
  2061.     texenvConst[3] = ambientColor.alpha();
  2062.     // Set up the texenv_combine extension to do DOT3 bump mapping.
  2063.     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
  2064.     // The primary color contains the light direction in surface
  2065.     // space, and texture0 is a normal map.  The lighting is
  2066.     // calculated by computing the dot product.
  2067.     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  2068.     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_DOT3_RGB_ARB);
  2069.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_EXT);
  2070.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
  2071.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
  2072.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
  2073.     // Add in the ambient color
  2074.     glx::glActiveTextureARB(GL_TEXTURE1_ARB);
  2075.     glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, texenvConst);
  2076.     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
  2077.     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
  2078.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
  2079.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
  2080.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_CONSTANT_EXT);
  2081.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
  2082.     glEnable(GL_TEXTURE_2D);
  2083.     // In the final stage, modulate the lighting value by the
  2084.     // base texture color.
  2085.     glx::glActiveTextureARB(GL_TEXTURE2_ARB);
  2086.     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
  2087.     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
  2088.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
  2089.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
  2090.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
  2091.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
  2092.     glEnable(GL_TEXTURE_2D);
  2093.     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  2094. }
  2095. #endif
  2096. static void setupTexenvAmbient(Color ambientColor)
  2097. {
  2098.     float texenvConst[4];
  2099.     texenvConst[0] = ambientColor.red();
  2100.     texenvConst[1] = ambientColor.green();
  2101.     texenvConst[2] = ambientColor.blue();
  2102.     texenvConst[3] = ambientColor.alpha();
  2103.     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
  2104.     // The primary color contains the light direction in surface
  2105.     // space, and texture0 is a normal map.  The lighting is
  2106.     // calculated by computing the dot product.
  2107.     glx::glActiveTextureARB(GL_TEXTURE0_ARB);
  2108.     glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, texenvConst);
  2109.     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
  2110.     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
  2111.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
  2112.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
  2113.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_CONSTANT_EXT);
  2114.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
  2115.     glEnable(GL_TEXTURE_2D);
  2116. }
  2117. static void setupTexenvGlossMapAlpha()
  2118. {
  2119.     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
  2120.     glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
  2121.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_EXT);
  2122.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
  2123.     glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
  2124.     glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_ALPHA);
  2125. }
  2126. static void setLightParameters_VP(VertexProcessor& vproc,
  2127.                                   const LightingState& ls,
  2128.                                   Color materialDiffuse,
  2129.                                   Color materialSpecular)
  2130. {
  2131.     Vec3f diffuseColor(materialDiffuse.red(),
  2132.                        materialDiffuse.green(),
  2133.                        materialDiffuse.blue());
  2134. #ifdef HDR_COMPRESS
  2135.     Vec3f specularColor(materialSpecular.red()   * 0.5f,
  2136.                         materialSpecular.green() * 0.5f,
  2137.                         materialSpecular.blue()  * 0.5f);
  2138. #else
  2139.     Vec3f specularColor(materialSpecular.red(),
  2140.                         materialSpecular.green(),
  2141.                         materialSpecular.blue());
  2142. #endif
  2143.     for (unsigned int i = 0; i < ls.nLights; i++)
  2144.     {
  2145.         const DirectionalLight& light = ls.lights[i];
  2146.         Vec3f lightColor = Vec3f(light.color.red(),
  2147.                                  light.color.green(),
  2148.                                  light.color.blue()) * light.irradiance;
  2149.         Vec3f diffuse(diffuseColor.x * lightColor.x,
  2150.                       diffuseColor.y * lightColor.y,
  2151.                       diffuseColor.z * lightColor.z);
  2152.         Vec3f specular(specularColor.x * lightColor.x,
  2153.                        specularColor.y * lightColor.y,
  2154.                        specularColor.z * lightColor.z);
  2155.         // Just handle two light sources for now
  2156.         if (i == 0)
  2157.         {
  2158.             vproc.parameter(vp::LightDirection0, ls.lights[0].direction_obj);
  2159.             vproc.parameter(vp::DiffuseColor0, diffuse);
  2160.             vproc.parameter(vp::SpecularColor0, specular);
  2161.         }
  2162.         else if (i == 1)
  2163.         {
  2164.             vproc.parameter(vp::LightDirection1, ls.lights[1].direction_obj);
  2165.             vproc.parameter(vp::DiffuseColor1, diffuse);
  2166.             vproc.parameter(vp::SpecularColor1, specular);
  2167.         }
  2168.     }
  2169. }
  2170. static void renderModelDefault(Geometry* geometry,
  2171.                                const RenderInfo& ri,
  2172.                                bool lit,
  2173.                                ResourceHandle texOverride)
  2174. {
  2175.     FixedFunctionRenderContext rc;
  2176.     Mesh::Material m;
  2177.     rc.setLighting(lit);
  2178.     if (ri.baseTex == NULL)
  2179.     {
  2180.         glDisable(GL_TEXTURE_2D);
  2181.     }
  2182.     else
  2183.     {
  2184.         glEnable(GL_TEXTURE_2D);
  2185.         ri.baseTex->bind();
  2186.     }
  2187.     glColor(ri.color);
  2188.     if (ri.baseTex != NULL)
  2189.     {
  2190.         m.diffuse = ri.color;
  2191.         m.specular = ri.specularColor;
  2192.         m.specularPower = ri.specularPower;
  2193.         m.maps[Mesh::DiffuseMap] = texOverride;
  2194.         rc.setMaterial(&m);
  2195.         rc.lock();
  2196.     }
  2197.     geometry->render(rc);
  2198.     if (geometry->usesTextureType(Mesh::EmissiveMap))
  2199.     {
  2200.         glDisable(GL_LIGHTING);
  2201.         glEnable(GL_BLEND);
  2202.         glBlendFunc(GL_ONE, GL_ONE);
  2203.         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  2204.         rc.setRenderPass(RenderContext::EmissivePass);
  2205.         rc.setMaterial(NULL);
  2206.         geometry->render(rc);
  2207.         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  2208.     }
  2209.     // Reset the material
  2210.     float black[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
  2211.     float zero = 0.0f;
  2212.     glColor4fv(black);
  2213.     glMaterialfv(GL_FRONT, GL_EMISSION, black);
  2214.     glMaterialfv(GL_FRONT, GL_SPECULAR, black);
  2215.     glMaterialfv(GL_FRONT, GL_SHININESS, &zero);
  2216. }
  2217. static void renderSphereDefault(const RenderInfo& ri,
  2218.                                 const Frustum& frustum,
  2219.                                 bool lit,
  2220.                                 const GLContext& context)
  2221. {
  2222.     if (lit)
  2223.         glEnable(GL_LIGHTING);
  2224.     else
  2225.         glDisable(GL_LIGHTING);
  2226.     if (ri.baseTex == NULL)
  2227.     {
  2228.         glDisable(GL_TEXTURE_2D);
  2229.     }
  2230.     else
  2231.     {
  2232.         glEnable(GL_TEXTURE_2D);
  2233.         ri.baseTex->bind();
  2234.     }
  2235.     glColor(ri.color);
  2236.     g_lodSphere->render(context,
  2237.                         LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  2238.                         frustum, ri.pixWidth,
  2239.                         ri.baseTex);
  2240.     if (ri.nightTex != NULL && ri.useTexEnvCombine)
  2241.     {
  2242.         ri.nightTex->bind();
  2243. #ifdef USE_HDR
  2244. #ifdef HDR_COMPRESS
  2245.         Color nightColor(ri.color.red()   * 2.f,
  2246.                          ri.color.green() * 2.f,
  2247.                          ri.color.blue()  * 2.f,
  2248.                          ri.nightLightScale);  // Modulate brightness using alpha
  2249. #else
  2250.         Color nightColor(ri.color.red(),
  2251.                          ri.color.green(),
  2252.                          ri.color.blue(),
  2253.                          ri.nightLightScale);  // Modulate brightness using alpha
  2254. #endif
  2255.         glColor(nightColor);
  2256. #endif
  2257.         setupNightTextureCombine();
  2258.         glEnable(GL_BLEND);
  2259. #ifdef USE_HDR
  2260.         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  2261. #else
  2262.         glBlendFunc(GL_ONE, GL_ONE);
  2263. #endif
  2264.         glAmbientLightColor(Color::Black); // Disable ambient light
  2265.         g_lodSphere->render(context,
  2266.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  2267.                             frustum, ri.pixWidth,
  2268.                             ri.nightTex);
  2269.         glAmbientLightColor(ri.ambientColor);
  2270. #ifdef USE_HDR
  2271.         glColor(ri.color);
  2272. #endif
  2273.         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  2274.     }
  2275.     if (ri.overlayTex != NULL)
  2276.     {
  2277.         ri.overlayTex->bind();
  2278.         glEnable(GL_BLEND);
  2279.         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  2280.         g_lodSphere->render(context,
  2281.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  2282.                             frustum, ri.pixWidth,
  2283.                             ri.overlayTex);
  2284.         glBlendFunc(GL_ONE, GL_ONE);
  2285.     }
  2286. }
  2287. // DEPRECATED -- renderSphere_Combiners_VP should be used instead; only
  2288. // very old drivers don't support vertex programs.
  2289. static void renderSphere_Combiners(const RenderInfo& ri,
  2290.                                    const Frustum& frustum,
  2291.                                    const GLContext& context)
  2292. {
  2293.     glDisable(GL_LIGHTING);
  2294.     if (ri.baseTex == NULL)
  2295.     {
  2296.         glDisable(GL_TEXTURE_2D);
  2297.     }
  2298.     else
  2299.     {
  2300.         glEnable(GL_TEXTURE_2D);
  2301.         ri.baseTex->bind();
  2302.     }
  2303.     glColor(ri.color * ri.sunColor);
  2304.     // Don't use a normal map if it's a dxt5nm map--only the GLSL path
  2305.     // can handle them.
  2306.     if (ri.bumpTex != NULL &&
  2307.         (ri.bumpTex->getFormatOptions() & Texture::DXT5NormalMap) == 0)
  2308.     {
  2309.         renderBumpMappedMesh(context,
  2310.                              *(ri.baseTex),
  2311.                              *(ri.bumpTex),
  2312.                              ri.sunDir_eye,
  2313.                              ri.orientation,
  2314.                              ri.ambientColor,
  2315.                              frustum,
  2316.                              ri.pixWidth);
  2317.     }
  2318.     else if (ri.baseTex != NULL)
  2319.     {
  2320.         renderSmoothMesh(context,
  2321.                          *(ri.baseTex),
  2322.                          ri.sunDir_eye,
  2323.                          ri.orientation,
  2324.                          ri.ambientColor,
  2325.                          ri.pixWidth,
  2326.                          frustum);
  2327.     }
  2328.     else
  2329.     {
  2330.         glEnable(GL_LIGHTING);
  2331.         g_lodSphere->render(context, frustum, ri.pixWidth, NULL, 0);
  2332.     }
  2333.     if (ri.nightTex != NULL)
  2334.     {
  2335.         ri.nightTex->bind();
  2336.         glEnable(GL_BLEND);
  2337.         glBlendFunc(GL_ONE, GL_ONE);
  2338.         renderSmoothMesh(context,
  2339.                          *(ri.nightTex),
  2340.                          ri.sunDir_eye,
  2341.                          ri.orientation,
  2342.                          Color::Black,
  2343.                          ri.pixWidth,
  2344.                          frustum,
  2345.                          true);
  2346.     }
  2347.     if (ri.overlayTex != NULL)
  2348.     {
  2349.         glEnable(GL_LIGHTING);
  2350.         ri.overlayTex->bind();
  2351.         glEnable(GL_BLEND);
  2352.         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  2353.         g_lodSphere->render(context,
  2354.                             LODSphereMesh::Normals | LODSphereMesh::TexCoords0,
  2355.                             frustum, ri.pixWidth,
  2356.                             ri.overlayTex);
  2357. #if 0
  2358.         renderSmoothMesh(context,
  2359.                          *(ri.overlayTex),
  2360.                          ri.sunDir_eye,
  2361.                          ri.orientation,
  2362.                          ri.ambientColor,
  2363.                          ri.pixWidth,
  2364.                          frustum);
  2365. #endif
  2366.         glBlendFunc(GL_ONE, GL_ONE);
  2367.     }
  2368.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  2369. }
  2370. static void renderSphere_DOT3_VP(const RenderInfo& ri,
  2371.                                  const LightingState& ls,
  2372.                                  const Frustum& frustum,
  2373.                                  const GLContext& context)
  2374. {
  2375.     VertexProcessor* vproc = context.getVertexProcessor();
  2376.     assert(vproc != NULL);
  2377.     if (ri.baseTex == NULL)
  2378.     {
  2379.         glDisable(GL_TEXTURE_2D);
  2380.     }
  2381.     else
  2382.     {
  2383.         glEnable(GL_TEXTURE_2D);
  2384.         ri.baseTex->bind();
  2385.     }
  2386.     vproc->enable();
  2387.     vproc->parameter(vp::EyePosition, ri.eyePos_obj);
  2388.     setLightParameters_VP(*vproc, ls, ri.color, ri.specularColor);
  2389.     Color ambient(ri.ambientColor * ri.color);
  2390. #ifdef USE_HDR
  2391.     ambient = ri.ambientColor;
  2392. #endif
  2393.     vproc->parameter(vp::AmbientColor, ambient);
  2394.     vproc->parameter(vp::SpecularExponent, 0.0f, 1.0f, 0.5f, ri.specularPower);
  2395.     // Don't use a normal map if it's a dxt5nm map--only the GLSL path
  2396.     // can handle them.
  2397.     if (ri.bumpTex != NULL &&
  2398.         (ri.bumpTex->getFormatOptions() & Texture::DXT5NormalMap) == 0 &&
  2399.         ri.baseTex != NULL)
  2400.     {
  2401.         // We don't yet handle the case where there's a bump map but no
  2402.         // base texture.
  2403. #ifdef HDR_COMPRESS
  2404.         vproc->use(vp::diffuseBumpHDR);
  2405. #else
  2406.         vproc->use(vp::diffuseBump);
  2407. #endif
  2408.         if (ri.ambientColor != Color::Black)
  2409.         {
  2410.             // If there's ambient light, we'll need to render in two passes:
  2411.             // one for the ambient light, and the second for light from the star.
  2412.             // We could do this in a single pass using three texture stages, but
  2413.             // this isn't won't work with hardware that only supported two
  2414.             // texture stages.
  2415.             // Render the base texture modulated by the ambient color
  2416.             setupTexenvAmbient(ambient);
  2417.             g_lodSphere->render(context,
  2418.                                 LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
  2419.                                 frustum, ri.pixWidth,
  2420.                                 ri.baseTex);
  2421.             // Add the light from the sun
  2422.             glEnable(GL_BLEND);
  2423.             glBlendFunc(GL_ONE, GL_ONE);
  2424.             setupBumpTexenv();
  2425.             g_lodSphere->render(context,
  2426.                                 LODSphereMesh::Normals | LODSphereMesh::Tangents |
  2427.                                 LODSphereMesh::TexCoords0 | LODSphereMesh::VertexProgParams,
  2428.                                 frustum, ri.pixWidth,
  2429.                                 ri.bumpTex, ri.baseTex);
  2430.             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  2431.             glDisable(GL_BLEND);
  2432.         }
  2433.         else
  2434.         {
  2435.             glx::glActiveTextureARB(GL_TEXTURE1_ARB);