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

OpenGL

开发平台:

Visual C++

  1. // visibleregion.cpp
  2. //
  3. // Visible region reference mark for ellipsoidal bodies.
  4. //
  5. // Copyright (C) 2008, the Celestia Development Team
  6. // Initial version by Chris Laurel, claurel@gmail.com
  7. //
  8. // This program is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU General Public License
  10. // as published by the Free Software Foundation; either version 2
  11. // of the License, or (at your option) any later version.
  12. #include <cstdio>
  13. #include <cmath>
  14. #include <celmath/intersect.h>
  15. #include "visibleregion.h"
  16. #include "body.h"
  17. #include "selection.h"
  18. #include "gl.h"
  19. #include "vecgl.h"
  20. #include "render.h"
  21. /*! Construct a new reference mark that shows the outline of the
  22.  *  region on the surface of a body in which the target object is
  23.  *  visible. The following are assumed:
  24.  *     - target is a point
  25.  *     - the body is an ellipsoid
  26.  *  
  27.  *  This reference mark is useful in a few situations. When the
  28.  *  body is a planet or moon and target is the sun, the outline of
  29.  *  the visible region is the terminator. If target is a satellite,
  30.  *  the outline is its circle of visibility.
  31.  */
  32. VisibleRegion::VisibleRegion(const Body& body, const Selection& target) :
  33.     m_body(body),
  34.     m_target(target),
  35.     m_color(1.0f, 1.0f, 0.0f),
  36. #ifdef USE_HDR
  37.     m_opacity(0.0f)
  38. #else
  39.     m_opacity(1.0f)
  40. #endif
  41. {
  42.     setTag("visible region");
  43. }
  44. VisibleRegion::~VisibleRegion()
  45. {
  46. }
  47. Color
  48. VisibleRegion::color() const
  49. {
  50.     return m_color;
  51. }
  52. void
  53. VisibleRegion::setColor(Color color)
  54. {
  55.     m_color = color;
  56. }
  57. float
  58. VisibleRegion::opacity() const
  59. {
  60.     return m_opacity;
  61. }
  62. void
  63. VisibleRegion::setOpacity(float opacity)
  64. {
  65.     m_opacity = opacity;
  66. #ifdef USE_HDR
  67.     m_opacity = 1.0f - opacity;
  68. #endif
  69. }
  70. template <typename T> static Vector3<T>
  71. ellipsoidTangent(const Vector3<T>& recipSemiAxes,
  72.                  const Vector3<T>& w,
  73.                  const Vector3<T>& e,
  74.                  const Vector3<T>& e_,
  75.                  T ee)
  76. {
  77.     // We want to find t such that -E(1-t) + Wt is the direction of a ray
  78.     // tangent to the ellipsoid.  A tangent ray will intersect the ellipsoid
  79.     // at exactly one point.  Finding the intersection between a ray and an
  80.     // ellipsoid ultimately requires using the quadratic formula, which has
  81.     // one solution when the discriminant (b^2 - 4ac) is zero.  The code below
  82.     // computes the value of t that results in a discriminant of zero.
  83.     Vector3<T> w_(w.x * recipSemiAxes.x, w.y * recipSemiAxes.y, w.z * recipSemiAxes.z);
  84.     T ww = w_ * w_;
  85.     T ew = w_ * e_;
  86.     // Before elimination of terms:
  87.     // double a =  4 * square(ee + ew) - 4 * (ee + 2 * ew + ww) * (ee - 1.0f);
  88.     // double b = -8 * ee * (ee + ew)  - 4 * (-2 * (ee + ew) * (ee - 1.0f));
  89.     // double c =  4 * ee * ee         - 4 * (ee * (ee - 1.0f));
  90.     // Simplify the below expression and eliminate the ee^2 terms; this
  91.     // prevents precision errors, as ee tends to be a very large value.
  92.     //T a =  4 * square(ee + ew) - 4 * (ee + 2 * ew + ww) * (ee - 1);
  93.     T a =  4 * (square(ew) - ee * ww + ee + 2 * ew + ww);
  94.     T b = -8 * (ee + ew);
  95.     T c =  4 * ee;
  96.     T t = 0;
  97.     T discriminant = b * b - 4 * a * c;
  98.     if (discriminant < 0)
  99.         t = (-b + (T) sqrt(-discriminant)) / (2 * a); // Bad!
  100.     else
  101.         t = (-b + (T) sqrt(discriminant)) / (2 * a);
  102.     // V is the direction vector.  We now need the point of intersection,
  103.     // which we obtain by solving the quadratic equation for the ray-ellipse
  104.     // intersection.  Since we already know that the discriminant is zero,
  105.     // the solution is just -b/2a
  106.     Vector3<T> v = -e * (1 - t) + w * t;
  107.     Vector3<T> v_(v.x * recipSemiAxes.x, v.y * recipSemiAxes.y, v.z * recipSemiAxes.z);
  108.     T a1 = v_ * v_;
  109.     T b1 = (T) 2 * v_ * e_;
  110.     T t1 = -b1 / (2 * a1);
  111.     return e + v * t1;
  112. }
  113. // Get a vector orthogonal to the specified one
  114. template <typename T> static Vector3<T>
  115. orthogonalUnitVector(const Vector3<T>& v)
  116. {
  117.     Vec3d w;
  118.     if (abs(v.x) < abs(v.y) && abs(v.x) < abs(v.z))
  119.         w = Vec3d(1, 0, 0) ^ v;
  120.     else if (abs(v.y) < abs(v.z))
  121.         w = Vec3d(0, 1, 0) ^ v;
  122.     else
  123.         w = Vec3d(0, 0, 1) ^ v;
  124.     w.normalize();
  125.     return w;
  126. }
  127. void
  128. VisibleRegion::render(Renderer* /* renderer */,
  129.                       const Point3f& /* pos */,
  130.                       float discSizeInPixels,
  131.                       double tdb) const
  132. {
  133.     // Don't render anything if the current time is not within the
  134.     // target object's time window.
  135.     if (m_target.body() != NULL)
  136.     {
  137.         if (!m_target.body()->extant(tdb))
  138.             return;
  139.     }
  140.     // Fade in the terminator when the planet is small
  141.     const float minDiscSize = 5.0f;
  142.     const float fullOpacityDiscSize = 10.0f;
  143.     float opacity = (discSizeInPixels - minDiscSize) / (fullOpacityDiscSize - minDiscSize);
  144.     // Don't render the terminator if the it's smaller than the minimum size
  145.     if (opacity <= 0.0f)
  146.         return;
  147.     opacity = min(opacity, 1.0f) * m_opacity;
  148.     // Base the amount of subdivision on the apparent size
  149.     unsigned int nSections = (unsigned int) (30.0f + discSizeInPixels * 0.5f);
  150.     nSections = min(nSections, 360u);
  151.     Quatd q = m_body.getEclipticToBodyFixed(tdb);
  152.     Quatf qf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
  153.     // The outline can't be rendered exactly on the planet sphere, or
  154.     // there will be z-fighting problems. Render it at a height above the
  155.     // planet that will place it about one pixel away from the planet.
  156.     float scale = (discSizeInPixels + 1) / discSizeInPixels;
  157.     scale = max(scale, 1.0001f);
  158.     Vec3f semiAxes = m_body.getSemiAxes();
  159.     // Enable depth buffering
  160.     glEnable(GL_DEPTH_TEST);
  161.     glDepthMask(GL_TRUE);
  162.     glEnable(GL_BLEND);
  163. #ifdef USE_HDR
  164.     glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
  165. #else
  166.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  167. #endif
  168.     glDisable(GL_TEXTURE_2D);
  169.     glDisable(GL_LIGHTING);
  170.     glPushMatrix();
  171.     glRotate(~qf);
  172.     double maxSemiAxis = m_body.getRadius();
  173.     // In order to avoid precision problems and extremely large values, scale
  174.     // the target position and semiaxes such that the largest semiaxis is 1.0.
  175.     Vec3d lightDir = m_body.getPosition(tdb) - m_target.getPosition(tdb);
  176.     lightDir = lightDir * (astro::microLightYearsToKilometers(1.0) / maxSemiAxis);
  177.     lightDir = lightDir * (~q).toMatrix3();
  178.     // Another measure to prevent precision problems: if the distance to the
  179.     // object is much greater than the largest semi axis, clamp it to 1e4 times
  180.     // the radius, as body-to-target rays at that distance are nearly parallel anyhow.
  181.     if (lightDir.length() > 10000.0)
  182.         lightDir *= (10000.0 / lightDir.length());
  183.     // Pick two orthogonal axes both normal to the light direction
  184.     Vec3d lightDirNorm = lightDir;
  185.     lightDirNorm.normalize();
  186.     Vec3d uAxis = orthogonalUnitVector(lightDirNorm);
  187.     Vec3d vAxis = uAxis ^ lightDirNorm;
  188.     Vec3d recipSemiAxes(maxSemiAxis / semiAxes.x, maxSemiAxis / semiAxes.y, maxSemiAxis / semiAxes.z);
  189.     Vec3d e = -lightDir;
  190.     Vec3d e_(e.x * recipSemiAxes.x, e.y * recipSemiAxes.y, e.z * recipSemiAxes.z);
  191.     double ee = e_ * e_;
  192.     glColor4f(m_color.red(), m_color.green(), m_color.blue(), opacity);
  193.     glBegin(GL_LINE_LOOP);
  194.     for (unsigned i = 0; i < nSections; i++)
  195.     {
  196.         double theta = (double) i / (double) (nSections) * 2.0 * PI;
  197.         Vec3d w = cos(theta) * uAxis + sin(theta) * vAxis;
  198.         
  199.         Vec3d toCenter = ellipsoidTangent(recipSemiAxes, w, e, e_, ee);
  200.         toCenter *= maxSemiAxis * scale;
  201.         glVertex3f((GLfloat) toCenter.x, (GLfloat) toCenter.y, (GLfloat) toCenter.z);
  202.     }
  203.     glEnd();
  204.     glPopMatrix();
  205.     glDisable(GL_DEPTH_TEST);
  206.     glDepthMask(GL_FALSE);
  207.     glEnable(GL_TEXTURE_2D);
  208.     glEnable(GL_BLEND);
  209.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  210. }
  211. float
  212. VisibleRegion::boundingSphereRadius() const
  213. {
  214.     return m_body.getRadius();
  215. }