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

OpenGL

开发平台:

Visual C++

  1.             v0.normalize();
  2.             v1 = v0;
  3.         }
  4.         else
  5.         {
  6.             v0 = v1 = cometPoints[i] - cometPoints[i - 1];
  7.             sectionLength = v0.length();
  8.             v0.normalize();
  9.             v1 = v0;
  10.         }
  11.         float radius = (float) i / (float) nTailPoints *
  12.             dustTailRadius;
  13.         float dr = (dustTailRadius / (float) nTailPoints) /
  14.             sectionLength;
  15.         float w0 = (float) atan(dr);
  16.         float d = std::sqrt(1.0f + w0 * w0);
  17.         float w1 = 1.0f / d;
  18.         w0 = w0 / d;
  19.         // Special case the first vertex in the comet tail
  20.         if (i == 0)
  21.         {
  22.             w0 = 1;
  23.             w1 = 0.0f;
  24.         }
  25.         for (int j = 0; j < nTailSlices; j++)
  26.         {
  27.             float theta = (float) (2 * PI * (float) j / nTailSlices);
  28.             float s = (float) sin(theta);
  29.             float c = (float) cos(theta);
  30.             CometTailVertex* vtx = &cometTailVertices[i * nTailSlices + j];
  31.             vtx->normal = u * (s * w1) + w * (c * w1) + v * w0;
  32.             s *= radius;
  33.             c *= radius;
  34.             vtx->point = Point3f(cometPoints[i].x + u.x * s + w.x * c,
  35.                                  cometPoints[i].y + u.y * s + w.y * c,
  36.                                  cometPoints[i].z + u.z * s + w.z * c);
  37.             vtx->brightness = brightness;
  38.             vtx->paraboloidPoint =
  39.                 Point3f(c, s, square((float) i / (float) MaxCometTailPoints));
  40.         }
  41.     }
  42.     Vec3f viewDir = pos - Point3f(0.0f, 0.0f, 0.0f);
  43.     viewDir.normalize();
  44.     glDisable(GL_CULL_FACE);
  45.     for (i = 0; i < nTailPoints - 1; i++)
  46.     {
  47.         glBegin(GL_QUAD_STRIP);
  48.         int n = i * nTailSlices;
  49.         for (int j = 0; j < nTailSlices; j++)
  50.         {
  51.             ProcessCometTailVertex(cometTailVertices[n + j], viewDir, fadeDistance);
  52.             ProcessCometTailVertex(cometTailVertices[n + j + nTailSlices],
  53.                                    viewDir, fadeDistance);
  54.         }
  55.         ProcessCometTailVertex(cometTailVertices[n], viewDir, fadeDistance);
  56.         ProcessCometTailVertex(cometTailVertices[n + nTailSlices],
  57.                                viewDir, fadeDistance);
  58.         glEnd();
  59.     }
  60.     glEnable(GL_CULL_FACE);
  61.     glBegin(GL_LINE_STRIP);
  62.     for (i = 0; i < nTailPoints; i++)
  63.     {
  64.         glVertex(cometPoints[i]);
  65.     }
  66.     glEnd();
  67.     glEnable(GL_TEXTURE_2D);
  68.     glEnable(GL_BLEND);
  69.     glPopMatrix();
  70. }
  71. // Render a reference mark
  72. void Renderer::renderReferenceMark(const ReferenceMark& refMark,
  73.                                    Point3f pos,
  74.                                    float distance,
  75.                                    double now,
  76.                                    float nearPlaneDistance)
  77. {
  78.     float altitude = distance - refMark.boundingSphereRadius();
  79.     float discSizeInPixels = refMark.boundingSphereRadius() /
  80.         (max(nearPlaneDistance, altitude) * pixelSize);
  81.     if (discSizeInPixels <= 1)
  82.         return;
  83.     // Apply the modelview transform for the object
  84.     glPushMatrix();
  85.     glTranslate(pos);
  86.     refMark.render(this, pos, discSizeInPixels, now);
  87.     glPopMatrix();
  88.     glDisable(GL_DEPTH_TEST);
  89.     glDepthMask(GL_FALSE);
  90.     glEnable(GL_TEXTURE_2D);
  91.     glEnable(GL_BLEND);
  92.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  93. }
  94. // Helper function to compute the luminosity of a perfectly
  95. // reflective disc with the specified radius. This is used as an upper
  96. // bound for the apparent brightness of an object when culling
  97. // invisible objects.
  98. static float luminosityAtOpposition(float sunLuminosity,
  99.                                     float distanceFromSun,
  100.                                     float objRadius)
  101. {
  102.     // Compute the total power of the star in Watts
  103.     double power = astro::SOLAR_POWER * sunLuminosity;
  104.     // Compute the irradiance at the body's distance from the star
  105.     double irradiance = power / sphereArea(distanceFromSun * 1000);
  106.     // Compute the total energy hitting the planet; assume an albedo of 1.0, so
  107.     // reflected energy = incident energy.
  108.     double incidentEnergy = irradiance * circleArea(objRadius * 1000);
  109.     
  110.     // Compute the luminosity (i.e. power relative to solar power)
  111.     return (float) (incidentEnergy / astro::SOLAR_POWER);
  112. }
  113. void Renderer::addRenderListEntries(RenderListEntry& rle,
  114.                                     Body& body,
  115.                                     bool isLabeled)
  116. {
  117.     bool visibleAsPoint = rle.appMag < faintestPlanetMag && body.isVisibleAsPoint();
  118.     if (rle.discSizeInPixels > 1 || visibleAsPoint || isLabeled)
  119.     {
  120.         rle.renderableType = RenderListEntry::RenderableBody;
  121.         rle.body = &body;
  122.         if (body.getGeometry() != InvalidResource && rle.discSizeInPixels > 1)
  123.         {
  124.             Geometry* geometry = GetGeometryManager()->find(body.getGeometry());
  125.             if (geometry == NULL)
  126.                 rle.isOpaque = true;
  127.             else
  128.                 rle.isOpaque = geometry->isOpaque();
  129.         }
  130.         else
  131.         {
  132.             rle.isOpaque = true;
  133.         }
  134.         rle.radius = body.getRadius();
  135.         renderList.push_back(rle);
  136.     }
  137.     if (body.getClassification() == Body::Comet && (renderFlags & ShowCometTails) != 0)
  138.     {
  139.         float radius = cometDustTailLength(rle.sun.length(), body.getRadius());
  140.         float discSize = (radius / (float) rle.distance) / pixelSize;
  141.         if (discSize > 1)
  142.         {
  143.             rle.renderableType = RenderListEntry::RenderableCometTail;
  144.             rle.body = &body;
  145.             rle.isOpaque = false;
  146.             rle.radius = radius;
  147.             rle.discSizeInPixels = discSize;
  148.             renderList.push_back(rle);
  149.         }
  150.     }
  151.     const list<ReferenceMark*>* refMarks = body.getReferenceMarks();
  152.     if (refMarks != NULL)
  153.     {
  154.         for (list<ReferenceMark*>::const_iterator iter = refMarks->begin();
  155.             iter != refMarks->end(); ++iter)
  156.         {
  157.             const ReferenceMark* rm = *iter;
  158.             rle.renderableType = RenderListEntry::RenderableReferenceMark;
  159.             rle.refMark = rm;
  160.             rle.isOpaque = rm->isOpaque();
  161.             rle.radius = rm->boundingSphereRadius();
  162.             renderList.push_back(rle);
  163.         }
  164.     }
  165. }
  166. void Renderer::buildRenderLists(const Point3d& astrocentricObserverPos,
  167.                                 const Frustum& viewFrustum,
  168.                                 const Vec3d& viewPlaneNormal,
  169.                                 const Vec3d& frameCenter,
  170.                                 const FrameTree* tree,
  171.                                 const Observer& observer,
  172.                                 double now)
  173. {
  174.     int labelClassMask = translateLabelModeToClassMask(labelMode);
  175.     Mat3f viewMat = observer.getOrientationf().toMatrix3();
  176.     Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
  177.     double invCosViewAngle = 1.0 / cosViewConeAngle;
  178.     double sinViewAngle = sqrt(1.0 - square(cosViewConeAngle));   
  179.     unsigned int nChildren = tree != NULL ? tree->childCount() : 0;
  180.     for (unsigned int i = 0; i < nChildren; i++)
  181.     {
  182.         const TimelinePhase* phase = tree->getChild(i);
  183.         // No need to do anything if the phase isn't active now
  184.         if (!phase->includes(now))
  185.             continue;
  186.         Body* body = phase->body();
  187.         // pos_s: sun-relative position of object
  188.         // pos_v: viewer-relative position of object
  189.         // Get the position of the body relative to the sun.
  190.         Point3d p = phase->orbit()->positionAtTime(now);
  191.         ReferenceFrame* frame = phase->orbitFrame();
  192.         Vec3d pos_s = frameCenter + Vec3d(p.x, p.y, p.z) * frame->getOrientation(now).toMatrix3();
  193.         // We now have the positions of the observer and the planet relative
  194.         // to the sun.  From these, compute the position of the body
  195.         // relative to the observer.
  196.         Vec3d pos_v = Point3d(pos_s.x, pos_s.y, pos_s.z) - astrocentricObserverPos;
  197.         // dist_vn: distance along view normal from the viewer to the
  198.         // projection of the object's center.
  199.         double dist_vn = viewPlaneNormal * pos_v;
  200.         // Vector from object center to its projection on the view normal.
  201.         Vec3d toViewNormal = pos_v - dist_vn * viewPlaneNormal;
  202.         float cullingRadius = body->getCullingRadius();
  203.         // The result of the planetshine test can be reused for the view cone
  204.         // test, but only when the object's light influence sphere is larger
  205.         // than the geometry. This is not 
  206.         bool viewConeTestFailed = false;
  207.         if (body->isSecondaryIlluminator())
  208.         {
  209.             float influenceRadius = body->getBoundingRadius() + (body->getRadius() * PLANETSHINE_DISTANCE_LIMIT_FACTOR);
  210.             if (dist_vn > -influenceRadius)
  211.             {
  212.                 double maxPerpDist = (influenceRadius + dist_vn * sinViewAngle) * invCosViewAngle;
  213.                 double perpDistSq = toViewNormal * toViewNormal;
  214.                 if (perpDistSq < maxPerpDist * maxPerpDist)
  215.                 {
  216.                     if ((body->getRadius() / (float) pos_v.length()) / pixelSize > PLANETSHINE_PIXEL_SIZE_LIMIT)
  217.                     {
  218.                         // add to planetshine list if larger than 1/10 pixel
  219. #if DEBUG_SECONDARY_ILLUMINATION
  220.                         clog << "Planetshine: " << body->getName()
  221.                              << ", " << body->getRadius() / (float) pos_v.length() / pixelSize << endl;
  222. #endif
  223.                         SecondaryIlluminator illum;
  224.                         illum.body = body;
  225.                         illum.position_v = pos_v;
  226.                         illum.radius = body->getRadius();
  227.                         secondaryIlluminators.push_back(illum);
  228.                     }
  229.                 }
  230.                 else
  231.                 {
  232.                     viewConeTestFailed = influenceRadius > cullingRadius;
  233.                 }
  234.             }
  235.             else
  236.             {
  237.                 viewConeTestFailed = influenceRadius > cullingRadius;
  238.             }
  239.         }
  240.         bool insideViewCone = false;
  241.         if (!viewConeTestFailed)
  242.         {
  243.             float radius = body->getCullingRadius();
  244.             if (dist_vn > -radius)
  245.             {
  246.                 double maxPerpDist = (radius + dist_vn * sinViewAngle) * invCosViewAngle;
  247.                 double perpDistSq = toViewNormal * toViewNormal;
  248.                 insideViewCone = perpDistSq < maxPerpDist * maxPerpDist;
  249.             }
  250.         }
  251.         if (insideViewCone)
  252.         {
  253.             // Calculate the distance to the viewer
  254.             double dist_v = pos_v.length();
  255.             // Calculate the size of the planet/moon disc in pixels
  256.             float discSize = (body->getCullingRadius() / (float) dist_v) / pixelSize;
  257.             // Compute the apparent magnitude; instead of summing the reflected
  258.             // light from all nearby stars, we just consider the one with the
  259.             // highest apparent brightness.
  260.             float appMag = 100.0f;
  261.             for (unsigned int li = 0; li < lightSourceList.size(); li++)
  262.             {
  263.                 Vec3d sunPos = pos_v - lightSourceList[li].position;
  264.                 appMag = min(appMag, body->getApparentMagnitude(lightSourceList[li].luminosity, sunPos, pos_v));
  265.             }
  266.             bool visibleAsPoint = appMag < faintestPlanetMag && body->isVisibleAsPoint();
  267.             bool isLabeled = (body->getOrbitClassification() & labelClassMask) != 0;
  268.             bool visible = body->isVisible();
  269.             if ((discSize > 1 || visibleAsPoint || isLabeled) && visible)
  270.             {
  271.                 RenderListEntry rle;
  272.                 rle.position = Point3f((float) pos_v.x, (float) pos_v.y, (float) pos_v.z);
  273.                 rle.distance = (float) dist_v;
  274.                 rle.centerZ = Vec3f((float) pos_v.x, (float) pos_v.y, (float) pos_v.z) * viewMatZ;
  275.                 rle.appMag   = appMag;
  276.                 rle.discSizeInPixels = body->getRadius() / ((float) dist_v * pixelSize);
  277.                 // TODO: Remove this. It's only used in two places: for calculating comet tail
  278.                 // length, and for calculating sky brightness to adjust the limiting magnitude.
  279.                 // In both cases, it's the wrong quantity to use (e.g. for objects with orbits
  280.                 // defined relative to the SSB.)
  281.                 rle.sun = Vec3f((float) -pos_s.x, (float) -pos_s.y, (float) -pos_s.z);
  282.                 addRenderListEntries(rle, *body, isLabeled);
  283.             }
  284.         }
  285.         const FrameTree* subtree = body->getFrameTree();
  286.         if (subtree != NULL)
  287.         {
  288.             double dist_v = pos_v.length();
  289.             bool traverseSubtree = false;
  290.             // There are two different tests available to determine whether we can reject
  291.             // the object's subtree. If the subtree contains no light reflecting objects,
  292.             // then render the subtree only when:
  293.             //    - the subtree bounding sphere intersects the view frustum, and
  294.             //    - the subtree contains an object bright or large enough to be visible.
  295.             // Otherwise, render the subtree when any of the above conditions are
  296.             // true or when a subtree object could potentially illuminate something
  297.             // in the view cone.
  298.             float minPossibleDistance = (float) (dist_v - subtree->boundingSphereRadius());
  299.             float brightestPossible = 0.0;
  300.             float largestPossible = 0.0;
  301.             // If the viewer is not within the subtree bounding sphere, see if we can cull it because
  302.             // it contains no objects brighter than the limiting magnitude and no objects that will
  303.             // be larger than one pixel in size.
  304.             if (minPossibleDistance > 1.0f)
  305.             {
  306.                 // Figure out the magnitude of the brightest possible object in the subtree.
  307.                 // Compute the luminosity from reflected light of the largest object in the subtree
  308.                 float lum = 0.0f;                
  309.                 for (unsigned int li = 0; li < lightSourceList.size(); li++)
  310.                 {
  311.                     Vec3d sunPos = pos_v - lightSourceList[li].position;
  312.                     lum += luminosityAtOpposition(lightSourceList[li].luminosity, (float) sunPos.length(), (float) subtree->maxChildRadius());
  313.                 }
  314.                 brightestPossible = astro::lumToAppMag(lum, astro::kilometersToLightYears(minPossibleDistance));
  315.                 largestPossible = (float) subtree->maxChildRadius() / (float) minPossibleDistance / pixelSize;
  316.             }
  317.             else
  318.             {
  319.                 // Viewer is within the bounding sphere, so the object could be very close.
  320.                 // Assume that an object in the subree could be very bright or large,
  321.                 // so no culling will occur.
  322.                 brightestPossible = -100.0f;
  323.                 largestPossible = 100.0f;
  324.             }
  325.             if (brightestPossible < faintestPlanetMag || largestPossible > 1.0f)
  326.             {
  327.                 // See if the object or any of its children are within the view frustum
  328.                 if (viewFrustum.testSphere(Point3f((float) pos_v.x, (float) pos_v.y, (float) pos_v.z), (float) subtree->boundingSphereRadius()) != Frustum::Outside)
  329.                 {
  330.                     traverseSubtree = true;
  331.                 }
  332.             }
  333.             // If the subtree contains secondary illuminators, do one last check if it hasn't
  334.             // already been determined if we need to traverse the subtree: see if something
  335.             // in the subtree could possibly contribute significant illumination to an
  336.             // object in the view cone.
  337.             if (subtree->containsSecondaryIlluminators() &&
  338.                 !traverseSubtree                         &&
  339.                 largestPossible > PLANETSHINE_PIXEL_SIZE_LIMIT)
  340.             {
  341.                 float influenceRadius = (float) (subtree->boundingSphereRadius() +
  342.                     (subtree->maxChildRadius() * PLANETSHINE_DISTANCE_LIMIT_FACTOR));
  343.                 if (dist_vn > -influenceRadius)
  344.                 {
  345.                     double maxPerpDist = (influenceRadius + dist_vn * sinViewAngle) * invCosViewAngle;
  346.                     double perpDistSq = toViewNormal * toViewNormal;
  347.                     if (perpDistSq < maxPerpDist * maxPerpDist)                   
  348.                         traverseSubtree = true;
  349.                 }
  350.             }
  351.             if (traverseSubtree)
  352.             {
  353.                 buildRenderLists(astrocentricObserverPos,
  354.                                  viewFrustum,
  355.                                  viewPlaneNormal,
  356.                                  pos_s,
  357.                                  subtree,
  358.                                  observer,
  359.                                  now);
  360.             }
  361.         } // end subtree traverse
  362.     }
  363. }
  364. void Renderer::buildOrbitLists(const Point3d& astrocentricObserverPos,
  365.                                const Quatf& observerOrientation,
  366.                                const Frustum& viewFrustum,
  367.                                const FrameTree* tree,
  368.                                double now)
  369. {
  370.     Mat3f viewMat = observerOrientation.toMatrix3();
  371.     Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
  372.     unsigned int nChildren = tree != NULL ? tree->childCount() : 0;
  373.     for (unsigned int i = 0; i < nChildren; i++)
  374.     {
  375.         const TimelinePhase* phase = tree->getChild(i);
  376.         // No need to do anything if the phase isn't active now
  377.         if (!phase->includes(now))
  378.             continue;
  379.         Body* body = phase->body();
  380.         // pos_s: sun-relative position of object
  381.         // pos_v: viewer-relative position of object
  382.         // Get the position of the body relative to the sun.
  383.         Point3d pos_s = body->getAstrocentricPosition(now);
  384.         // We now have the positions of the observer and the planet relative
  385.         // to the sun.  From these, compute the position of the body
  386.         // relative to the observer.
  387.         Vec3d pos_v = pos_s - astrocentricObserverPos;
  388.         // Only show orbits for major bodies or selected objects. 
  389.         Body::VisibilityPolicy orbitVis = body->getOrbitVisibility();
  390.         if (body->isVisible() &&
  391.             (body == highlightObject.body() ||
  392.              orbitVis == Body::AlwaysVisible ||
  393.              (orbitVis == Body::UseClassVisibility && (body->getOrbitClassification() & orbitMask) != 0)))
  394.         {
  395.             Point3d orbitOrigin(0.0, 0.0, 0.0);
  396.             Selection centerObject = phase->orbitFrame()->getCenter();
  397.             if (centerObject.body() != NULL)
  398.             {
  399.                 orbitOrigin = centerObject.body()->getAstrocentricPosition(now);
  400.             }
  401.             // Calculate the origin of the orbit relative to the observer
  402.             Vec3d relOrigin = orbitOrigin - astrocentricObserverPos;
  403.             Vec3f origf((float) relOrigin.x, (float) relOrigin.y, (float) relOrigin.z);
  404.             // Compute the size of the orbit in pixels
  405.             double originDistance = pos_v.length();
  406.             double boundingRadius = body->getOrbit(now)->getBoundingRadius();
  407.             float orbitRadiusInPixels = (float) (boundingRadius / (originDistance * pixelSize));
  408.             if (orbitRadiusInPixels > minOrbitSize)
  409.             {
  410.                 // Add the orbit of this body to the list of orbits to be rendered
  411.                 OrbitPathListEntry path;
  412.                 path.body = body;
  413.                 path.star = NULL;
  414.                 path.centerZ = origf * viewMatZ;
  415.                 path.radius = (float) boundingRadius;
  416.                 path.origin = Point3f(origf.x, origf.y, origf.z);
  417.                 path.opacity = sizeFade(orbitRadiusInPixels, minOrbitSize, 2.0f);
  418.                 orbitPathList.push_back(path);
  419.             }
  420.         }
  421.         const FrameTree* subtree = body->getFrameTree();
  422.         if (subtree != NULL)
  423.         {
  424.             // Only try to render orbits of child objects when:
  425.             //   - The apparent size of the subtree bounding sphere is large enough that
  426.             //     orbit paths will be visible, and
  427.             //   - The subtree bounding sphere isn't outside the view frustum
  428.             double dist_v = pos_v.length();
  429.             float distanceToBoundingSphere = (float) (dist_v - subtree->boundingSphereRadius());
  430.             bool traverseSubtree = false;
  431.             if (distanceToBoundingSphere > 0.0f)
  432.             {
  433.                 // We're inside the subtree's bounding sphere
  434.                 traverseSubtree = true;
  435.             }
  436.             else
  437.             {
  438.                 float maxPossibleOrbitSize = (float) subtree->boundingSphereRadius() / ((float) dist_v * pixelSize);
  439.                 if (maxPossibleOrbitSize > minOrbitSize)
  440.                     traverseSubtree = true;
  441.             }
  442.             if (traverseSubtree)
  443.             {
  444.                 // See if the object or any of its children are within the view frustum
  445.                 if (viewFrustum.testSphere(Point3f((float) pos_v.x, (float) pos_v.y, (float) pos_v.z), (float) subtree->boundingSphereRadius()) != Frustum::Outside)
  446.                 {
  447.                     buildOrbitLists(astrocentricObserverPos,
  448.                                     observerOrientation,
  449.                                     viewFrustum,
  450.                                     subtree,
  451.                                     now);
  452.                 }
  453.             }
  454.         } // end subtree traverse
  455.     }
  456. }
  457. void Renderer::buildLabelLists(const Frustum& viewFrustum,
  458.                                double now)
  459. {
  460.     int labelClassMask = translateLabelModeToClassMask(labelMode);
  461.     Body* lastPrimary = NULL;
  462.     Sphered primarySphere;
  463.     for (vector<RenderListEntry>::const_iterator iter = renderList.begin();
  464.          iter != renderList.end(); iter++)
  465.     {
  466.         int classification = iter->body->getOrbitClassification();
  467.         if (iter->renderableType == RenderListEntry::RenderableBody &&
  468.             (classification & labelClassMask)                       &&
  469.             viewFrustum.testSphere(iter->position, iter->radius) != Frustum::Outside)
  470.         {
  471.             const Body* body = iter->body;
  472.             Vec3f pos(iter->position.x, iter->position.y, iter->position.z);
  473.             float boundingRadiusSize = (float) (body->getOrbit(now)->getBoundingRadius() / iter->distance) / pixelSize;
  474.             if (boundingRadiusSize > minOrbitSize)
  475.             {
  476.                 Color labelColor;
  477.                 float opacity = sizeFade(boundingRadiusSize, minOrbitSize, 2.0f);
  478.                 switch (classification)
  479.                 {
  480.                 case Body::Planet:
  481.                     labelColor = PlanetLabelColor;
  482.                     break;
  483.                 case Body::DwarfPlanet:
  484.                     labelColor = DwarfPlanetLabelColor;
  485.                     break;
  486.                 case Body::Moon:
  487.                     labelColor = MoonLabelColor;
  488.                     break;
  489.                 case Body::MinorMoon:
  490.                     labelColor = MinorMoonLabelColor;
  491.                     break;
  492.                 case Body::Asteroid:
  493.                     labelColor = AsteroidLabelColor;
  494.                     break;
  495.                 case Body::Comet:
  496.                     labelColor = CometLabelColor;
  497.                     break;
  498.                 case Body::Spacecraft:
  499.                     labelColor = SpacecraftLabelColor;
  500.                     break;
  501.                 default:
  502.                     labelColor = Color::Black;
  503.                     break;
  504.                 }
  505.                 labelColor = Color(labelColor, opacity * labelColor.alpha());
  506.                 if (!body->getName().empty())
  507.                 {
  508.                     bool isBehindPrimary = false;
  509.                     const TimelinePhase* phase = body->getTimeline()->findPhase(now);
  510.                     Body* primary = phase->orbitFrame()->getCenter().body();
  511.                     if (primary != NULL && (primary->getClassification() & Body::Invisible) != 0)
  512.                     {
  513.                         Body* parent = phase->orbitFrame()->getCenter().body();
  514.                         if (parent != NULL)
  515.                             primary = parent;
  516.                     }
  517.                     // Position the label slightly in front of the object along a line from
  518.                     // object center to viewer.
  519.                     pos = pos * (1.0f - body->getBoundingRadius() * 1.01f / pos.length());
  520.                     // Try and position the label so that it's not partially
  521.                     // occluded by other objects. We'll consider just the object
  522.                     // that the labeled body is orbiting (its primary) as a
  523.                     // potential occluder. If a ray from the viewer to labeled
  524.                     // object center intersects the occluder first, skip
  525.                     // rendering the object label. Otherwise, ensure that the
  526.                     // label is completely in front of the primary by projecting
  527.                     // it onto the plane tangent to the primary at the
  528.                     // viewer-primary intersection point. Whew. Don't do any of
  529.                     // this if the primary isn't an ellipsoid.
  530.                     //
  531.                     // This only handles the problem of partial label occlusion
  532.                     // for low orbiting and surface positioned objects, but that
  533.                     // case is *much* more common than other possibilities.
  534.                     if (primary != NULL && primary->isEllipsoid())
  535.                     {
  536.                         // In the typical case, we're rendering labels for many
  537.                         // objects that orbit the same primary. Avoid repeatedly
  538.                         // calling getPosition() by caching the last primary
  539.                         // position.
  540.                         if (primary != lastPrimary)
  541.                         {
  542.                             Point3d p = phase->orbit()->positionAtTime(now) * 
  543.                                         phase->orbitFrame()->getOrientation(now).toMatrix3();
  544.                             Vec3d v(iter->position.x - p.x, iter->position.y - p.y, iter->position.z - p.z);
  545.                             
  546.                             primarySphere = Sphered(Point3d(v.x, v.y, v.z),
  547.                                                     primary->getRadius());
  548.                             lastPrimary = primary;
  549.                         }
  550.                         Ray3d testRay(Point3d(0.0, 0.0, 0.0), Vec3d(pos.x, pos.y, pos.z));
  551.                         // Test the viewer-to-labeled object ray against
  552.                         // the primary sphere (TODO: handle ellipsoids)
  553.                         double t = 0.0;
  554.                         if (testIntersection(testRay, primarySphere, t))
  555.                         {
  556.                             // Center of labeled object is behind primary
  557.                             // sphere; mark it for rejection.
  558.                             isBehindPrimary = t < 1.0;
  559.                         }
  560.                         if (!isBehindPrimary)
  561.                         {
  562.                             // Not rejected. Compute the plane tangent to
  563.                             // the primary at the viewer-to-primary
  564.                             // intersection point.
  565.                             Vec3d primaryVec(primarySphere.center.x,
  566.                                              primarySphere.center.y,
  567.                                              primarySphere.center.z);
  568.                             double distToPrimary = primaryVec.length();
  569.                             Planed primaryTangentPlane(primaryVec, primaryVec * (primaryVec * (1.0 - primarySphere.radius / distToPrimary)));
  570.                             // Compute the intersection of the viewer-to-labeled
  571.                             // object ray with the tangent plane.
  572.                             float u = (float) (primaryTangentPlane.d / (primaryTangentPlane.normal * Vec3d(pos.x, pos.y, pos.z)));
  573.                             // If the intersection point is closer to the viewer
  574.                             // than the label, then project the label onto the
  575.                             // tangent plane.
  576.                             if (u < 1.0f && u > 0.0f)
  577.                             {
  578.                                 pos = pos * u;
  579.                             }
  580.                         }
  581.                     }
  582.                     addSortedAnnotation(NULL, body->getName(true), labelColor,
  583.                                         Point3f(pos.x, pos.y, pos.z));
  584.                 }
  585.             }
  586.         }
  587.     } // for each render list entry      
  588. }
  589. // Add a star orbit to the render list
  590. void Renderer::addStarOrbitToRenderList(const Star& star,
  591.                                         const Observer& observer,
  592.                                         double now)
  593. {
  594.     // If the star isn't fixed, add its orbit to the render list
  595.     if ((renderFlags & ShowOrbits) != 0 &&
  596.         ((orbitMask & Body::Stellar) != 0 || highlightObject.star() == &star))
  597.     {
  598.         Mat3f viewMat = observer.getOrientationf().toMatrix3();
  599.         Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
  600.         if (star.getOrbit() != NULL)
  601.         {
  602.             // Get orbit origin relative to the observer
  603.             Vec3d orbitOrigin = star.getOrbitBarycenterPosition(now) - observer.getPosition();
  604.             orbitOrigin *= astro::microLightYearsToKilometers(1.0);
  605.             Vec3f origf((float) orbitOrigin.x, (float) orbitOrigin.y, (float) orbitOrigin.z);
  606.             // Compute the size of the orbit in pixels
  607.             double originDistance = orbitOrigin.length();
  608.             double boundingRadius = star.getOrbit()->getBoundingRadius();
  609.             float orbitRadiusInPixels = (float) (boundingRadius / (originDistance * pixelSize));
  610.             if (orbitRadiusInPixels > minOrbitSize)
  611.             {
  612.                 // Add the orbit of this body to the list of orbits to be rendered
  613.                 OrbitPathListEntry path;
  614.                 path.star = &star;
  615.                 path.body = NULL;
  616.                 path.centerZ = origf * viewMatZ;
  617.                 path.radius = (float) boundingRadius;
  618.                 path.origin = Point3f(origf.x, origf.y, origf.z);
  619.                 path.opacity = sizeFade(orbitRadiusInPixels, minOrbitSize, 2.0f);
  620.                 orbitPathList.push_back(path);
  621.             }
  622.         }
  623.     }
  624. }
  625. template <class OBJ, class PREC> class ObjectRenderer : public OctreeProcessor<OBJ, PREC>
  626. {
  627.  public:
  628.     ObjectRenderer(const PREC distanceLimit);
  629.     void process(const OBJ&, PREC, float) {};
  630.  public:
  631.     const Observer* observer;
  632.     GLContext* context;
  633.     Renderer*  renderer;
  634.     Vec3f viewNormal;
  635.     float fov;
  636.     float size;
  637.     float pixelSize;
  638.     float faintestMag;
  639.     float faintestMagNight;
  640.     float saturationMag;
  641. #ifdef USE_HDR
  642.     float exposure;
  643. #endif
  644.     float brightnessScale;
  645.     float brightnessBias;
  646.     float distanceLimit;
  647.     // Objects brighter than labelThresholdMag will be labeled
  648.     float labelThresholdMag;
  649.     // These are not fully used by this template's descendants
  650.     // but we place them here just in case a more sophisticated
  651.     // rendering scheme is implemented:
  652.     int nRendered;
  653.     int nClose;
  654.     int nBright;
  655.     int nProcessed;
  656.     int nLabelled;
  657.     int renderFlags;
  658.     int labelMode;
  659. };
  660. template <class OBJ, class PREC>
  661. ObjectRenderer<OBJ, PREC>::ObjectRenderer(const PREC _distanceLimit) :
  662.     distanceLimit((float) _distanceLimit),
  663. #ifdef USE_HDR
  664.     exposure     (0.0f),
  665. #endif
  666.     nRendered    (0),
  667.     nClose       (0),
  668.     nBright      (0),
  669.     nProcessed   (0),
  670.     nLabelled    (0)
  671. {
  672. }
  673. class StarRenderer : public ObjectRenderer<Star, float>
  674. {
  675.  public:
  676.     StarRenderer();
  677.     void process(const Star& star, float distance, float appMag);
  678.  public:
  679.     Point3d obsPos;
  680.     vector<Renderer::Particle>*      glareParticles;
  681.     vector<RenderListEntry>*         renderList;
  682.     Renderer::StarVertexBuffer*      starVertexBuffer;
  683.     Renderer::PointStarVertexBuffer* pointStarVertexBuffer;
  684.     const StarDatabase* starDB;
  685.     bool   useScaledDiscs;
  686.     GLenum starPrimitive;
  687.     float  maxDiscSize;
  688.     float cosFOV;
  689.     const ColorTemperatureTable* colorTemp;
  690. #ifdef DEBUG_HDR_ADAPT
  691.     float minMag;
  692.     float maxMag;
  693.     float minAlpha;
  694.     float maxAlpha;
  695.     float maxSize;
  696.     float above;
  697.     unsigned long countAboveN;
  698.     unsigned long total;
  699. #endif
  700. };
  701. StarRenderer::StarRenderer() :
  702.     ObjectRenderer<Star, float>(STAR_DISTANCE_LIMIT),
  703.     starVertexBuffer     (NULL),
  704.     pointStarVertexBuffer(NULL),
  705.     useScaledDiscs       (false),
  706.     maxDiscSize          (1.0f),
  707.     cosFOV               (1.0f),
  708.     colorTemp            (NULL)
  709. {
  710. }
  711. void StarRenderer::process(const Star& star, float distance, float appMag)
  712. {
  713.     nProcessed++;
  714.     Point3f starPos = star.getPosition();
  715.     Vec3f   relPos((float) ((double) starPos.x - obsPos.x),
  716.                    (float) ((double) starPos.y - obsPos.y),
  717.                    (float) ((double) starPos.z - obsPos.z));    
  718.     float   orbitalRadius = star.getOrbitalRadius();
  719.     bool    hasOrbit = orbitalRadius > 0.0f;
  720.     if (distance > distanceLimit)
  721.         return;
  722.     if (relPos * viewNormal > 0 || relPos.x * relPos.x < 0.1f || hasOrbit)
  723.     {
  724. #ifdef HDR_COMPRESS
  725.         Color starColorFull = colorTemp->lookupColor(star.getTemperature());
  726.         Color starColor(starColorFull.red()   * 0.5f,
  727.                         starColorFull.green() * 0.5f,
  728.                         starColorFull.blue()  * 0.5f);
  729. #else
  730.         Color starColor = colorTemp->lookupColor(star.getTemperature());
  731. #endif
  732.         float renderDistance = distance;
  733.         float s = renderDistance * size;
  734.         float discSizeInPixels = 0.0f;
  735.         float orbitSizeInPixels = 0.0f;
  736.         if (hasOrbit)
  737.             orbitSizeInPixels = orbitalRadius / (distance * pixelSize);
  738.         // Special handling for stars less than one light year away . . .
  739.         // We can't just go ahead and render a nearby star in the usual way
  740.         // for two reasons:
  741.         //   * It may be clipped by the near plane
  742.         //   * It may be large enough that we should render it as a mesh
  743.         //     instead of a particle
  744.         // It's possible that the second condition might apply for stars
  745.         // further than one light year away if the star is huge, the fov is
  746.         // very small and the resolution is high.  We'll ignore this for now
  747.         // and use the most inexpensive test possible . . .
  748.         if (distance < 1.0f || orbitSizeInPixels > 1.0f)
  749.         {
  750.             // Compute the position of the observer relative to the star.
  751.             // This is a much more accurate (and expensive) distance
  752.             // calculation than the previous one which used the observer's
  753.             // position rounded off to floats.
  754.             Point3d hPos = astrocentricPosition(observer->getPosition(),
  755.                                                 star,
  756.                                                 observer->getTime());
  757.             relPos = Vec3f((float) hPos.x, (float) hPos.y, (float) hPos.z) *
  758.                 -astro::kilometersToLightYears(1.0f),
  759.             distance = relPos.length();
  760.             // Recompute apparent magnitude using new distance computation
  761.             appMag = astro::absToAppMag(star.getAbsoluteMagnitude(), distance);
  762.             float f        = RenderDistance / distance;
  763.             renderDistance = RenderDistance;
  764.             starPos        = obsPos + relPos * f;
  765.             float radius = star.getRadius();
  766.             discSizeInPixels = radius / astro::lightYearsToKilometers(distance) / pixelSize;
  767.             ++nClose;
  768.         }
  769.         // Place labels for stars brighter than the specified label threshold brightness
  770.         if ((labelMode & Renderer::StarLabels) && appMag < labelThresholdMag)
  771.         {
  772.             Vec3f starDir = relPos;
  773.             starDir.normalize();
  774.             if (dot(starDir, viewNormal) > cosFOV)
  775.             {
  776.                 char nameBuffer[Renderer::MaxLabelLength];
  777.                 starDB->getStarName(star, nameBuffer, sizeof(nameBuffer), true);
  778.                 float distr = 3.5f * (labelThresholdMag - appMag)/labelThresholdMag;
  779.                 if (distr > 1.0f)
  780.                     distr = 1.0f;
  781.                 renderer->addBackgroundAnnotation(NULL, nameBuffer,
  782.                                                   Color(Renderer::StarLabelColor, distr * Renderer::StarLabelColor.alpha()),
  783.                                                   Point3f(relPos.x, relPos.y, relPos.z));
  784.                 nLabelled++;
  785.             }
  786.         }
  787.         // Stars closer than the maximum solar system size are actually
  788.         // added to the render list and depth sorted, since they may occlude
  789.         // planets.
  790.         if (distance > MaxSolarSystemSize)
  791.         {
  792. #ifdef USE_HDR
  793.             float alpha = exposure*(faintestMag - appMag)/(faintestMag - saturationMag + 0.001f);
  794. #else
  795.             float alpha = (faintestMag - appMag) * brightnessScale + brightnessBias;
  796. #endif
  797. #ifdef DEBUG_HDR_ADAPT
  798.             minMag = max(minMag, appMag);
  799.             maxMag = min(maxMag, appMag);
  800.             minAlpha = min(minAlpha, alpha);
  801.             maxAlpha = max(maxAlpha, alpha);
  802.             ++total;
  803.             if (alpha > above)
  804.             {
  805.                 ++countAboveN;
  806.             }
  807. #endif
  808.             float pointSize;
  809.             if (useScaledDiscs)
  810.             {
  811.                 float discSize = size;
  812.                 if (alpha < 0.0f)
  813.                 {
  814.                     alpha = 0.0f;
  815.                 }
  816.                 else if (alpha > 1.0f)
  817.                 {
  818.                     discSize = min(discSize * (2.0f * alpha - 1.0f), maxDiscSize);
  819.                     alpha = 1.0f;
  820.                 }
  821.                 pointSize = discSize;
  822.             }
  823.             else
  824.             {
  825.                 alpha = clamp(alpha);
  826.                 pointSize = size;
  827. #ifdef DEBUG_HDR_ADAPT
  828.                 maxSize = max(maxSize, pointSize);
  829. #endif
  830.             }
  831.             if (starPrimitive == GL_POINTS)
  832.             {
  833.                 pointStarVertexBuffer->addStar(relPos,
  834.                                                Color(starColor, alpha),
  835.                                                pointSize);
  836.             }
  837.             else
  838.             {
  839.                 starVertexBuffer->addStar(relPos,
  840.                                           Color(starColor, alpha),
  841.                                           pointSize * renderDistance);
  842.             }
  843.             ++nRendered;
  844.             // If the star is brighter than the saturation magnitude, add a
  845.             // halo around it to make it appear more brilliant.  This is a
  846.             // hack to compensate for the limited dynamic range of monitors.
  847.             if (appMag < saturationMag)
  848.             {
  849.                 Renderer::Particle p;
  850.                 p.center = Point3f(relPos.x, relPos.y, relPos.z);
  851.                 p.size = size;
  852.                 p.color = Color(starColor, alpha);
  853.                 alpha = GlareOpacity * clamp((appMag - saturationMag) * -0.8f);
  854.                 s = renderDistance * 0.001f * (3 - (appMag - saturationMag)) * 2;
  855.                 if (s > p.size * 3)
  856.                 {
  857.                     p.size = s * 2.0f/(1.0f + FOV/fov);
  858.                 }
  859.                 else
  860.                 {
  861.                     if (s > p.size * 3)
  862.                         p.size = s * 2.0f; //2.0f/(1.0f +FOV/fov);
  863.                     else
  864.                         p.size = p.size * 3;
  865.                     p.size *= 1.6f;
  866.                 }
  867.                 p.color = Color(starColor, alpha);
  868.                 glareParticles->insert(glareParticles->end(), p);
  869.                 ++nBright;
  870.             }
  871.         }
  872.         else
  873.         {
  874.             Mat3f viewMat = observer->getOrientationf().toMatrix3();
  875.             Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
  876.             RenderListEntry rle;
  877.             rle.renderableType = RenderListEntry::RenderableStar;
  878.             rle.star = &star;
  879.             rle.isOpaque = true;
  880.             // Objects in the render list are always rendered relative to
  881.             // a viewer at the origin--this is different than for distant
  882.             // stars.
  883.             float scale = astro::lightYearsToKilometers(1.0f);
  884.             rle.position = Point3f(relPos.x * scale, relPos.y * scale, relPos.z * scale);
  885.             rle.centerZ = Vec3f(rle.position.x, rle.position.y, rle.position.z) * viewMatZ;
  886.             rle.distance = rle.position.distanceFromOrigin();
  887.             rle.radius = star.getRadius();
  888.             rle.discSizeInPixels = discSizeInPixels;
  889.             rle.appMag = appMag;
  890.             renderList->insert(renderList->end(), rle);
  891.         }
  892.     }
  893. }
  894. class PointStarRenderer : public ObjectRenderer<Star, float>
  895. {
  896.  public:
  897.     PointStarRenderer();
  898.     void process(const Star& star, float distance, float appMag);
  899.  public:
  900.     Point3d obsPos;
  901.     vector<RenderListEntry>*         renderList;
  902.     Renderer::PointStarVertexBuffer* starVertexBuffer;
  903.     Renderer::PointStarVertexBuffer* glareVertexBuffer;
  904.     const StarDatabase* starDB;
  905.     bool   useScaledDiscs;
  906.     GLenum starPrimitive;
  907.     float  maxDiscSize;
  908.     float cosFOV;
  909.     const ColorTemperatureTable* colorTemp;
  910. #ifdef DEBUG_HDR_ADAPT
  911.     float minMag;
  912.     float maxMag;
  913.     float minAlpha;
  914.     float maxAlpha;
  915.     float maxSize;
  916.     float above;
  917.     unsigned long countAboveN;
  918.     unsigned long total;
  919. #endif
  920. };
  921. PointStarRenderer::PointStarRenderer() :
  922.     ObjectRenderer<Star, float>(STAR_DISTANCE_LIMIT),
  923.     starVertexBuffer     (NULL),
  924.     useScaledDiscs       (false),
  925.     maxDiscSize          (1.0f),
  926.     cosFOV               (1.0f),
  927.     colorTemp            (NULL)
  928. {
  929. }
  930. void PointStarRenderer::process(const Star& star, float distance, float appMag)
  931. {
  932.     nProcessed++;
  933.     Point3f starPos = star.getPosition();
  934.     Vec3f   relPos((float) ((double) starPos.x - obsPos.x),
  935.                    (float) ((double) starPos.y - obsPos.y),
  936.                    (float) ((double) starPos.z - obsPos.z));
  937.     float   orbitalRadius = star.getOrbitalRadius();
  938.     bool    hasOrbit = orbitalRadius > 0.0f;
  939.     if (distance > distanceLimit)
  940.         return;
  941.     // A very rough check to see if the star may be visible: is the star in
  942.     // front of the viewer? If the star might be close (relPos.x^2 < 0.1) or
  943.     // is moving in an orbit, we'll always regard it as potentially visible.
  944.     // TODO: consider normalizing relPos and comparing relPos*viewNormal against
  945.     // cosFOV--this will cull many more stars than relPos*viewNormal, at the
  946.     // cost of a normalize per star.
  947.     if (relPos * viewNormal > 0 || relPos.x * relPos.x < 0.1f || hasOrbit)
  948.     {
  949. #ifdef HDR_COMPRESS
  950.         Color starColorFull = colorTemp->lookupColor(star.getTemperature());
  951.         Color starColor(starColorFull.red()   * 0.5f,
  952.                         starColorFull.green() * 0.5f,
  953.                         starColorFull.blue()  * 0.5f);
  954. #else
  955.         Color starColor = colorTemp->lookupColor(star.getTemperature());
  956. #endif
  957.         float renderDistance = distance;
  958.         /*float s = renderDistance * size;      Unused*/
  959.         float discSizeInPixels = 0.0f;
  960.         float orbitSizeInPixels = 0.0f;
  961.         if (hasOrbit)
  962.             orbitSizeInPixels = orbitalRadius / (distance * pixelSize);
  963.         // Special handling for stars less than one light year away . . .
  964.         // We can't just go ahead and render a nearby star in the usual way
  965.         // for two reasons:
  966.         //   * It may be clipped by the near plane
  967.         //   * It may be large enough that we should render it as a mesh
  968.         //     instead of a particle
  969.         // It's possible that the second condition might apply for stars
  970.         // further than one light year away if the star is huge, the fov is
  971.         // very small and the resolution is high.  We'll ignore this for now
  972.         // and use the most inexpensive test possible . . .
  973.         if (distance < 1.0f || orbitSizeInPixels > 1.0f)
  974.         {
  975.             // Compute the position of the observer relative to the star.
  976.             // This is a much more accurate (and expensive) distance
  977.             // calculation than the previous one which used the observer's
  978.             // position rounded off to floats.
  979.             Point3d hPos = astrocentricPosition(observer->getPosition(),
  980.                                                 star,
  981.                                                 observer->getTime());
  982.             relPos = Vec3f((float) hPos.x, (float) hPos.y, (float) hPos.z) *
  983.                 -astro::kilometersToLightYears(1.0f),
  984.             distance = relPos.length();
  985.             // Recompute apparent magnitude using new distance computation
  986.             appMag = astro::absToAppMag(star.getAbsoluteMagnitude(), distance);
  987.             float f        = RenderDistance / distance;
  988.             renderDistance = RenderDistance;
  989.             starPos        = obsPos + relPos * f;
  990.             float radius = star.getRadius();
  991.             discSizeInPixels = radius / astro::lightYearsToKilometers(distance) / pixelSize;
  992.             ++nClose;
  993.         }
  994.         // Place labels for stars brighter than the specified label threshold brightness
  995.         if ((labelMode & Renderer::StarLabels) && appMag < labelThresholdMag)
  996.         {
  997.             Vec3f starDir = relPos;
  998.             starDir.normalize();
  999.             if (dot(starDir, viewNormal) > cosFOV)
  1000.             {
  1001.                 char nameBuffer[Renderer::MaxLabelLength];
  1002.                 starDB->getStarName(star, nameBuffer, sizeof(nameBuffer), true);
  1003.                 float distr = 3.5f * (labelThresholdMag - appMag)/labelThresholdMag;
  1004.                 if (distr > 1.0f)
  1005.                     distr = 1.0f;
  1006.                 renderer->addBackgroundAnnotation(NULL, nameBuffer,
  1007.                                                   Color(Renderer::StarLabelColor, distr * Renderer::StarLabelColor.alpha()),
  1008.                                                   Point3f(relPos.x, relPos.y, relPos.z));
  1009.                 nLabelled++;
  1010.             }
  1011.         }
  1012.         // Stars closer than the maximum solar system size are actually
  1013.         // added to the render list and depth sorted, since they may occlude
  1014.         // planets.
  1015.         if (distance > MaxSolarSystemSize)
  1016.         {
  1017. #ifdef USE_HDR
  1018.             float satPoint = saturationMag;
  1019.             float alpha = exposure*(faintestMag - appMag)/(faintestMag - saturationMag + 0.001f);
  1020. #else
  1021.             float satPoint = faintestMag - (1.0f - brightnessBias) / brightnessScale; // TODO: precompute this value
  1022.             float alpha = (faintestMag - appMag) * brightnessScale + brightnessBias;
  1023. #endif
  1024. #ifdef DEBUG_HDR_ADAPT
  1025.             minMag = max(minMag, appMag);
  1026.             maxMag = min(maxMag, appMag);
  1027.             minAlpha = min(minAlpha, alpha);
  1028.             maxAlpha = max(maxAlpha, alpha);
  1029.             ++total;
  1030.             if (alpha > above)
  1031.             {
  1032.                 ++countAboveN;
  1033.             }
  1034. #endif
  1035.             if (useScaledDiscs)
  1036.             {
  1037.                 float discSize = size;
  1038.                 if (alpha < 0.0f)
  1039.                 {
  1040.                     alpha = 0.0f;
  1041.                 }
  1042.                 else if (alpha > 1.0f)
  1043.                 {
  1044.                     float discScale = min(MaxScaledDiscStarSize, (float) pow(2.0f, 0.3f * (satPoint - appMag)));
  1045.                     discSize *= discScale;
  1046.                     float glareAlpha = min(0.5f, discScale / 4.0f);
  1047.                     glareVertexBuffer->addStar(relPos, Color(starColor, glareAlpha), discSize * 3.0f);
  1048.                     alpha = 1.0f;
  1049.                 }
  1050.                 starVertexBuffer->addStar(relPos, Color(starColor, alpha), discSize);
  1051.             }
  1052.             else
  1053.             {
  1054.                 if (alpha < 0.0f)
  1055.                 {
  1056.                     alpha = 0.0f;
  1057.                 }
  1058.                 else if (alpha > 1.0f)
  1059.                 {
  1060.                     float discScale = min(100.0f, satPoint - appMag + 2.0f);
  1061.                     float glareAlpha = min(GlareOpacity, (discScale - 2.0f) / 4.0f);
  1062.                     glareVertexBuffer->addStar(relPos, Color(starColor, glareAlpha), 2.0f * discScale * size);
  1063. #ifdef DEBUG_HDR_ADAPT
  1064.                     maxSize = max(maxSize, 2.0f * discScale * size);
  1065. #endif
  1066.                 }
  1067.                 starVertexBuffer->addStar(relPos, Color(starColor, alpha), size);
  1068.             }
  1069.             ++nRendered;
  1070.         }
  1071.         else
  1072.         {
  1073.             Mat3f viewMat = observer->getOrientationf().toMatrix3();
  1074.             Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]);
  1075.             RenderListEntry rle;
  1076.             rle.renderableType = RenderListEntry::RenderableStar;
  1077.             rle.star = &star;
  1078.             // Objects in the render list are always rendered relative to
  1079.             // a viewer at the origin--this is different than for distant
  1080.             // stars.
  1081.             float scale = astro::lightYearsToKilometers(1.0f);
  1082.             rle.position = Point3f(relPos.x * scale, relPos.y * scale, relPos.z * scale);
  1083.             rle.centerZ = Vec3f(rle.position.x, rle.position.y, rle.position.z) * viewMatZ;
  1084.             rle.distance = rle.position.distanceFromOrigin();
  1085.             rle.radius = star.getRadius();
  1086.             rle.discSizeInPixels = discSizeInPixels;
  1087.             rle.appMag = appMag;
  1088.             renderList->insert(renderList->end(), rle);
  1089.         }
  1090.     }
  1091. }
  1092. template<class T> static Point3<T> microLYToLY(const Point3<T>& p)
  1093. {
  1094.     return Point3<T>(p.x * (T) 1e-6, p.y * (T) 1e-6, p.z * (T) 1e-6);
  1095. }
  1096. // Calculate the maximum field of view (from top left corner to bottom right) of
  1097. // a frustum with the specified aspect ratio (width/height) and vertical field of
  1098. // view. We follow the convention used elsewhere and use units of degrees for
  1099. // the field of view angle.
  1100. static double calcMaxFOV(double fovY_degrees, double aspectRatio)
  1101. {
  1102.     double l = 1.0 / tan(degToRad(fovY_degrees / 2.0));
  1103.     return radToDeg(atan(sqrt(aspectRatio * aspectRatio + 1.0) / l)) * 2.0;
  1104. }
  1105. void Renderer::renderStars(const StarDatabase& starDB,
  1106.                            float faintestMagNight,
  1107.                            const Observer& observer)
  1108. {
  1109.     StarRenderer starRenderer;
  1110.     Point3d obsPos = microLYToLY((Point3d) observer.getPosition());
  1111.     starRenderer.context          = context;
  1112.     starRenderer.renderer         = this;
  1113.     starRenderer.starDB           = &starDB;
  1114.     starRenderer.observer         = &observer;
  1115.     starRenderer.obsPos           = obsPos;
  1116.     starRenderer.viewNormal       = Vec3f(0, 0, -1) * observer.getOrientationf().toMatrix3();
  1117.     starRenderer.glareParticles   = &glareParticles;
  1118.     starRenderer.renderList       = &renderList;
  1119.     starRenderer.starVertexBuffer = starVertexBuffer;
  1120.     starRenderer.pointStarVertexBuffer = pointStarVertexBuffer;
  1121.     starRenderer.fov              = fov;
  1122.     starRenderer.cosFOV            = (float) cos(degToRad(calcMaxFOV(fov, (float) windowWidth / (float) windowHeight)) / 2.0f);
  1123.     // size/pixelSize =0.86 at 120deg, 1.43 at 45deg and 1.6 at 0deg.
  1124.     starRenderer.size             = pixelSize * 1.6f / corrFac;
  1125.     starRenderer.pixelSize        = pixelSize;
  1126.     starRenderer.brightnessScale  = brightnessScale * corrFac;
  1127.     starRenderer.brightnessBias   = brightnessBias;
  1128.     starRenderer.faintestMag      = faintestMag;
  1129.     starRenderer.faintestMagNight = faintestMagNight;
  1130.     starRenderer.saturationMag    = saturationMag;
  1131. #ifdef USE_HDR
  1132.     starRenderer.exposure         = exposure + brightPlus;
  1133. #endif
  1134. #ifdef DEBUG_HDR_ADAPT
  1135.     starRenderer.minMag = -100.f;
  1136.     starRenderer.maxMag =  100.f;
  1137.     starRenderer.minAlpha = 1.f;
  1138.     starRenderer.maxAlpha = 0.f;
  1139.     starRenderer.maxSize  = 0.f;
  1140.     starRenderer.above    = 1.f;
  1141.     starRenderer.countAboveN = 0L;
  1142.     starRenderer.total       = 0L;
  1143. #endif
  1144.     starRenderer.distanceLimit    = distanceLimit;
  1145.     starRenderer.labelMode        = labelMode;
  1146.     // = 1.0 at startup
  1147.     float effDistanceToScreen = mmToInches((float) REF_DISTANCE_TO_SCREEN) * pixelSize * getScreenDpi();
  1148.     starRenderer.labelThresholdMag = max(1.0f, (faintestMag - 4.0f) * (1.0f - 0.5f * (float) log10(effDistanceToScreen)));
  1149.     if (starStyle == PointStars || useNewStarRendering)
  1150.     {
  1151.         starRenderer.starPrimitive = GL_POINTS;
  1152.         //starRenderer.size          = 3.2f;
  1153.     }
  1154.     else
  1155.     {
  1156.         starRenderer.starPrimitive = GL_QUADS;
  1157.     }
  1158.     if (starStyle == ScaledDiscStars)
  1159.     {
  1160.         starRenderer.useScaledDiscs = true;
  1161.         starRenderer.brightnessScale *= 2.0f;
  1162.         starRenderer.maxDiscSize = starRenderer.size * MaxScaledDiscStarSize;
  1163.     }
  1164.     starRenderer.colorTemp = colorTemp;
  1165.     glareParticles.clear();
  1166.     starVertexBuffer->setBillboardOrientation(observer.getOrientationf());
  1167.     glEnable(GL_TEXTURE_2D);
  1168.     if (useNewStarRendering)
  1169.         gaussianDiscTex->bind();
  1170.     else
  1171.         starTex->bind();
  1172.     if (starRenderer.starPrimitive == GL_POINTS)
  1173.     {
  1174.         // Point primitives (either real points or point sprites)
  1175.         if (starStyle == PointStars)
  1176.             starRenderer.pointStarVertexBuffer->startPoints(*context);
  1177.         else
  1178.             starRenderer.pointStarVertexBuffer->startSprites(*context);
  1179.     }
  1180.     else
  1181.     {
  1182.         // Use quad primitives
  1183.         starRenderer.starVertexBuffer->start();
  1184.     }
  1185.     starDB.findVisibleStars(starRenderer,
  1186.                             Point3f((float) obsPos.x, (float) obsPos.y, (float) obsPos.z),
  1187.                             observer.getOrientationf(),
  1188.                             degToRad(fov),
  1189.                             (float) windowWidth / (float) windowHeight,
  1190.                             faintestMagNight);
  1191. #ifdef DEBUG_HDR_ADAPT
  1192.   HDR_LOG <<
  1193.       "* minMag = "    << starRenderer.minMag << ", " <<
  1194.       "maxMag = "      << starRenderer.maxMag << ", " <<
  1195.       "percent above " << starRenderer.above << " = " <<
  1196.       (100.0*(double)starRenderer.countAboveN/(double)starRenderer.total) << endl;
  1197. #endif
  1198.     if (starRenderer.starPrimitive == GL_POINTS)
  1199.         starRenderer.pointStarVertexBuffer->finish();
  1200.     else
  1201.         starRenderer.starVertexBuffer->finish();
  1202.     gaussianGlareTex->bind();
  1203.     renderParticles(glareParticles, observer.getOrientationf());
  1204. }
  1205. void Renderer::renderPointStars(const StarDatabase& starDB,
  1206.                                 float faintestMagNight,
  1207.                                 const Observer& observer)
  1208. {
  1209.     Point3d obsPos = microLYToLY((Point3d) observer.getPosition());
  1210.     PointStarRenderer starRenderer;
  1211.     starRenderer.context           = context;
  1212.     starRenderer.renderer          = this;
  1213.     starRenderer.starDB            = &starDB;
  1214.     starRenderer.observer          = &observer;
  1215.     starRenderer.obsPos            = obsPos;
  1216.     starRenderer.viewNormal        = Vec3f(0, 0, -1) * observer.getOrientationf().toMatrix3();
  1217.     starRenderer.renderList        = &renderList;
  1218.     starRenderer.starVertexBuffer  = pointStarVertexBuffer;
  1219.     starRenderer.glareVertexBuffer = glareVertexBuffer;
  1220.     starRenderer.fov               = fov;
  1221.     starRenderer.cosFOV            = (float) cos(degToRad(calcMaxFOV(fov, (float) windowWidth / (float) windowHeight)) / 2.0f);
  1222.     starRenderer.pixelSize         = pixelSize;
  1223.     starRenderer.brightnessScale   = brightnessScale * corrFac;
  1224.     starRenderer.brightnessBias    = brightnessBias;
  1225.     starRenderer.faintestMag       = faintestMag;
  1226.     starRenderer.faintestMagNight  = faintestMagNight;
  1227.     starRenderer.saturationMag     = saturationMag;
  1228. #ifdef USE_HDR
  1229.     starRenderer.exposure          = exposure + brightPlus;
  1230. #endif
  1231. #ifdef DEBUG_HDR_ADAPT
  1232.     starRenderer.minMag = -100.f;
  1233.     starRenderer.maxMag =  100.f;
  1234.     starRenderer.minAlpha = 1.f;
  1235.     starRenderer.maxAlpha = 0.f;
  1236.     starRenderer.maxSize  = 0.f;
  1237.     starRenderer.above    = 1.0f;
  1238.     starRenderer.countAboveN = 0L;
  1239.     starRenderer.total       = 0L;
  1240. #endif
  1241.     starRenderer.distanceLimit     = distanceLimit;
  1242.     starRenderer.labelMode         = labelMode;
  1243.     // = 1.0 at startup
  1244.     float effDistanceToScreen = mmToInches((float) REF_DISTANCE_TO_SCREEN) * pixelSize * getScreenDpi();
  1245.     starRenderer.labelThresholdMag = 1.2f * max(1.0f, (faintestMag - 4.0f) * (1.0f - 0.5f * (float) log10(effDistanceToScreen)));
  1246.     starRenderer.size          = BaseStarDiscSize;
  1247.     if (starStyle == ScaledDiscStars)
  1248.     {
  1249.         starRenderer.useScaledDiscs = true;
  1250.         starRenderer.brightnessScale *= 2.0f;
  1251.         starRenderer.maxDiscSize = starRenderer.size * MaxScaledDiscStarSize;
  1252.     }
  1253.     else if (starStyle == FuzzyPointStars)
  1254.     {
  1255.         starRenderer.brightnessScale *= 1.0f;
  1256.     }
  1257.     starRenderer.colorTemp = colorTemp;
  1258.     glEnable(GL_TEXTURE_2D);
  1259.     gaussianDiscTex->bind();
  1260.     starRenderer.starVertexBuffer->setTexture(gaussianDiscTex);
  1261.     starRenderer.glareVertexBuffer->setTexture(gaussianGlareTex);
  1262.     starRenderer.glareVertexBuffer->startSprites(*context);
  1263.     if (starStyle == PointStars)
  1264.         starRenderer.starVertexBuffer->startPoints(*context);
  1265.     else
  1266.         starRenderer.starVertexBuffer->startSprites(*context);
  1267.     starDB.findVisibleStars(starRenderer,
  1268.                             Point3f((float) obsPos.x, (float) obsPos.y, (float) obsPos.z),
  1269.                             observer.getOrientationf(),
  1270.                             degToRad(fov),
  1271.                             (float) windowWidth / (float) windowHeight,
  1272.                             faintestMagNight);
  1273.     starRenderer.starVertexBuffer->render();
  1274.     starRenderer.glareVertexBuffer->render();
  1275.     starRenderer.starVertexBuffer->finish();
  1276.     starRenderer.glareVertexBuffer->finish();
  1277. }
  1278. class DSORenderer : public ObjectRenderer<DeepSkyObject*, double>
  1279. {
  1280.  public:
  1281.     DSORenderer();
  1282.     void process(DeepSkyObject* const &, double, float);
  1283.  public:
  1284.     Point3d      obsPos;
  1285.     DSODatabase* dsoDB;
  1286.     Frustum      frustum;
  1287.     Mat3f orientationMatrix;
  1288.     int wWidth;
  1289.     int wHeight;
  1290.     double avgAbsMag;
  1291. };
  1292. DSORenderer::DSORenderer() :
  1293.     ObjectRenderer<DeepSkyObject*, double>(DSO_OCTREE_ROOT_SIZE),
  1294.     frustum(degToRad(45.0f), 1.0f, 1.0f)
  1295. {
  1296. }
  1297. void DSORenderer::process(DeepSkyObject* const & dso,
  1298.                           double distanceToDSO,
  1299.                           float  absMag)
  1300. {
  1301.     if (distanceToDSO > distanceLimit)
  1302.         return;
  1303.     
  1304.     Point3d dsoPos = dso->getPosition();
  1305.     Vec3f   relPos = Vec3f((float)(dsoPos.x - obsPos.x),
  1306.                            (float)(dsoPos.y - obsPos.y),
  1307.                            (float)(dsoPos.z - obsPos.z));
  1308.     Point3d center = Point3d(0.0f, 0.0f, 0.0f) + relPos * orientationMatrix;
  1309. double enhance = 4.0, pc10 = 32.6167;
  1310. // The parameter 'enhance' adjusts the DSO brightness as viewed from "inside" 
  1311. // (e.g. MilkyWay as seen from Earth). It provides an enhanced apparent  core  
  1312. // brightness appMag  ~ absMag - enhance. 'enhance' thus serves to uniformly  
  1313. // enhance the too low sprite luminosity at close distance.
  1314.     float  appMag = (distanceToDSO >= pc10)? (float) astro::absToAppMag((double) absMag, distanceToDSO): absMag + (float) (enhance * tanh(distanceToDSO/pc10 - 1.0));
  1315.     
  1316. // Test the object's bounding sphere against the view frustum. If we
  1317.     // avoid this stage, overcrowded octree cells may hit performance badly:
  1318.     // each object (even if it's not visible) would be sent to the OpenGL
  1319.     // pipeline.
  1320.     
  1321.     if ((renderFlags & dso->getRenderMask()) && dso->isVisible())
  1322.     {
  1323. double dsoRadius = dso->getBoundingSphereRadius(); 
  1324.         if (frustum.testSphere(center, dsoRadius) != Frustum::Outside)
  1325.         {
  1326.             // Input: display looks satisfactory for 0.2 < brightness < O(1.0)
  1327.             // Ansatz: brightness = a - b * appMag(distanceToDSO), emulating eye sensitivity...
  1328.             // determine a,b such that
  1329.             // a - b * absMag = absMag / avgAbsMag ~ 1; a - b * faintestMag = 0.2.
  1330.             // The 2nd eq. guarantees that the faintest galaxies are still visible.            
  1331.             
  1332. if(!strcmp(dso->getObjTypeName(),"globular"))
  1333. avgAbsMag =  -6.86;    // average over 150  globulars in globulars.dsc.
  1334. else if (!strcmp(dso->getObjTypeName(),"galaxy"))
  1335. avgAbsMag = -19.04;    // average over 10937 galaxies in galaxies.dsc.
  1336.             
  1337.             float r   = absMag / (float) avgAbsMag;   
  1338. float brightness = r - (r - 0.2f) * (absMag - appMag) / (absMag - faintestMag);
  1339. // obviously, brightness(appMag = absMag) = r and 
  1340. // brightness(appMag = faintestMag) = 0.2, as desired.
  1341. brightness = 2.3f * brightness * (faintestMag - 4.75f) / renderer->getFaintestAM45deg();
  1342. #ifdef USE_HDR
  1343.             brightness *= exposure;
  1344. #endif
  1345.             if (brightness < 0)
  1346. brightness = 0;
  1347. if (dsoRadius < 1000.0)
  1348.             {
  1349.                 // Small objects may be prone to clipping; give them special
  1350.                 // handling.  We don't want to always set the projection
  1351.                 // matrix, since that could be expensive with large galaxy
  1352.                 // catalogs.
  1353.                 float nearZ = (float) (distanceToDSO / 2);
  1354.                 float farZ  = (float) (distanceToDSO + dsoRadius * 2 * CubeCornerToCenterDistance);
  1355.                 if (nearZ < dsoRadius * 0.001)
  1356.                 {
  1357.                     nearZ = (float) (dsoRadius * 0.001);
  1358.                     farZ  = nearZ * 10000.0f;
  1359.                 }
  1360.                 glMatrixMode(GL_PROJECTION);
  1361.                 glPushMatrix();
  1362.                 glLoadIdentity();
  1363.                 gluPerspective(fov,
  1364.                                (float) wWidth / (float) wHeight,
  1365.                                nearZ,
  1366.                                farZ);
  1367.                 glMatrixMode(GL_MODELVIEW);
  1368.             }
  1369.             glPushMatrix();
  1370.             glTranslate(relPos);
  1371.             dso->render(*context,
  1372.                         relPos,
  1373.                         observer->getOrientationf(),
  1374.                         (float) brightness,
  1375.                         pixelSize);
  1376.             glPopMatrix();
  1377. #if 1
  1378.             if (dsoRadius < 1000.0)
  1379.             {
  1380.                 glMatrixMode(GL_PROJECTION);
  1381.                 glPopMatrix();
  1382.                 glMatrixMode(GL_MODELVIEW);
  1383.             }
  1384. #endif
  1385.         } // frustum test
  1386.     } // renderFlags check
  1387.     // Only render those labels that are in front of the camera:
  1388.     // Place labels for DSOs brighter than the specified label threshold brightness
  1389.     //
  1390.     unsigned int labelMask = dso->getLabelMask();
  1391.     if ((labelMask & labelMode) && dot(relPos, viewNormal) > 0 && dso->isVisible())
  1392.     {
  1393.         Color labelColor;
  1394.         float appMagEff = 6.0f;
  1395.         float step = 6.0f;
  1396.         float symbolSize = 0.0f;
  1397.         MarkerRepresentation* rep = NULL;
  1398.         // Use magnitude based fading for galaxies, and distance based
  1399.         // fading for nebulae and open clusters.
  1400.         switch (labelMask)
  1401.         {
  1402.         case Renderer::NebulaLabels:
  1403.             rep = &renderer->nebulaRep;
  1404.             labelColor = Renderer::NebulaLabelColor;
  1405.             appMagEff = astro::absToAppMag(-7.5f, (float) distanceToDSO);
  1406.             symbolSize = (float) (dso->getRadius() / distanceToDSO) / pixelSize;
  1407.             step = 6.0f;
  1408.             break;
  1409.         case Renderer::OpenClusterLabels:
  1410.             rep = &renderer->openClusterRep;
  1411.             labelColor = Renderer::OpenClusterLabelColor;
  1412.             appMagEff = astro::absToAppMag(-6.0f, (float) distanceToDSO);
  1413.             symbolSize = (float) (dso->getRadius() / distanceToDSO) / pixelSize;
  1414.             step = 4.0f;
  1415.             break;
  1416.         case Renderer::GalaxyLabels:
  1417.             labelColor = Renderer::GalaxyLabelColor;
  1418.             appMagEff = appMag;
  1419.             step = 6.0f;
  1420.             break;
  1421. case Renderer::GlobularLabels:
  1422.             labelColor = Renderer::GlobularLabelColor;
  1423.             appMagEff = appMag;
  1424.             step = 3.0f;
  1425.             break;         
  1426.         default:
  1427.             // Unrecognized object class
  1428.             labelColor = Color::White;
  1429.             appMagEff = appMag;
  1430.             step = 6.0f;
  1431.             break;
  1432.         }
  1433.         if (appMagEff < labelThresholdMag)
  1434.         {
  1435.             // introduce distance dependent label transparency.
  1436.             float distr = step * (labelThresholdMag - appMagEff) / labelThresholdMag;
  1437.             if (distr > 1.0f)
  1438.                 distr = 1.0f;
  1439.             renderer->addBackgroundAnnotation(rep,
  1440.                                               dsoDB->getDSOName(dso, true),
  1441.                                               Color(labelColor, distr * labelColor.alpha()),
  1442.                                               Point3f(relPos.x, relPos.y, relPos.z),
  1443.                                               Renderer::AlignLeft, Renderer::VerticalAlignCenter, symbolSize);
  1444.         }
  1445.     }
  1446. }
  1447. void Renderer::renderDeepSkyObjects(const Universe&  universe,
  1448.                                     const Observer& observer,
  1449.                                     const float     faintestMagNight)
  1450. {
  1451.     DSORenderer dsoRenderer;
  1452.     Point3d obsPos    = (Point3d) observer.getPosition();
  1453.     obsPos.x         *= 1e-6;
  1454.     obsPos.y         *= 1e-6;
  1455.     obsPos.z         *= 1e-6;
  1456.     DSODatabase* dsoDB  = universe.getDSOCatalog();
  1457.     dsoRenderer.context          = context;
  1458.     dsoRenderer.renderer         = this;
  1459.     dsoRenderer.dsoDB            = dsoDB;
  1460.     dsoRenderer.orientationMatrix = conjugate(observer.getOrientationf()).toMatrix3();
  1461.     dsoRenderer.observer          = &observer;
  1462.     dsoRenderer.obsPos            = obsPos;
  1463.     dsoRenderer.viewNormal        = Vec3f(0, 0, -1) * observer.getOrientationf().toMatrix3();
  1464.     dsoRenderer.fov              = fov;
  1465.     // size/pixelSize =0.86 at 120deg, 1.43 at 45deg and 1.6 at 0deg.
  1466.     dsoRenderer.size             = pixelSize * 1.6f / corrFac;
  1467.     dsoRenderer.pixelSize        = pixelSize;
  1468.     dsoRenderer.brightnessScale  = brightnessScale * corrFac;
  1469.     dsoRenderer.brightnessBias   = brightnessBias;
  1470.     dsoRenderer.avgAbsMag        = dsoDB->getAverageAbsoluteMagnitude();
  1471.     dsoRenderer.faintestMag      = faintestMag;
  1472.     dsoRenderer.faintestMagNight = faintestMagNight;
  1473.     dsoRenderer.saturationMag    = saturationMag;
  1474. #ifdef USE_HDR
  1475.     dsoRenderer.exposure         = exposure + brightPlus;
  1476. #endif
  1477.     dsoRenderer.renderFlags      = renderFlags;
  1478.     dsoRenderer.labelMode        = labelMode;
  1479.     dsoRenderer.wWidth           = windowWidth;
  1480.     dsoRenderer.wHeight          = windowHeight;
  1481.     dsoRenderer.frustum = Frustum(degToRad(fov),
  1482.                         (float) windowWidth / (float) windowHeight,
  1483.                         MinNearPlaneDistance);
  1484.     // Use pixelSize * screenDpi instead of FoV, to eliminate windowHeight dependence.
  1485.     // = 1.0 at startup
  1486.     float effDistanceToScreen = mmToInches((float) REF_DISTANCE_TO_SCREEN) * pixelSize * getScreenDpi();
  1487.     dsoRenderer.labelThresholdMag = 2.0f * max(1.0f, (faintestMag - 4.0f) * (1.0f - 0.5f * (float) log10(effDistanceToScreen)));
  1488.     galaxyRep      = MarkerRepresentation(MarkerRepresentation::Triangle, 8.0f, GalaxyLabelColor);
  1489.     nebulaRep      = MarkerRepresentation(MarkerRepresentation::Square,   8.0f, NebulaLabelColor);
  1490.     openClusterRep = MarkerRepresentation(MarkerRepresentation::Circle,   8.0f, OpenClusterLabelColor);
  1491.     globularRep    = MarkerRepresentation(MarkerRepresentation::Circle,   8.0f, OpenClusterLabelColor);
  1492.     // Render any line primitives with smooth lines
  1493.     // (mostly to make graticules look good.)
  1494.     if ((renderFlags & ShowSmoothLines) != 0)
  1495.         enableSmoothLines();
  1496.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  1497.     dsoDB->findVisibleDSOs(dsoRenderer,
  1498.                            obsPos,
  1499.                            observer.getOrientationf(),
  1500.                            degToRad(fov),
  1501.                            (float) windowWidth / (float) windowHeight,
  1502.                            2 * faintestMagNight);
  1503.     if ((renderFlags & ShowSmoothLines) != 0)
  1504.         disableSmoothLines();
  1505. }
  1506. static Vec3d toStandardCoords(const Vec3d& v)
  1507. {
  1508.     return Vec3d(v.x, -v.z, v.y);
  1509. }
  1510. void Renderer::renderSkyGrids(const Observer& observer)
  1511. {
  1512.     if (renderFlags & ShowCelestialSphere)
  1513.     {
  1514.         SkyGrid grid;
  1515.         grid.setOrientation(Quatd::xrotation(astro::J2000Obliquity));
  1516.         grid.setLineColor(EquatorialGridColor);
  1517.         grid.setLabelColor(EquatorialGridLabelColor);
  1518.         grid.render(*this, observer, windowWidth, windowHeight);
  1519.     }
  1520.     if (renderFlags & ShowGalacticGrid)
  1521.     {
  1522.         SkyGrid galacticGrid;
  1523.         galacticGrid.setOrientation(~(astro::eclipticToEquatorial() * astro::equatorialToGalactic()));
  1524.         galacticGrid.setLineColor(GalacticGridColor);
  1525.         galacticGrid.setLabelColor(GalacticGridLabelColor);
  1526.         galacticGrid.setLongitudeUnits(SkyGrid::LongitudeDegrees);
  1527.         galacticGrid.render(*this, observer, windowWidth, windowHeight);
  1528.     }
  1529.     if (renderFlags & ShowEclipticGrid)
  1530.     {
  1531.         SkyGrid grid;
  1532.         grid.setOrientation(Quatd(1.0));
  1533.         grid.setLineColor(EclipticGridColor);
  1534.         grid.setLabelColor(EclipticGridLabelColor);
  1535.         grid.setLongitudeUnits(SkyGrid::LongitudeDegrees);
  1536.         grid.render(*this, observer, windowWidth, windowHeight);
  1537.     }
  1538.     if (renderFlags & ShowHorizonGrid)
  1539.     {
  1540.         double tdb = observer.getTime();
  1541.         const ObserverFrame* frame = observer.getFrame();
  1542.         Body* body = frame->getRefObject().body();
  1543.         
  1544.         if (body)
  1545.         {
  1546.             SkyGrid grid;
  1547.             grid.setLineColor(HorizonGridColor);
  1548.             grid.setLabelColor(HorizonGridLabelColor);
  1549.             grid.setLongitudeUnits(SkyGrid::LongitudeDegrees);
  1550.             grid.setLongitudeDirection(SkyGrid::IncreasingClockwise);
  1551.             
  1552.             Vec3d zenithDirection = observer.getPosition() - body->getPosition(tdb);
  1553.             zenithDirection.normalize();
  1554.             
  1555.             Vec3d northPole = Vec3d(0.0, 1.0, 0.0) * (body->getEclipticToEquatorial(tdb)).toMatrix3();
  1556.             zenithDirection = toStandardCoords(zenithDirection);
  1557.             northPole = toStandardCoords(northPole);
  1558.             
  1559.             Vec3d v = zenithDirection ^ northPole;
  1560.             // Horizontal coordinate system not well defined when observer
  1561.             // is at a pole.
  1562.             double tolerance = 1.0e-10;
  1563.             if (v.length() > tolerance && v.length() < 1.0 - tolerance)
  1564.             {
  1565.                 v.normalize();
  1566.                 Vec3d u = v ^ zenithDirection;
  1567.                 
  1568.                 Mat3d m = Mat3d(u, v, zenithDirection);
  1569.                 Quatd q = Quatd::matrixToQuaternion(m);
  1570.                 grid.setOrientation(q);
  1571.                 
  1572.                 grid.render(*this, observer, windowWidth, windowHeight);
  1573.             }
  1574.         }
  1575.     }
  1576.     if (renderFlags & ShowEcliptic)
  1577.     {
  1578.         // Draw the J2000.0 ecliptic; trivial, since this forms the basis for
  1579.         // Celestia's coordinate system.
  1580.         const int subdivision = 200;
  1581.         glColor(EclipticColor);
  1582.         glBegin(GL_LINE_LOOP);
  1583.         for (int i = 0; i < subdivision; i++)
  1584.         {
  1585.             double theta = (double) i / (double) subdivision * 2 * PI;
  1586.             glVertex3f((float) cos(theta) * 1000.0f, 0.0f, (float) sin(theta) * 1000.0f);
  1587.         }
  1588.         glEnd();
  1589.     }
  1590. }
  1591. /*! Draw an arrow at the view border pointing to an offscreen selection. This method
  1592.  *  should only be called when the selection lies outside the view frustum.
  1593.  */
  1594. void Renderer::renderSelectionPointer(const Observer& observer,
  1595.                                       double now,
  1596.                                       const Frustum& viewFrustum,
  1597.                                       const Selection& sel)
  1598. {
  1599.     const float cursorDistance = 20.0f;
  1600.     if (sel.empty())
  1601.         return;
  1602.     Mat3f m = observer.getOrientationf().toMatrix3();
  1603.     Vec3f u = Vec3f(1, 0, 0) * m;
  1604.     Vec3f v = Vec3f(0, 1, 0) * m;
  1605.     // Get the position of the cursor relative to the eye
  1606.     Vec3d position = (sel.getPosition(now) - observer.getPosition()) * astro::microLightYearsToKilometers(1.0);
  1607.     double distance = position.length();
  1608.     bool isVisible = viewFrustum.testSphere(Point3d(0, 0, 0) + position, sel.radius()) != Frustum::Outside;
  1609.     position *= cursorDistance / distance;
  1610. #ifdef USE_HDR
  1611.     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
  1612. #endif
  1613.     glDisable(GL_DEPTH_TEST);
  1614.     glDisable(GL_TEXTURE_2D);
  1615.     glEnable(GL_BLEND);
  1616.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1617.     if (!isVisible)
  1618.     {
  1619.         double viewAspectRatio = (double) windowWidth / (double) windowHeight;
  1620.         double vfov = observer.getFOV();
  1621.         float h = (float) tan(vfov / 2);
  1622.         float w = (float) (h * viewAspectRatio);
  1623.         float diag = std::sqrt(h * h + w * w);
  1624.         Vec3f posf((float) position.x, (float) position.y, (float) position.z);
  1625.         posf *= (1.0f / cursorDistance);
  1626.         float x = u * posf;
  1627.         float y = v * posf;
  1628.         float angle = std::atan2(y, x);
  1629.         float c = std::cos(angle);
  1630.         float s = std::sin(angle);
  1631.         float t = 1.0f;
  1632.         float x0 = c * diag;
  1633.         float y0 = s * diag;
  1634.         if (std::abs(x0) < w)
  1635.             t = h / std::abs(y0);
  1636.         else
  1637.             t = w / std::abs(x0);
  1638.         x0 *= t;
  1639.         y0 *= t;
  1640.         glColor(SelectionCursorColor, 0.6f);
  1641.         Vec3f center = Vec3f(0, 0, -1) * m;
  1642.         glPushMatrix();
  1643.         glTranslatef((float) center.x, (float) center.y, (float) center.z);
  1644.         Vec3f p0(0.0f, 0.0f, 0.0f);
  1645.         Vec3f p1(-20.0f * pixelSize,  6.0f * pixelSize, 0.0f);
  1646.         Vec3f p2(-20.0f * pixelSize, -6.0f * pixelSize, 0.0f);
  1647.         glBegin(GL_TRIANGLES);
  1648.         glVertex((p0.x * c - p0.y * s + x0) * u + (p0.x * s + p0.y * c + y0) * v); 
  1649.         glVertex((p1.x * c - p1.y * s + x0) * u + (p1.x * s + p1.y * c + y0) * v); 
  1650.         glVertex((p2.x * c - p2.y * s + x0) * u + (p2.x * s + p2.y * c + y0) * v); 
  1651.         glEnd();
  1652.         glPopMatrix();
  1653.     }
  1654. #ifdef USE_HDR
  1655.     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  1656. #endif
  1657.     glEnable(GL_TEXTURE_2D);
  1658. }
  1659. void Renderer::labelConstellations(const AsterismList& asterisms,
  1660.                                    const Observer& observer)
  1661. {
  1662.     Point3f observerPos = (Point3f) observer.getPosition();
  1663.     for (AsterismList::const_iterator iter = asterisms.begin();
  1664.          iter != asterisms.end(); iter++)
  1665.     {
  1666.         Asterism* ast = *iter;
  1667.         if (ast->getChainCount() > 0 && ast->getActive())
  1668.         {
  1669.             const Asterism::Chain& chain = ast->getChain(0);
  1670.             if (chain.size() > 0)
  1671.             {
  1672.                 // The constellation label is positioned at the average
  1673.                 // position of all stars in the first chain.  This usually
  1674.                 // gives reasonable results.
  1675.                 Vec3f avg(0, 0, 0);
  1676.                 for (Asterism::Chain::const_iterator iter = chain.begin();
  1677.                      iter != chain.end(); iter++)
  1678.                     avg += (*iter - Point3f(0, 0, 0));
  1679.                 avg = avg / (float) chain.size();
  1680.                 // Draw all constellation labels at the same distance
  1681.                 avg.normalize();
  1682.                 avg = avg * 1.0e10f;
  1683.                 Vec3f rpos = Point3f(avg.x, avg.y, avg.z) - observerPos;
  1684.                 if ((observer.getOrientationf().toMatrix3() * rpos).z < 0)
  1685.                 {
  1686.                     // We'll linearly fade the labels as a function of the
  1687.                     // observer's distance to the origin of coordinates:
  1688.                     float opacity = 1.0f;
  1689.                     float dist = observerPos.distanceFromOrigin();
  1690.                     if (dist > MaxAsterismLabelsConstDist)
  1691.                     {
  1692.                         opacity = clamp((MaxAsterismLabelsConstDist - dist) /
  1693.                                         (MaxAsterismLabelsDist - MaxAsterismLabelsConstDist) + 1);
  1694.                     }
  1695.                     // Use the default label color unless the constellation has an
  1696.                     // override color set.
  1697.                     Color labelColor = ConstellationLabelColor;
  1698.                     if (ast->isColorOverridden())
  1699.                         labelColor = ast->getOverrideColor();
  1700.                     addBackgroundAnnotation(NULL,
  1701.                                             ast->getName((labelMode & I18nConstellationLabels) != 0),
  1702.                                             Color(labelColor, opacity),
  1703.                                             Point3f(rpos.x, rpos.y, rpos.z),
  1704.                                             AlignCenter, VerticalAlignCenter);
  1705.                 }
  1706.             }
  1707.         }
  1708.     }
  1709. }
  1710. void Renderer::renderParticles(const vector<Particle>& particles,
  1711.                                Quatf orientation)
  1712. {
  1713.     int nParticles = particles.size();
  1714.     {
  1715.         Mat3f m = orientation.toMatrix3();
  1716.         Vec3f v0 = Vec3f(-1, -1, 0) * m;
  1717.         Vec3f v1 = Vec3f( 1, -1, 0) * m;
  1718.         Vec3f v2 = Vec3f( 1,  1, 0) * m;
  1719.         Vec3f v3 = Vec3f(-1,  1, 0) * m;
  1720.         glBegin(GL_QUADS);
  1721.         for (int i = 0; i < nParticles; i++)
  1722.         {
  1723.             Point3f center = particles[i].center;
  1724.             float size = particles[i].size;
  1725.             glColor(particles[i].color);
  1726.             glTexCoord2f(0, 1);
  1727.             glVertex(center + (v0 * size));
  1728.             glTexCoord2f(1, 1);
  1729.             glVertex(center + (v1 * size));
  1730.             glTexCoord2f(1, 0);
  1731.             glVertex(center + (v2 * size));
  1732.             glTexCoord2f(0, 0);
  1733.             glVertex(center + (v3 * size));
  1734.         }
  1735.         glEnd();
  1736.     }
  1737. }
  1738. static void renderCrosshair(float pixelSize, double tsec)
  1739. {
  1740.     const float cursorMinRadius = 6.0f;
  1741.     const float cursorRadiusVariability = 4.0f;
  1742.     const float minCursorWidth = 7.0f;
  1743.     const float cursorPulsePeriod = 1.5f;
  1744.     float selectionSizeInPixels = pixelSize;
  1745.     float cursorRadius = selectionSizeInPixels + cursorMinRadius;
  1746.     cursorRadius += cursorRadiusVariability * (float) (0.5 + 0.5 * std::sin(tsec * 2 * PI / cursorPulsePeriod));
  1747.     // Enlarge the size of the cross hair sligtly when the selection
  1748.     // has a large apparent size
  1749.     float cursorGrow = max(1.0f, min(2.5f, (selectionSizeInPixels - 10.0f) / 100.0f));
  1750.     float h = 2.0f * cursorGrow;
  1751.     float cursorWidth = minCursorWidth * cursorGrow;
  1752.     float r0 = cursorRadius;
  1753.     float r1 = cursorRadius + cursorWidth;
  1754.     const unsigned int markCount = 4;
  1755.     Vec3f p0(r0, 0.0f, 0.0f);
  1756.     Vec3f p1(r1, -h, 0.0f);
  1757.     Vec3f p2(r1,  h, 0.0f);
  1758.     glBegin(GL_TRIANGLES);
  1759.     for (unsigned int i = 0; i < markCount; i++)
  1760.     {
  1761.         float theta = (float) (PI / 4.0) + (float) i / (float) markCount * (float) (2 * PI);
  1762.         float c = std::cos(theta);
  1763.         float s = std::sin(theta);
  1764.         glVertex3f(p0.x * c - p0.y * s, p0.x * s + p0.y * c, 0.0f); 
  1765.         glVertex3f(p1.x * c - p1.y * s, p1.x * s + p1.y * c, 0.0f); 
  1766.         glVertex3f(p2.x * c - p2.y * s, p2.x * s + p2.y * c, 0.0f); 
  1767.     }
  1768.     glEnd();
  1769. }
  1770. void Renderer::renderAnnotations(const vector<Annotation>& annotations, FontStyle fs)
  1771. {
  1772.     if (font[fs] == NULL)
  1773.         return;
  1774.     // Enable line smoothing for rendering symbols
  1775.     if ((renderFlags & ShowSmoothLines) != 0)
  1776.         enableSmoothLines();
  1777.     
  1778. #ifdef USE_HDR
  1779.     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
  1780. #endif
  1781.     glEnable(GL_TEXTURE_2D);
  1782.     font[fs]->bind();
  1783.     glEnable(GL_BLEND);
  1784.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1785.     glMatrixMode(GL_PROJECTION);
  1786.     glPushMatrix();
  1787.     glLoadIdentity();
  1788.     gluOrtho2D(0, windowWidth, 0, windowHeight);
  1789.     glMatrixMode(GL_MODELVIEW);
  1790.     glPushMatrix();
  1791.     glLoadIdentity();
  1792.     glTranslatef(GLfloat((int) (windowWidth / 2)),
  1793.                  GLfloat((int) (windowHeight / 2)), 0);
  1794.     for (int i = 0; i < (int) annotations.size(); i++)
  1795.     {
  1796.         if (annotations[i].markerRep != NULL)
  1797.         {
  1798.             glPushMatrix();
  1799.             const MarkerRepresentation& markerRep = *annotations[i].markerRep;
  1800.             float size = markerRep.size();
  1801.             if (annotations[i].size > 0.0f)
  1802.             {
  1803.                 size = annotations[i].size;
  1804.             }
  1805.             glColor(annotations[i].color);
  1806.             glTranslatef((GLfloat) (int) annotations[i].position.x,
  1807.                          (GLfloat) (int) annotations[i].position.y, 0.0f);
  1808.             glDisable(GL_TEXTURE_2D);
  1809.             if (markerRep.symbol() == MarkerRepresentation::Crosshair)
  1810.                 renderCrosshair(size, realTime);
  1811.             else
  1812.                 markerRep.render(size);
  1813.             glEnable(GL_TEXTURE_2D);
  1814.             
  1815.             if (!markerRep.label().empty())
  1816.             {
  1817.                 int labelOffset = (int) markerRep.size() / 2;
  1818.                 glTranslatef(labelOffset + PixelOffset, -labelOffset - font[fs]->getHeight() + PixelOffset, 0.0f);
  1819.                 font[fs]->render(markerRep.label());
  1820.             }  
  1821.             glPopMatrix();
  1822.         }
  1823.         if (annotations[i].labelText[0] != '')
  1824.         {
  1825.             glPushMatrix();
  1826.             int labelWidth = 0;
  1827.             int hOffset = 2;
  1828.             int vOffset = 0;
  1829.             
  1830.             switch (annotations[i].halign)
  1831.             {
  1832.             case AlignCenter:
  1833.                 labelWidth = (font[fs]->getWidth(annotations[i].labelText));
  1834.                 hOffset = -labelWidth / 2;
  1835.                 break;
  1836.             
  1837.             case AlignRight:
  1838.                 labelWidth = (font[fs]->getWidth(annotations[i].labelText));
  1839.                 hOffset = -(labelWidth + 2);
  1840.                 break;
  1841.             
  1842.             case AlignLeft:
  1843.                 if (annotations[i].markerRep != NULL)
  1844.                     hOffset = 2 + (int) annotations[i].markerRep->size() / 2;
  1845.                 break;
  1846.             }
  1847.             
  1848.             switch (annotations[i].valign)
  1849.             {
  1850.             case AlignCenter:
  1851.                 vOffset = -font[fs]->getHeight() / 2;
  1852.                 break;
  1853.             case VerticalAlignTop:
  1854.                 vOffset = -font[fs]->getHeight();
  1855.                 break;
  1856.             case VerticalAlignBottom:
  1857.                 vOffset = 0;
  1858.                 break;
  1859.             }
  1860.             
  1861.             glColor(annotations[i].color);
  1862.             glTranslatef((int) annotations[i].position.x + hOffset + PixelOffset,
  1863.                          (int) annotations[i].position.y + vOffset + PixelOffset, 0.0f);
  1864.             // EK TODO: Check where to replace (see '_(' above)
  1865.             font[fs]->render(annotations[i].labelText);
  1866.             glPopMatrix();
  1867.         }
  1868.     }
  1869.     glPopMatrix();
  1870.     glMatrixMode(GL_PROJECTION);
  1871.     glPopMatrix();
  1872.     glMatrixMode(GL_MODELVIEW);
  1873. #ifdef USE_HDR
  1874.     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  1875. #endif
  1876.     if ((renderFlags & ShowSmoothLines) != 0)
  1877.         disableSmoothLines();
  1878. }
  1879. void
  1880. Renderer::renderBackgroundAnnotations(FontStyle fs)
  1881. {
  1882.     glEnable(GL_DEPTH_TEST);
  1883.     renderAnnotations(backgroundAnnotations, fs);
  1884.     glDisable(GL_DEPTH_TEST);
  1885.     
  1886.     clearAnnotations(backgroundAnnotations);
  1887. }
  1888. void
  1889. Renderer::renderForegroundAnnotations(FontStyle fs)
  1890. {
  1891.     glDisable(GL_DEPTH_TEST);
  1892.     renderAnnotations(foregroundAnnotations, fs);
  1893.     
  1894.     clearAnnotations(foregroundAnnotations);
  1895. }
  1896. vector<Renderer::Annotation>::iterator
  1897. Renderer::renderSortedAnnotations(vector<Annotation>::iterator iter,
  1898.                                   float nearDist,
  1899.                                   float farDist,
  1900.                                   FontStyle fs)
  1901. {
  1902.     if (font[fs] == NULL)
  1903.         return iter;
  1904.     glEnable(GL_DEPTH_TEST);
  1905.     glEnable(GL_TEXTURE_2D);
  1906.     font[fs]->bind();
  1907.     glEnable(GL_BLEND);
  1908.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1909.     glMatrixMode(GL_PROJECTION);
  1910.     glPushMatrix();
  1911.     glLoadIdentity();
  1912.     gluOrtho2D(0, windowWidth, 0, windowHeight);
  1913.     glMatrixMode(GL_MODELVIEW);
  1914.     glPushMatrix();
  1915.     glLoadIdentity();
  1916.     glTranslatef(GLfloat((int) (windowWidth / 2)),
  1917.                  GLfloat((int) (windowHeight / 2)), 0);
  1918.     // Precompute values that will be used to generate the normalized device z value;
  1919.     // we're effectively just handling the projection instead of OpenGL. We use an orthographic
  1920.     // projection matrix in order to get the label text position exactly right but need to mimic
  1921.     // the depth coordinate generation of a perspective projection.
  1922.     float d1 = -(farDist + nearDist) / (farDist - nearDist);
  1923.     float d2 = -2.0f * nearDist * farDist / (farDist - nearDist);
  1924.     for (; iter != depthSortedAnnotations.end() && iter->position.z > nearDist; iter++)
  1925.     {
  1926.         // Compute normalized device z
  1927.         float ndc_z = d1 + d2 / -iter->position.z;
  1928.         ndc_z = min(1.0f, max(-1.0f, ndc_z)); // Clamp to [-1,1]
  1929.         // Offsets to left align label
  1930.         int labelHOffset = 0;
  1931.         int labelVOffset = 0;
  1932.         glPushMatrix();
  1933.         if (iter->markerRep != NULL)
  1934.         {
  1935.             const MarkerRepresentation& markerRep = *iter->markerRep;
  1936.             float size = markerRep.size();
  1937.             if (iter->size > 0.0f)
  1938.             {
  1939.                 size = iter->size;
  1940.             }
  1941.             
  1942.             glTranslatef((GLfloat) (int) iter->position.x, (GLfloat) (int) iter->position.y, ndc_z);
  1943.             glColor(iter->color);
  1944.                         
  1945.             glDisable(GL_TEXTURE_2D);
  1946.             if (markerRep.symbol() == MarkerRepresentation::Crosshair)
  1947.                 renderCrosshair(size, realTime);
  1948.             else
  1949.                 markerRep.render(size);
  1950.             glEnable(GL_TEXTURE_2D);            
  1951.             
  1952.             if (!markerRep.label().empty())
  1953.             {
  1954.                 int labelOffset = (int) markerRep.size() / 2;
  1955.                 glTranslatef(labelOffset + PixelOffset, -labelOffset - font[fs]->getHeight() + PixelOffset, 0.0f);
  1956.                 font[fs]->render(markerRep.label());
  1957.             }
  1958.         }
  1959.         else
  1960.         {
  1961.             glTranslatef((int) iter->position.x + PixelOffset + labelHOffset,
  1962.                          (int) iter->position.y + PixelOffset + labelVOffset,
  1963.                          ndc_z);
  1964.             glColor(iter->color);
  1965.             font[fs]->render(iter->labelText);
  1966.         }
  1967.         glPopMatrix();
  1968.     }
  1969.     glPopMatrix();
  1970.     glMatrixMode(GL_PROJECTION);
  1971.     glPopMatrix();
  1972.     glMatrixMode(GL_MODELVIEW);
  1973.     glDisable(GL_DEPTH_TEST);
  1974.     return iter;
  1975. }
  1976. vector<Renderer::Annotation>::iterator
  1977. Renderer::renderAnnotations(vector<Annotation>::iterator startIter,
  1978.                             vector<Annotation>::iterator endIter,
  1979.                             float nearDist,
  1980.                             float farDist,
  1981.                             FontStyle fs)
  1982. {
  1983.     if (font[fs] == NULL)
  1984.         return endIter;
  1985.     glEnable(GL_DEPTH_TEST);
  1986.     glEnable(GL_TEXTURE_2D);
  1987.     font[fs]->bind();
  1988.     glEnable(GL_BLEND);
  1989.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1990.     glMatrixMode(GL_PROJECTION);
  1991.     glPushMatrix();
  1992.     glLoadIdentity();
  1993.     gluOrtho2D(0, windowWidth, 0, windowHeight);
  1994.     glMatrixMode(GL_MODELVIEW);
  1995.     glPushMatrix();
  1996.     glLoadIdentity();
  1997.     glTranslatef(GLfloat((int) (windowWidth / 2)),
  1998.                  GLfloat((int) (windowHeight / 2)), 0);
  1999.     // Precompute values that will be used to generate the normalized device z value;
  2000.     // we're effectively just handling the projection instead of OpenGL. We use an orthographic
  2001.     // projection matrix in order to get the label text position exactly right but need to mimic
  2002.     // the depth coordinate generation of a perspective projection.
  2003.     float d1 = -(farDist + nearDist) / (farDist - nearDist);
  2004.     float d2 = -2.0f * nearDist * farDist / (farDist - nearDist);
  2005.     vector<Annotation>::iterator iter = startIter;
  2006.     for (; iter != endIter && iter->position.z > nearDist; iter++)
  2007.     {
  2008.         // Compute normalized device z
  2009.         float ndc_z = d1 + d2 / -iter->position.z;
  2010.         ndc_z = min(1.0f, max(-1.0f, ndc_z)); // Clamp to [-1,1]
  2011.         // Offsets to left align label
  2012.         int labelHOffset = 0;
  2013.         int labelVOffset = 0;
  2014.         if (iter->markerRep != NULL)
  2015.         {
  2016.             glPushMatrix();
  2017.             const MarkerRepresentation& markerRep = *iter->markerRep;
  2018.             float size = markerRep.size();
  2019.             if (iter->size > 0.0f)
  2020.             {
  2021.                 size = iter->size;
  2022.             }
  2023.             
  2024.             glTranslatef((GLfloat) (int) iter->position.x, (GLfloat) (int) iter->position.y, ndc_z);
  2025.             glColor(iter->color);
  2026.                         
  2027.             glDisable(GL_TEXTURE_2D);
  2028.             if (markerRep.symbol() == MarkerRepresentation::Crosshair)
  2029.                 renderCrosshair(size, realTime);
  2030.             else
  2031.                 markerRep.render(size);
  2032.             glEnable(GL_TEXTURE_2D);            
  2033.             
  2034.             if (!markerRep.label().empty())
  2035.             {
  2036.                 int labelOffset = (int) markerRep.size() / 2;
  2037.                 glTranslatef(labelOffset + PixelOffset, -labelOffset - font[fs]->getHeight() + PixelOffset, 0.0f);
  2038.                 font[fs]->render(markerRep.label());
  2039.             }
  2040.             glPopMatrix();
  2041.         }
  2042.         
  2043.         if (iter->labelText[0] != '')
  2044.         {
  2045.             if (iter->markerRep != NULL)
  2046.                 labelHOffset += (int) iter->markerRep->size() / 2 + 3;
  2047.             glPushMatrix();
  2048.             glTranslatef((int) iter->position.x + PixelOffset + labelHOffset,
  2049.                          (int) iter->position.y + PixelOffset + labelVOffset,
  2050.                          ndc_z);
  2051.             glColor(iter->color);
  2052.             font[fs]->render(iter->labelText);
  2053.             glPopMatrix();
  2054.         }
  2055.     }
  2056.     glPopMatrix();
  2057.     glMatrixMode(GL_PROJECTION);
  2058.     glPopMatrix();
  2059.     glMatrixMode(GL_MODELVIEW);
  2060.     glDisable(GL_DEPTH_TEST);
  2061.     return iter;
  2062. }
  2063. void Renderer::renderMarkers(const MarkerList& markers,
  2064.                              const UniversalCoord& cameraPosition,
  2065.                  const Quatd& cameraOrientation,
  2066.                              double jd)
  2067. {
  2068.     // Calculate the cosine of half the maximum field of view. We'll use this for
  2069.     // fast testing of marker visibility. The stored field of view is the
  2070.     // vertical field of view; we want the field of view as measured on the
  2071.     // diagonal between viewport corners.
  2072.     double h = tan(degToRad(fov / 2));
  2073.     double diag = sqrt(1.0 + square(h) + square(h * (double) windowWidth / (double) windowHeight));
  2074.     double cosFOV = 1.0 / diag;
  2075.     
  2076.     Vec3d viewVector = Vec3d(0.0, 0.0, -1.0) * cameraOrientation.toMatrix3();
  2077.     
  2078.     for (MarkerList::const_iterator iter = markers.begin(); iter != markers.end(); iter++)
  2079.     {
  2080.         UniversalCoord uc = iter->position(jd);
  2081.         Vec3d offset = (uc - cameraPosition) * astro::microLightYearsToKilometers(1.0);
  2082.                 
  2083.         // Only render those markers that lie withing the field of view.
  2084.         if ((offset * viewVector) > cosFOV * offset.length())
  2085.         {
  2086.             double distance = offset.length();
  2087.             float symbolSize = 0.0f;
  2088.             if (iter->sizing() == DistanceBasedSize)
  2089.             {
  2090.                 symbolSize = (float) (iter->representation().size() / distance) / pixelSize;
  2091.             }
  2092.             if (iter->occludable())
  2093.             {
  2094.                 // If the marker is occludable, add it to the sorted annotation list if it's relatively
  2095.                 // nearby, and to the background list if it's very distant.
  2096.                 if (distance < astro::lightYearsToKilometers(1.0))
  2097.                 {
  2098.                     // Modify the marker position so that it is always in front of the marked object.
  2099.                     double boundingRadius;
  2100.                     if (iter->object().body() != NULL)
  2101.                         boundingRadius = iter->object().body()->getBoundingRadius();
  2102.                     else
  2103.                         boundingRadius = iter->object().radius();                
  2104.                     offset *= (1.0 - boundingRadius * 1.01 / distance);
  2105.                 
  2106.                     addSortedAnnotation(&(iter->representation()), EMPTY_STRING, iter->representation().color(),
  2107.                                         Point3f((float) offset.x, (float) offset.y, (float) offset.z),
  2108.                                         AlignLeft, VerticalAlignTop, symbolSize);
  2109.                 }
  2110.                 else
  2111.                 {
  2112.                     addAnnotation(backgroundAnnotations,
  2113.                                   &(iter->representation()), EMPTY_STRING, iter->representation().color(),
  2114.                                   Point3f((float) offset.x, (float) offset.y, (float) offset.z),
  2115.                                   AlignLeft, VerticalAlignTop, symbolSize);
  2116.                 }
  2117.             }
  2118.             else
  2119.             {
  2120.                 addAnnotation(foregroundAnnotations,
  2121.                               &(iter->representation()), EMPTY_STRING, iter->representation().color(),
  2122.                               Point3f((float) offset.x, (float) offset.y, (float) offset.z),
  2123.                               AlignLeft, VerticalAlignTop, symbolSize);
  2124.             }
  2125.         }
  2126.     }
  2127. }
  2128. void Renderer::setStarStyle(StarStyle style)
  2129. {
  2130.     starStyle = style;
  2131.     markSettingsChanged();
  2132. }
  2133. Renderer::StarStyle Renderer::getStarStyle() const
  2134. {
  2135.     return starStyle;
  2136. }
  2137. Renderer::StarVertexBuffer::StarVertexBuffer(unsigned int _capacity) :
  2138.     capacity(_capacity),
  2139.     vertices(NULL),
  2140.     texCoords(NULL),
  2141.     colors(NULL)
  2142. {
  2143.     nStars = 0;
  2144.     vertices = new float[capacity * 12];
  2145.     texCoords = new float[capacity * 8];
  2146.     colors = new unsigned char[capacity * 16];
  2147.     // Fill the texture coordinate array now, since it will always have
  2148.     // the same contents.
  2149.     for (unsigned int i = 0; i < capacity; i++)
  2150.     {
  2151.         unsigned int n = i * 8;
  2152.         texCoords[n    ] = 0; texCoords[n + 1] = 0;
  2153.         texCoords[n + 2] = 1; texCoords[n + 3] = 0;
  2154.         texCoords[n + 4] = 1; texCoords[n + 5] = 1;
  2155.         texCoords[n + 6] = 0; texCoords[n + 7] = 1;
  2156.     }
  2157. }
  2158. Renderer::StarVertexBuffer::~StarVertexBuffer()
  2159. {
  2160.     if (vertices != NULL)
  2161.         delete[] vertices;
  2162.     if (colors != NULL)
  2163.         delete[] colors;
  2164.     if (texCoords != NULL)
  2165.         delete[] texCoords;
  2166. }
  2167. void Renderer::StarVertexBuffer::start()
  2168. {
  2169.     glEnableClientState(GL_VERTEX_ARRAY);
  2170.     glVertexPointer(3, GL_FLOAT, 0, vertices);
  2171.     glEnableClientState(GL_COLOR_ARRAY);
  2172.     glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
  2173.     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  2174.     glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
  2175.     glDisableClientState(GL_NORMAL_ARRAY);
  2176. }
  2177. void Renderer::StarVertexBuffer::render()
  2178. {
  2179.     if (nStars != 0)
  2180.     {
  2181.         glDrawArrays(GL_QUADS, 0, nStars * 4);
  2182.         nStars = 0;
  2183.     }
  2184. }
  2185. void Renderer::StarVertexBuffer::finish()
  2186. {
  2187.     render();
  2188.     glDisableClientState(GL_COLOR_ARRAY);
  2189.     glDisableClientState(GL_VERTEX_ARRAY);
  2190.     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  2191. }
  2192. void Renderer::StarVertexBuffer::addStar(const Vec3f& pos,
  2193.                                          const Color& color,
  2194.                                          float size)
  2195. {
  2196.     if (nStars < capacity)
  2197.     {
  2198.         int n = nStars * 12;
  2199.         vertices[n + 0]  = pos.x + v0.x * size;
  2200.         vertices[n + 1]  = pos.y + v0.y * size;
  2201.         vertices[n + 2]  = pos.z + v0.z * size;
  2202.         vertices[n + 3]  = pos.x + v1.x * size;
  2203.         vertices[n + 4]  = pos.y + v1.y * size;
  2204.         vertices[n + 5]  = pos.z + v1.z * size;
  2205.         vertices[n + 6]  = pos.x + v2.x * size;
  2206.         vertices[n + 7]  = pos.y + v2.y * size;
  2207.         vertices[n + 8]  = pos.z + v2.z * size;
  2208.         vertices[n + 9]  = pos.x + v3.x * size;
  2209.         vertices[n + 10] = pos.y + v3.y * size;
  2210.         vertices[n + 11] = pos.z + v3.z * size;
  2211.         n = nStars * 16;
  2212.         color.get(colors + n);
  2213.         color.get(colors + n + 4);
  2214.         color.get(colors + n + 8);
  2215.         color.get(colors + n + 12);
  2216.         nStars++;
  2217.     }
  2218.     if (nStars == capacity)
  2219.     {
  2220.         render();
  2221.         nStars = 0;
  2222.     }
  2223. }
  2224. void Renderer::StarVertexBuffer::setBillboardOrientation(const Quatf& q)
  2225. {
  2226.     Mat3f m = q.toMatrix3();
  2227.     v0 = Vec3f(-1, -1, 0) * m;
  2228.     v1 = Vec3f( 1, -1, 0) * m;
  2229.     v2 = Vec3f( 1,  1, 0) * m;
  2230.     v3 = Vec3f(-1,  1, 0) * m;
  2231. }
  2232. Renderer::PointStarVertexBuffer::PointStarVertexBuffer(unsigned int _capacity) :
  2233.     capacity(_capacity),
  2234.     nStars(0),
  2235.     vertices(NULL),
  2236.     context(NULL),
  2237.     useSprites(false),
  2238. texture(NULL)
  2239. {
  2240.     vertices = new StarVertex[capacity];
  2241. }
  2242. Renderer::PointStarVertexBuffer::~PointStarVertexBuffer()
  2243. {
  2244.     if (vertices != NULL)
  2245.         delete[] vertices;
  2246. }
  2247. void Renderer::PointStarVertexBuffer::startSprites(const GLContext& _context)
  2248. {
  2249.     context = &_context;
  2250.     assert(context->getVertexProcessor() != NULL || !useSprites); // vertex shaders required for new star rendering
  2251.     unsigned int stride = sizeof(StarVertex);
  2252.     glEnableClientState(GL_VERTEX_ARRAY);
  2253.     glVertexPointer(3, GL_FLOAT, stride, &vertices[0].position);
  2254.     glEnableClientState(GL_COLOR_ARRAY);
  2255.     glColorPointer(4, GL_UNSIGNED_BYTE, stride, &vertices[0].color);
  2256.     VertexProcessor* vproc = context->getVertexProcessor();
  2257.     vproc->enable();
  2258.     vproc->use(vp::starDisc);
  2259.     vproc->enableAttribArray(6);
  2260.     vproc->attribArray(6, 1, GL_FLOAT, stride, &vertices[0].size);
  2261.     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  2262.     glDisableClientState(GL_NORMAL_ARRAY);
  2263.     glEnable(GL_POINT_SPRITE_ARB);
  2264.     glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
  2265.     useSprites = true;
  2266. }
  2267. void Renderer::PointStarVertexBuffer::startPoints(const GLContext& _context)
  2268. {
  2269.     context = &_context;
  2270.     unsigned int stride = sizeof(StarVertex);
  2271.     glEnableClientState(GL_VERTEX_ARRAY);
  2272.     glVertexPointer(3, GL_FLOAT, stride, &vertices[0].position);
  2273.     glEnableClientState(GL_COLOR_ARRAY);
  2274.     glColorPointer(4, GL_UNSIGNED_BYTE, stride, &vertices[0].color);
  2275.     // An option to control the size of the stars would be helpful.
  2276.     // Which size looks best depends a lot on the resolution and the
  2277.     // type of display device.
  2278.     // glPointSize(2.0f);
  2279.     // glEnable(GL_POINT_SMOOTH);
  2280.     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  2281.     glDisable(GL_TEXTURE_2D);
  2282.     glDisableClientState(GL_NORMAL_ARRAY);
  2283.     useSprites = false;
  2284. }
  2285. void Renderer::PointStarVertexBuffer::render()
  2286. {
  2287.     if (nStars != 0)
  2288.     {
  2289.         unsigned int stride = sizeof(StarVertex);
  2290.         if (useSprites)
  2291.         {
  2292.             glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
  2293.             glEnable(GL_TEXTURE_2D);
  2294.         }
  2295.         else
  2296.         {
  2297.             glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
  2298.             glDisable(GL_TEXTURE_2D);
  2299.             glPointSize(1.0f);
  2300.         }
  2301.         glVertexPointer(3, GL_FLOAT, stride, &vertices[0].position);
  2302.         glColorPointer(4, GL_UNSIGNED_BYTE, stride, &vertices[0].color);
  2303.         if (useSprites)
  2304.         {
  2305.             VertexProcessor* vproc = context->getVertexProcessor();
  2306.             vproc->attribArray(6, 1, GL_FLOAT, stride, &vertices[0].size);
  2307.         }
  2308.         if (texture != NULL)
  2309.             texture->bind();
  2310.         glDrawArrays(GL_POINTS, 0, nStars);
  2311.         nStars = 0;
  2312.     }
  2313. }
  2314. void Renderer::PointStarVertexBuffer::finish()
  2315. {
  2316.     render();
  2317.     glDisableClientState(GL_COLOR_ARRAY);
  2318.     glDisableClientState(GL_VERTEX_ARRAY);
  2319.     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  2320.     if (useSprites)
  2321.     {
  2322.         VertexProcessor* vproc = context->getVertexProcessor();
  2323.         vproc->disableAttribArray(6);
  2324.         vproc->disable();
  2325.         glDisable(GL_POINT_SPRITE_ARB);
  2326.     }
  2327.     else
  2328.     {
  2329.         glEnable(GL_TEXTURE_2D);
  2330.     }
  2331. }
  2332. void Renderer::PointStarVertexBuffer::addStar(const Vec3f& pos,
  2333.                                               const Color& color,
  2334.                                               float size)
  2335. {
  2336.     if (nStars < capacity)
  2337.     {
  2338.         vertices[nStars].position = pos;
  2339.         vertices[nStars].size = size;
  2340.         color.get(vertices[nStars].color);
  2341.         nStars++;
  2342.     }
  2343.     if (nStars == capacity)
  2344.     {
  2345.         render();
  2346.         nStars = 0;
  2347.     }
  2348. }
  2349. void Renderer::PointStarVertexBuffer::setTexture(Texture* _texture)
  2350. {
  2351. texture = _texture;
  2352. }
  2353. void Renderer::loadTextures(Body* body)
  2354. {
  2355.     Surface& surface = body->getSurface();
  2356.     if (surface.baseTexture.tex[textureResolution] != InvalidResource)
  2357.         surface.baseTexture.find(textureResolution);
  2358.     if ((surface.appearanceFlags & Surface::ApplyBumpMap) != 0 &&
  2359.         context->bumpMappingSupported() &&
  2360.         surface.bumpTexture.tex[textureResolution] != InvalidResource)
  2361.         surface.bumpTexture.find(textureResolution);
  2362.     if ((surface.appearanceFlags & Surface::ApplyNightMap) != 0 &&
  2363.         (renderFlags & ShowNightMaps) != 0)
  2364.         surface.nightTexture.find(textureResolution);
  2365.     if ((surface.appearanceFlags & Surface::SeparateSpecularMap) != 0 &&
  2366.         surface.specularTexture.tex[textureResolution] != InvalidResource)
  2367.         surface.specularTexture.find(textureResolution);
  2368.     if ((renderFlags & ShowCloudMaps) != 0 &&
  2369.         body->getAtmosphere() != NULL &&
  2370.         body->getAtmosphere()->cloudTexture.tex[textureResolution] != InvalidResource)
  2371.     {
  2372.         body->getAtmosphere()->cloudTexture.find(textureResolution);
  2373.     }
  2374.     if (body->getRings() != NULL &&
  2375.         body->getRings()->texture.tex[textureResolution] != InvalidResource)
  2376.     {
  2377.         body->getRings()->texture.find(textureResolution);
  2378.     }
  2379.     
  2380.     if (body->getGeometry() != InvalidResource)
  2381.     {
  2382.         GetGeometryManager()->find(body->getGeometry());
  2383.     }
  2384. }
  2385. void Renderer::invalidateOrbitCache()
  2386. {
  2387.     orbitCache.clear();
  2388. }
  2389. bool Renderer::settingsHaveChanged() const
  2390. {
  2391.     return settingsChanged;
  2392. }
  2393. void Renderer::markSettingsChanged()
  2394. {
  2395.     settingsChanged = true;
  2396.     notifyWatchers();
  2397. }
  2398. void Renderer::addWatcher(RendererWatcher* watcher)
  2399. {
  2400.     assert(watcher != NULL);
  2401.     watchers.insert(watchers.end(), watcher);
  2402. }
  2403. void Renderer::removeWatcher(RendererWatcher* watcher)
  2404. {
  2405.     list<RendererWatcher*>::iterator iter =
  2406.         find(watchers.begin(), watchers.end(), watcher);
  2407.     if (iter != watchers.end())
  2408.         watchers.erase(iter);
  2409. }
  2410. void Renderer::notifyWatchers() const
  2411. {
  2412.     for (list<RendererWatcher*>::const_iterator iter = watchers.begin();
  2413.          iter != watchers.end(); iter++)
  2414.     {
  2415.         (*iter)->notifyRenderSettingsChanged(this);
  2416.     }
  2417. }