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

OpenGL

开发平台:

Visual C++

  1. // planetgrid.cpp
  2. //
  3. // Longitude/latitude grids 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 "planetgrid.h"
  16. #include "body.h"
  17. #include "gl.h"
  18. #include "vecgl.h"
  19. #include "render.h"
  20. unsigned int PlanetographicGrid::circleSubdivisions = 100;
  21. float* PlanetographicGrid::xyCircle = NULL;
  22. float* PlanetographicGrid::xzCircle = NULL;
  23. PlanetographicGrid::PlanetographicGrid(const Body& _body) :
  24.     body(_body),
  25.     minLongitudeStep(10.0f),
  26.     minLatitudeStep(10.0f),
  27.     longitudeConvention(Westward),
  28.     northDirection(NorthNormal)
  29. {
  30.     if (xyCircle == NULL)
  31.         InitializeGeometry();
  32.     setTag("planetographic grid");
  33.     setIAULongLatConvention();
  34. }
  35. PlanetographicGrid::~PlanetographicGrid()
  36. {
  37. }
  38. static void longLatLabel(const string& labelText,
  39.                          double longitude,
  40.                          double latitude,
  41.                          const Vec3d& viewRayOrigin,
  42.                          const Vec3d& viewNormal,
  43.                          const Vec3d& bodyCenter,
  44.                          const Quatd& bodyOrientation,
  45.                          const Vec3f& semiAxes,
  46.                          float labelOffset,
  47.                          Renderer* renderer)
  48. {
  49.     double theta = degToRad(longitude);
  50.     double phi = degToRad(latitude);
  51.     Vec3d pos(cos(phi) * cos(theta) * semiAxes.x,
  52.               sin(phi) * semiAxes.y,
  53.               -cos(phi) * sin(theta) * semiAxes.z);
  54.     
  55.     float nearDist = renderer->getNearPlaneDistance();
  56.     
  57.     pos = pos * (1.0 + labelOffset);
  58.     
  59.     double boundingRadius = max(semiAxes.x, max(semiAxes.y, semiAxes.z));
  60.     // Draw the label only if it isn't obscured by the body ellipsoid
  61.     double t = 0.0;
  62.     if (testIntersection(Ray3d(Point3d(0.0, 0.0, 0.0) + viewRayOrigin, pos - viewRayOrigin),
  63.                          Ellipsoidd(Vec3d(semiAxes.x, semiAxes.y, semiAxes.z)), t) && t >= 1.0)
  64.     {
  65.         // Compute the position of the label
  66.         Vec3d labelPos = bodyCenter +
  67.                          (1.0 + labelOffset) * pos * bodyOrientation.toMatrix3();
  68.         
  69.         // Calculate the intersection of the eye-to-label ray with the plane perpendicular to
  70.         // the view normal that touches the front of the objects bounding sphere
  71.         double planetZ = viewNormal * bodyCenter - boundingRadius;
  72.         if (planetZ < -nearDist * 1.001)
  73.             planetZ = -nearDist * 1.001;
  74.         double z = viewNormal * labelPos;
  75.         labelPos *= planetZ / z;
  76.         
  77.         renderer->addObjectAnnotation(NULL, labelText,
  78.                                       Renderer::PlanetographicGridLabelColor,
  79.                                       Point3f((float) labelPos.x, (float) labelPos.y, (float) labelPos.z));                             
  80.     }
  81. }
  82. void
  83. PlanetographicGrid::render(Renderer* renderer,
  84.                            const Point3f& pos,
  85.                            float discSizeInPixels,
  86.                            double tdb) const
  87. {
  88.     Quatd q = Quatd::yrotation(PI) * body.getEclipticToBodyFixed(tdb);
  89.     Quatf qf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
  90.     // The grid can't be rendered exactly on the planet sphere, or
  91.     // there will be z-fighting problems. Render it at a height above the
  92.     // planet that will place it about one pixel away from the planet.
  93.     float scale = (discSizeInPixels + 1) / discSizeInPixels;
  94.     scale = max(scale, 1.001f);
  95.     float offset = scale - 1.0f;
  96.     Vec3f semiAxes = body.getSemiAxes();
  97.     Vec3d posd(pos.x, pos.y, pos.z);
  98.     Vec3d viewRayOrigin = Vec3d(-pos.x, -pos.y, -pos.z)  * (~q).toMatrix3();
  99.     
  100.     // Calculate the view normal; this is used for placement of the long/lat
  101.     // label text.
  102.     Vec3f vn  = Vec3f(0.0f, 0.0f, -1.0f) * renderer->getCameraOrientation().toMatrix3();
  103.     Vec3d viewNormal(vn.x, vn.y, vn.z);
  104.     // Enable depth buffering
  105.     glEnable(GL_DEPTH_TEST);
  106.     glDepthMask(GL_TRUE);
  107.     glDisable(GL_BLEND);
  108.     glDisable(GL_TEXTURE_2D);
  109.     glPushMatrix();
  110.     glRotate(~qf);
  111.     glScale(scale * semiAxes);
  112.     glEnableClientState(GL_VERTEX_ARRAY);
  113.     glVertexPointer(3, GL_FLOAT, 0, xzCircle);
  114.     // Only show the coordinate labels if the body is sufficiently large on screen
  115.     bool showCoordinateLabels = false;
  116.     if (discSizeInPixels > 50)
  117.         showCoordinateLabels = true;
  118.     float latitudeStep = minLatitudeStep;
  119.     float longitudeStep = minLongitudeStep;
  120.     if (discSizeInPixels < 200)
  121.     {
  122.         latitudeStep = 30.0f;
  123.         longitudeStep = 30.0f;
  124.     }
  125.     
  126.     for (float latitude = -90.0f + latitudeStep; latitude < 90.0f; latitude += latitudeStep)
  127.     {
  128.         float phi = degToRad(latitude);
  129.         float r = (float) cos(phi);
  130.         if (latitude == 0.0f)
  131.         {
  132.             glColor(Renderer::PlanetEquatorColor);
  133.             glLineWidth(2.0f);
  134.         }
  135.         else
  136.         {
  137.             glColor(Renderer::PlanetographicGridColor);
  138.         }
  139.         glPushMatrix();
  140.         glTranslatef(0.0f, (float) sin(phi), 0.0f);
  141.         glScalef(r, r, r);
  142.         glDrawArrays(GL_LINE_LOOP, 0, circleSubdivisions);
  143.         glPopMatrix();
  144.         glLineWidth(1.0f);
  145.         
  146.         if (showCoordinateLabels)
  147.         {
  148.             if (latitude != 0.0f && abs(latitude) < 90.0f)
  149.             {
  150.                 char buf[64];
  151.                 char ns;
  152.                 if (latitude < 0.0f)
  153.                     ns = northDirection == NorthNormal ? 'S' : 'N';
  154.                 else
  155.                     ns = northDirection == NorthNormal ? 'N' : 'S';
  156.                 sprintf(buf, "%d%c", (int) fabs((double) latitude), ns);
  157.                 longLatLabel(buf, 0.0, latitude, viewRayOrigin, viewNormal, posd, q, semiAxes, offset, renderer);
  158.                 longLatLabel(buf, 180.0, latitude, viewRayOrigin, viewNormal, posd, q, semiAxes, offset, renderer);
  159.             }
  160.         }
  161.     }
  162.     glVertexPointer(3, GL_FLOAT, 0, xyCircle);
  163.     for (float longitude = 0.0f; longitude <= 180.0f; longitude += longitudeStep)
  164.     {
  165.         glColor(Renderer::PlanetographicGridColor);
  166.         glPushMatrix();
  167.         glRotatef(longitude, 0.0f, 1.0f, 0.0f);
  168.         glDrawArrays(GL_LINE_LOOP, 0, circleSubdivisions);
  169.         glPopMatrix();
  170.         if (showCoordinateLabels)
  171.         {
  172.             int showLongitude = 0;
  173.             char ew = 'E';
  174.             switch (longitudeConvention)
  175.             {
  176.             case EastWest:
  177.                 ew = 'E';
  178.                 showLongitude = (int) longitude;
  179.                 break;
  180.             case Eastward:
  181.                 if (longitude > 0.0f)
  182.                     showLongitude = 360 - (int) longitude;
  183.                 ew = 'E';
  184.                 break;
  185.             case Westward:
  186.                 if (longitude > 0.0f)
  187.                     showLongitude = 360 - (int) longitude;
  188.                 ew = 'W';
  189.                 break;
  190.             }
  191.             char buf[64];
  192.             sprintf(buf, "%d%c", (int) showLongitude, ew);
  193.             longLatLabel(buf, longitude, 0.0, viewRayOrigin, viewNormal, posd, q, semiAxes, offset, renderer);
  194.             if (longitude > 0.0f && longitude < 180.0f)
  195.             {
  196.                 showLongitude = (int) longitude;
  197.                 switch (longitudeConvention)
  198.                 {
  199.                 case EastWest:
  200.                     ew = 'W';
  201.                     showLongitude = (int) longitude;
  202.                     break;
  203.                 case Eastward:
  204.                     showLongitude = (int) longitude;
  205.                     ew = 'E';
  206.                     break;
  207.                 case Westward:
  208.                     showLongitude = (int) longitude;
  209.                     ew = 'W';
  210.                     break;
  211.                 }
  212.                 sprintf(buf, "%d%c", showLongitude, ew);       
  213.                 longLatLabel(buf, -longitude, 0.0, viewRayOrigin, viewNormal, posd, q, semiAxes, offset, renderer);
  214.             }
  215.         }
  216.     }
  217.     glDisableClientState(GL_VERTEX_ARRAY);
  218.     glPopMatrix();
  219.     glDisable(GL_LIGHTING);
  220.     glDisable(GL_DEPTH_TEST);
  221.     glDepthMask(GL_FALSE);
  222.     glEnable(GL_TEXTURE_2D);
  223.     glEnable(GL_BLEND);
  224.     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  225. }
  226. float
  227. PlanetographicGrid::boundingSphereRadius() const
  228. {
  229.     return body.getRadius();
  230. }
  231. /*! Determine the longitude convention to use based on IAU rules:
  232.  *    Westward for prograde rotators, Eastward for retrograde
  233.  *    rotators, EastWest for the Earth and Moon.
  234.  */
  235. void
  236. PlanetographicGrid::setIAULongLatConvention()
  237. {
  238.     if (body.getName() == "Earth" || body.getName() == "Moon")
  239.     {
  240.         northDirection = NorthNormal;
  241.         longitudeConvention = EastWest;
  242.     }
  243.     else
  244.     {
  245.         if (body.getAngularVelocity(astro::J2000).y >= 0.0)
  246.         {
  247.             northDirection = NorthNormal;
  248.             longitudeConvention = Westward;
  249.         }
  250.         else
  251.         {
  252.             northDirection = NorthReversed;
  253.             longitudeConvention = Eastward;
  254.         }
  255.     }
  256. }
  257. void
  258. PlanetographicGrid::InitializeGeometry()
  259. {
  260.     xyCircle = new float[circleSubdivisions * 3];
  261.     xzCircle = new float[circleSubdivisions * 3];
  262.     for (unsigned int i = 0; i < circleSubdivisions; i++)
  263.     {
  264.         float theta = (float) (2.0 * PI) * (float) i / (float) circleSubdivisions;
  265.         xyCircle[i * 3 + 0] = (float) cos(theta);
  266.         xyCircle[i * 3 + 1] = (float) sin(theta);
  267.         xyCircle[i * 3 + 2] = 0.0f;
  268.         xzCircle[i * 3 + 0] = (float) cos(theta);
  269.         xzCircle[i * 3 + 1] = 0.0f;
  270.         xzCircle[i * 3 + 2] = (float) sin(theta);
  271.     }
  272. }