planetgrid.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:10k
- // planetgrid.cpp
- //
- // Longitude/latitude grids for ellipsoidal bodies.
- //
- // Copyright (C) 2008, the Celestia Development Team
- // Initial version by Chris Laurel, claurel@gmail.com
- //
- // This program is free software; you can redistribute it and/or
- // modify it under the terms of the GNU General Public License
- // as published by the Free Software Foundation; either version 2
- // of the License, or (at your option) any later version.
- #include <cstdio>
- #include <cmath>
- #include <celmath/intersect.h>
- #include "planetgrid.h"
- #include "body.h"
- #include "gl.h"
- #include "vecgl.h"
- #include "render.h"
- unsigned int PlanetographicGrid::circleSubdivisions = 100;
- float* PlanetographicGrid::xyCircle = NULL;
- float* PlanetographicGrid::xzCircle = NULL;
- PlanetographicGrid::PlanetographicGrid(const Body& _body) :
- body(_body),
- minLongitudeStep(10.0f),
- minLatitudeStep(10.0f),
- longitudeConvention(Westward),
- northDirection(NorthNormal)
- {
- if (xyCircle == NULL)
- InitializeGeometry();
- setTag("planetographic grid");
- setIAULongLatConvention();
- }
- PlanetographicGrid::~PlanetographicGrid()
- {
- }
- static void longLatLabel(const string& labelText,
- double longitude,
- double latitude,
- const Vec3d& viewRayOrigin,
- const Vec3d& viewNormal,
- const Vec3d& bodyCenter,
- const Quatd& bodyOrientation,
- const Vec3f& semiAxes,
- float labelOffset,
- Renderer* renderer)
- {
- double theta = degToRad(longitude);
- double phi = degToRad(latitude);
- Vec3d pos(cos(phi) * cos(theta) * semiAxes.x,
- sin(phi) * semiAxes.y,
- -cos(phi) * sin(theta) * semiAxes.z);
-
- float nearDist = renderer->getNearPlaneDistance();
-
- pos = pos * (1.0 + labelOffset);
-
- double boundingRadius = max(semiAxes.x, max(semiAxes.y, semiAxes.z));
- // Draw the label only if it isn't obscured by the body ellipsoid
- double t = 0.0;
- if (testIntersection(Ray3d(Point3d(0.0, 0.0, 0.0) + viewRayOrigin, pos - viewRayOrigin),
- Ellipsoidd(Vec3d(semiAxes.x, semiAxes.y, semiAxes.z)), t) && t >= 1.0)
- {
- // Compute the position of the label
- Vec3d labelPos = bodyCenter +
- (1.0 + labelOffset) * pos * bodyOrientation.toMatrix3();
-
- // Calculate the intersection of the eye-to-label ray with the plane perpendicular to
- // the view normal that touches the front of the objects bounding sphere
- double planetZ = viewNormal * bodyCenter - boundingRadius;
- if (planetZ < -nearDist * 1.001)
- planetZ = -nearDist * 1.001;
- double z = viewNormal * labelPos;
- labelPos *= planetZ / z;
-
- renderer->addObjectAnnotation(NULL, labelText,
- Renderer::PlanetographicGridLabelColor,
- Point3f((float) labelPos.x, (float) labelPos.y, (float) labelPos.z));
- }
- }
- void
- PlanetographicGrid::render(Renderer* renderer,
- const Point3f& pos,
- float discSizeInPixels,
- double tdb) const
- {
- Quatd q = Quatd::yrotation(PI) * body.getEclipticToBodyFixed(tdb);
- Quatf qf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
- // The grid can't be rendered exactly on the planet sphere, or
- // there will be z-fighting problems. Render it at a height above the
- // planet that will place it about one pixel away from the planet.
- float scale = (discSizeInPixels + 1) / discSizeInPixels;
- scale = max(scale, 1.001f);
- float offset = scale - 1.0f;
- Vec3f semiAxes = body.getSemiAxes();
- Vec3d posd(pos.x, pos.y, pos.z);
- Vec3d viewRayOrigin = Vec3d(-pos.x, -pos.y, -pos.z) * (~q).toMatrix3();
-
- // Calculate the view normal; this is used for placement of the long/lat
- // label text.
- Vec3f vn = Vec3f(0.0f, 0.0f, -1.0f) * renderer->getCameraOrientation().toMatrix3();
- Vec3d viewNormal(vn.x, vn.y, vn.z);
- // Enable depth buffering
- glEnable(GL_DEPTH_TEST);
- glDepthMask(GL_TRUE);
- glDisable(GL_BLEND);
- glDisable(GL_TEXTURE_2D);
- glPushMatrix();
- glRotate(~qf);
- glScale(scale * semiAxes);
- glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(3, GL_FLOAT, 0, xzCircle);
- // Only show the coordinate labels if the body is sufficiently large on screen
- bool showCoordinateLabels = false;
- if (discSizeInPixels > 50)
- showCoordinateLabels = true;
- float latitudeStep = minLatitudeStep;
- float longitudeStep = minLongitudeStep;
- if (discSizeInPixels < 200)
- {
- latitudeStep = 30.0f;
- longitudeStep = 30.0f;
- }
-
- for (float latitude = -90.0f + latitudeStep; latitude < 90.0f; latitude += latitudeStep)
- {
- float phi = degToRad(latitude);
- float r = (float) cos(phi);
- if (latitude == 0.0f)
- {
- glColor(Renderer::PlanetEquatorColor);
- glLineWidth(2.0f);
- }
- else
- {
- glColor(Renderer::PlanetographicGridColor);
- }
- glPushMatrix();
- glTranslatef(0.0f, (float) sin(phi), 0.0f);
- glScalef(r, r, r);
- glDrawArrays(GL_LINE_LOOP, 0, circleSubdivisions);
- glPopMatrix();
- glLineWidth(1.0f);
-
- if (showCoordinateLabels)
- {
- if (latitude != 0.0f && abs(latitude) < 90.0f)
- {
- char buf[64];
- char ns;
- if (latitude < 0.0f)
- ns = northDirection == NorthNormal ? 'S' : 'N';
- else
- ns = northDirection == NorthNormal ? 'N' : 'S';
- sprintf(buf, "%d%c", (int) fabs((double) latitude), ns);
- longLatLabel(buf, 0.0, latitude, viewRayOrigin, viewNormal, posd, q, semiAxes, offset, renderer);
- longLatLabel(buf, 180.0, latitude, viewRayOrigin, viewNormal, posd, q, semiAxes, offset, renderer);
- }
- }
- }
- glVertexPointer(3, GL_FLOAT, 0, xyCircle);
- for (float longitude = 0.0f; longitude <= 180.0f; longitude += longitudeStep)
- {
- glColor(Renderer::PlanetographicGridColor);
- glPushMatrix();
- glRotatef(longitude, 0.0f, 1.0f, 0.0f);
- glDrawArrays(GL_LINE_LOOP, 0, circleSubdivisions);
- glPopMatrix();
- if (showCoordinateLabels)
- {
- int showLongitude = 0;
- char ew = 'E';
- switch (longitudeConvention)
- {
- case EastWest:
- ew = 'E';
- showLongitude = (int) longitude;
- break;
- case Eastward:
- if (longitude > 0.0f)
- showLongitude = 360 - (int) longitude;
- ew = 'E';
- break;
- case Westward:
- if (longitude > 0.0f)
- showLongitude = 360 - (int) longitude;
- ew = 'W';
- break;
- }
- char buf[64];
- sprintf(buf, "%d%c", (int) showLongitude, ew);
- longLatLabel(buf, longitude, 0.0, viewRayOrigin, viewNormal, posd, q, semiAxes, offset, renderer);
- if (longitude > 0.0f && longitude < 180.0f)
- {
- showLongitude = (int) longitude;
- switch (longitudeConvention)
- {
- case EastWest:
- ew = 'W';
- showLongitude = (int) longitude;
- break;
- case Eastward:
- showLongitude = (int) longitude;
- ew = 'E';
- break;
- case Westward:
- showLongitude = (int) longitude;
- ew = 'W';
- break;
- }
- sprintf(buf, "%d%c", showLongitude, ew);
- longLatLabel(buf, -longitude, 0.0, viewRayOrigin, viewNormal, posd, q, semiAxes, offset, renderer);
- }
- }
- }
- glDisableClientState(GL_VERTEX_ARRAY);
- glPopMatrix();
- glDisable(GL_LIGHTING);
- glDisable(GL_DEPTH_TEST);
- glDepthMask(GL_FALSE);
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE);
- }
- float
- PlanetographicGrid::boundingSphereRadius() const
- {
- return body.getRadius();
- }
- /*! Determine the longitude convention to use based on IAU rules:
- * Westward for prograde rotators, Eastward for retrograde
- * rotators, EastWest for the Earth and Moon.
- */
- void
- PlanetographicGrid::setIAULongLatConvention()
- {
- if (body.getName() == "Earth" || body.getName() == "Moon")
- {
- northDirection = NorthNormal;
- longitudeConvention = EastWest;
- }
- else
- {
- if (body.getAngularVelocity(astro::J2000).y >= 0.0)
- {
- northDirection = NorthNormal;
- longitudeConvention = Westward;
- }
- else
- {
- northDirection = NorthReversed;
- longitudeConvention = Eastward;
- }
- }
- }
- void
- PlanetographicGrid::InitializeGeometry()
- {
- xyCircle = new float[circleSubdivisions * 3];
- xzCircle = new float[circleSubdivisions * 3];
- for (unsigned int i = 0; i < circleSubdivisions; i++)
- {
- float theta = (float) (2.0 * PI) * (float) i / (float) circleSubdivisions;
- xyCircle[i * 3 + 0] = (float) cos(theta);
- xyCircle[i * 3 + 1] = (float) sin(theta);
- xyCircle[i * 3 + 2] = 0.0f;
- xzCircle[i * 3 + 0] = (float) cos(theta);
- xzCircle[i * 3 + 1] = 0.0f;
- xzCircle[i * 3 + 2] = (float) sin(theta);
- }
- }