2008-03-20 15:36:17 -06:00
|
|
|
// planetgrid.cpp
|
|
|
|
//
|
|
|
|
// Longitude/latitude grids for ellipsoidal bodies.
|
|
|
|
//
|
2009-10-04 08:12:59 -06:00
|
|
|
// Copyright (C) 2008-2009, the Celestia Development Team
|
2008-03-20 15:36:17 -06:00
|
|
|
// 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.
|
|
|
|
|
2009-10-04 08:12:59 -06:00
|
|
|
#include <cmath>
|
2020-06-14 10:06:05 -06:00
|
|
|
#include <Eigen/Geometry>
|
|
|
|
#include <celmath/intersect.h>
|
2019-08-28 20:29:36 -06:00
|
|
|
#include <celutil/debug.h>
|
2020-06-14 10:06:05 -06:00
|
|
|
#include "body.h"
|
|
|
|
#include "planetgrid.h"
|
|
|
|
#include "render.h"
|
|
|
|
#include "vecgl.h"
|
2009-07-14 21:06:30 -06:00
|
|
|
|
|
|
|
using namespace Eigen;
|
2019-05-16 15:51:11 -06:00
|
|
|
using namespace celmath;
|
2020-05-12 09:51:25 -06:00
|
|
|
using namespace celestia;
|
2008-03-20 15:36:17 -06:00
|
|
|
|
|
|
|
|
|
|
|
unsigned int PlanetographicGrid::circleSubdivisions = 100;
|
2020-12-05 02:56:43 -07:00
|
|
|
std::vector<LineStripEnd> PlanetographicGrid::xyCircle;
|
|
|
|
std::vector<LineStripEnd> PlanetographicGrid::xzCircle;
|
2008-03-20 15:36:17 -06:00
|
|
|
|
|
|
|
|
|
|
|
PlanetographicGrid::PlanetographicGrid(const Body& _body) :
|
2018-09-22 07:13:49 -06:00
|
|
|
body(_body)
|
2008-03-20 15:36:17 -06:00
|
|
|
{
|
2020-12-05 02:56:43 -07:00
|
|
|
if (xyCircle.empty())
|
2008-03-20 15:36:17 -06:00
|
|
|
InitializeGeometry();
|
|
|
|
setTag("planetographic grid");
|
|
|
|
setIAULongLatConvention();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void longLatLabel(const string& labelText,
|
|
|
|
double longitude,
|
|
|
|
double latitude,
|
2009-07-14 21:06:30 -06:00
|
|
|
const Vector3d& viewRayOrigin,
|
|
|
|
const Vector3d& viewNormal,
|
|
|
|
const Vector3d& bodyCenter,
|
|
|
|
const Quaterniond& bodyOrientation,
|
|
|
|
const Vector3f& semiAxes,
|
2008-03-24 11:45:04 -06:00
|
|
|
float labelOffset,
|
2008-03-20 15:36:17 -06:00
|
|
|
Renderer* renderer)
|
|
|
|
{
|
|
|
|
double theta = degToRad(longitude);
|
|
|
|
double phi = degToRad(latitude);
|
2009-07-14 21:06:30 -06:00
|
|
|
Vector3d pos(cos(phi) * cos(theta) * semiAxes.x(),
|
|
|
|
sin(phi) * semiAxes.y(),
|
|
|
|
-cos(phi) * sin(theta) * semiAxes.z());
|
2018-03-11 07:12:58 -06:00
|
|
|
|
2008-04-30 12:31:30 -06:00
|
|
|
float nearDist = renderer->getNearPlaneDistance();
|
2018-03-11 07:12:58 -06:00
|
|
|
|
2008-03-24 11:45:04 -06:00
|
|
|
pos = pos * (1.0 + labelOffset);
|
2018-03-11 07:12:58 -06:00
|
|
|
|
2009-07-14 21:06:30 -06:00
|
|
|
double boundingRadius = semiAxes.maxCoeff();
|
2008-03-20 15:36:17 -06:00
|
|
|
|
|
|
|
// Draw the label only if it isn't obscured by the body ellipsoid
|
|
|
|
double t = 0.0;
|
2009-07-14 21:06:30 -06:00
|
|
|
if (testIntersection(Ray3d(viewRayOrigin, pos - viewRayOrigin), Ellipsoidd(semiAxes.cast<double>()), t) && t >= 1.0)
|
2008-03-20 15:36:17 -06:00
|
|
|
{
|
2008-04-30 12:31:30 -06:00
|
|
|
// Compute the position of the label
|
2009-07-14 21:06:30 -06:00
|
|
|
Vector3d labelPos = bodyCenter +
|
|
|
|
bodyOrientation.conjugate() * pos * (1.0 + labelOffset);
|
2018-03-11 07:12:58 -06:00
|
|
|
|
2008-04-30 12:31:30 -06:00
|
|
|
// 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
|
2009-07-14 21:06:30 -06:00
|
|
|
double planetZ = viewNormal.dot(bodyCenter) - boundingRadius;
|
2008-04-30 12:31:30 -06:00
|
|
|
if (planetZ < -nearDist * 1.001)
|
|
|
|
planetZ = -nearDist * 1.001;
|
2009-07-14 21:06:30 -06:00
|
|
|
double z = viewNormal.dot(labelPos);
|
2008-04-30 12:31:30 -06:00
|
|
|
labelPos *= planetZ / z;
|
2018-03-11 07:12:58 -06:00
|
|
|
|
2018-09-22 07:13:49 -06:00
|
|
|
renderer->addObjectAnnotation(nullptr, labelText,
|
2008-04-30 12:31:30 -06:00
|
|
|
Renderer::PlanetographicGridLabelColor,
|
2009-07-20 22:04:26 -06:00
|
|
|
labelPos.cast<float>());
|
2008-03-20 15:36:17 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
PlanetographicGrid::render(Renderer* renderer,
|
2009-07-20 22:04:26 -06:00
|
|
|
const Eigen::Vector3f& pos,
|
2008-03-20 15:36:17 -06:00
|
|
|
float discSizeInPixels,
|
2020-05-12 09:51:25 -06:00
|
|
|
double tdb,
|
|
|
|
const Matrices& m) const
|
2008-03-20 15:36:17 -06:00
|
|
|
{
|
2020-04-22 10:44:09 -06:00
|
|
|
ShaderProperties shadprop;
|
2020-12-05 02:56:43 -07:00
|
|
|
shadprop.texUsage = ShaderProperties::VertexColors | ShaderProperties::LineAsTriangles;
|
2020-04-22 10:44:09 -06:00
|
|
|
shadprop.lightModel = ShaderProperties::UnlitModel;
|
|
|
|
auto *prog = renderer->getShaderManager().getShader(shadprop);
|
2019-11-24 12:21:21 -07:00
|
|
|
if (prog == nullptr)
|
|
|
|
return;
|
|
|
|
|
2009-07-14 21:06:30 -06:00
|
|
|
// Compatibility
|
2009-07-16 17:37:48 -06:00
|
|
|
Quaterniond q = Quaterniond(AngleAxis<double>(PI, Vector3d::UnitY())) * body.getEclipticToBodyFixed(tdb);
|
2009-07-14 21:06:30 -06:00
|
|
|
Quaternionf qf = q.cast<float>();
|
2008-03-20 15:36:17 -06:00
|
|
|
|
2008-04-10 19:14:23 -06:00
|
|
|
// 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;
|
|
|
|
|
2009-07-16 17:37:48 -06:00
|
|
|
Vector3f semiAxes = body.getSemiAxes();
|
2009-07-20 22:04:26 -06:00
|
|
|
Vector3d posd = pos.cast<double>();
|
2020-05-12 09:51:25 -06:00
|
|
|
Vector3d viewRayOrigin = q * -posd;
|
2018-03-11 07:12:58 -06:00
|
|
|
|
2008-04-30 12:31:30 -06:00
|
|
|
// Calculate the view normal; this is used for placement of the long/lat
|
|
|
|
// label text.
|
2009-07-20 22:04:26 -06:00
|
|
|
Vector3f vn = renderer->getCameraOrientation().conjugate() * -Vector3f::UnitZ();
|
|
|
|
Vector3d viewNormal = vn.cast<double>();
|
2008-03-20 15:36:17 -06:00
|
|
|
|
|
|
|
// Enable depth buffering
|
2020-07-07 11:00:08 -06:00
|
|
|
renderer->enableDepthTest();
|
|
|
|
renderer->enableDepthMask();
|
2020-06-14 10:06:05 -06:00
|
|
|
renderer->disableBlending();
|
2008-03-20 15:36:17 -06:00
|
|
|
|
2020-05-12 09:51:25 -06:00
|
|
|
Affine3f transform = Translation3f(pos) * qf.conjugate() * Scaling(scale * semiAxes);
|
2020-09-29 20:27:22 -06:00
|
|
|
Matrix4f projection = *m.projection;
|
|
|
|
Matrix4f modelView = *m.modelview * transform.matrix();
|
2008-03-20 15:36:17 -06:00
|
|
|
|
2020-04-28 06:56:56 -06:00
|
|
|
glEnableVertexAttribArray(CelestiaGLProgram::VertexCoordAttributeIndex);
|
2020-12-05 02:56:43 -07:00
|
|
|
glEnableVertexAttribArray(CelestiaGLProgram::NextVCoordAttributeIndex);
|
|
|
|
glEnableVertexAttribArray(CelestiaGLProgram::ScaleFactorAttributeIndex);
|
2020-04-28 06:56:56 -06:00
|
|
|
glVertexAttribPointer(CelestiaGLProgram::VertexCoordAttributeIndex,
|
2020-12-05 02:56:43 -07:00
|
|
|
3, GL_FLOAT, GL_FALSE, sizeof(LineStripEnd), &xzCircle[0].point);
|
|
|
|
glVertexAttribPointer(CelestiaGLProgram::NextVCoordAttributeIndex,
|
|
|
|
3, GL_FLOAT, GL_FALSE, sizeof(LineStripEnd), &xzCircle[2].point);
|
|
|
|
glVertexAttribPointer(CelestiaGLProgram::ScaleFactorAttributeIndex,
|
|
|
|
1, GL_FLOAT, GL_FALSE, sizeof(LineStripEnd), &xzCircle[0].scale);
|
2008-03-20 15:36:17 -06:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
2018-03-11 07:12:58 -06:00
|
|
|
|
2019-11-24 12:21:21 -07:00
|
|
|
prog->use();
|
2020-12-05 02:56:43 -07:00
|
|
|
float lineWidthX = renderer->getLineWidthX();
|
|
|
|
float lineWidthY = renderer->getLineWidthY();
|
2019-11-24 12:21:21 -07:00
|
|
|
|
2008-03-20 15:36:17 -06:00
|
|
|
for (float latitude = -90.0f + latitudeStep; latitude < 90.0f; latitude += latitudeStep)
|
|
|
|
{
|
|
|
|
float phi = degToRad(latitude);
|
2018-09-22 07:13:49 -06:00
|
|
|
auto r = (float) std::cos(phi);
|
2008-03-20 15:36:17 -06:00
|
|
|
|
|
|
|
if (latitude == 0.0f)
|
|
|
|
{
|
2020-04-28 06:56:56 -06:00
|
|
|
glVertexAttrib(CelestiaGLProgram::ColorAttributeIndex,
|
|
|
|
Renderer::PlanetEquatorColor);
|
2020-12-05 02:56:43 -07:00
|
|
|
prog->lineWidthX = 2.0f * lineWidthX;
|
|
|
|
prog->lineWidthY = 2.0f * lineWidthY;
|
2008-03-20 15:36:17 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-28 06:56:56 -06:00
|
|
|
glVertexAttrib(CelestiaGLProgram::ColorAttributeIndex,
|
|
|
|
Renderer::PlanetographicGridColor);
|
2020-12-05 02:56:43 -07:00
|
|
|
prog->lineWidthX = lineWidthX;
|
|
|
|
prog->lineWidthY = lineWidthY;
|
2008-03-20 15:36:17 -06:00
|
|
|
}
|
2020-09-29 20:27:22 -06:00
|
|
|
prog->setMVPMatrices(projection, modelView * vecgl::translate(0.0f, sin(phi), 0.0f) * vecgl::scale(r));
|
2020-12-05 02:56:43 -07:00
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, xzCircle.size() - 2);
|
2018-03-11 07:12:58 -06:00
|
|
|
|
2008-03-20 15:36:17 -06:00
|
|
|
if (showCoordinateLabels)
|
|
|
|
{
|
|
|
|
if (latitude != 0.0f && abs(latitude) < 90.0f)
|
|
|
|
{
|
|
|
|
char ns;
|
|
|
|
if (latitude < 0.0f)
|
|
|
|
ns = northDirection == NorthNormal ? 'S' : 'N';
|
|
|
|
else
|
|
|
|
ns = northDirection == NorthNormal ? 'N' : 'S';
|
2018-10-14 06:56:04 -06:00
|
|
|
string buf;
|
2020-05-12 09:51:25 -06:00
|
|
|
buf = fmt::sprintf("%d%c", (int) fabs(latitude), ns);
|
2008-04-30 12:31:30 -06:00
|
|
|
longLatLabel(buf, 0.0, latitude, viewRayOrigin, viewNormal, posd, q, semiAxes, offset, renderer);
|
|
|
|
longLatLabel(buf, 180.0, latitude, viewRayOrigin, viewNormal, posd, q, semiAxes, offset, renderer);
|
2008-03-20 15:36:17 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-28 06:56:56 -06:00
|
|
|
glVertexAttribPointer(CelestiaGLProgram::VertexCoordAttributeIndex,
|
2020-12-05 02:56:43 -07:00
|
|
|
3, GL_FLOAT, GL_FALSE, sizeof(LineStripEnd), &xyCircle[0].point);
|
|
|
|
glVertexAttribPointer(CelestiaGLProgram::NextVCoordAttributeIndex,
|
|
|
|
3, GL_FLOAT, GL_FALSE, sizeof(LineStripEnd), &xyCircle[2].point);
|
|
|
|
glVertexAttribPointer(CelestiaGLProgram::ScaleFactorAttributeIndex,
|
|
|
|
1, GL_FLOAT, GL_FALSE, sizeof(LineStripEnd), &xyCircle[0].scale);
|
2008-03-20 15:36:17 -06:00
|
|
|
|
2020-04-28 06:56:56 -06:00
|
|
|
glVertexAttrib(CelestiaGLProgram::ColorAttributeIndex,
|
|
|
|
Renderer::PlanetographicGridColor);
|
2008-03-20 15:36:17 -06:00
|
|
|
for (float longitude = 0.0f; longitude <= 180.0f; longitude += longitudeStep)
|
|
|
|
{
|
2020-09-29 20:27:22 -06:00
|
|
|
prog->setMVPMatrices(projection, modelView * vecgl::rotate(AngleAxisf(degToRad(longitude), Vector3f::UnitY())));
|
2020-12-05 02:56:43 -07:00
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, xyCircle.size() - 2);
|
2008-03-20 15:36:17 -06:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-10-14 06:56:04 -06:00
|
|
|
string buf;
|
|
|
|
buf = fmt::sprintf("%d%c", (int) showLongitude, ew);
|
2008-04-30 12:31:30 -06:00
|
|
|
longLatLabel(buf, longitude, 0.0, viewRayOrigin, viewNormal, posd, q, semiAxes, offset, renderer);
|
2008-03-20 15:36:17 -06:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-10-14 06:56:04 -06:00
|
|
|
buf = fmt::sprintf("%d%c", showLongitude, ew);
|
2008-04-30 12:31:30 -06:00
|
|
|
longLatLabel(buf, -longitude, 0.0, viewRayOrigin, viewNormal, posd, q, semiAxes, offset, renderer);
|
2008-03-20 15:36:17 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-28 06:56:56 -06:00
|
|
|
glDisableVertexAttribArray(CelestiaGLProgram::VertexCoordAttributeIndex);
|
2020-12-05 02:56:43 -07:00
|
|
|
glDisableVertexAttribArray(CelestiaGLProgram::NextVCoordAttributeIndex);
|
|
|
|
glDisableVertexAttribArray(CelestiaGLProgram::ScaleFactorAttributeIndex);
|
2008-03-20 15:36:17 -06:00
|
|
|
|
2020-06-14 10:06:05 -06:00
|
|
|
renderer->enableBlending();
|
|
|
|
renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE);
|
2008-03-20 15:36:17 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2009-07-16 17:37:48 -06:00
|
|
|
if (body.getAngularVelocity(astro::J2000).y() >= 0.0)
|
2008-03-20 15:36:17 -06:00
|
|
|
{
|
|
|
|
northDirection = NorthNormal;
|
|
|
|
longitudeConvention = Westward;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
northDirection = NorthReversed;
|
|
|
|
longitudeConvention = Eastward;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
PlanetographicGrid::InitializeGeometry()
|
|
|
|
{
|
2020-12-05 02:56:43 -07:00
|
|
|
xyCircle.reserve((circleSubdivisions + 2) * 2);
|
|
|
|
xzCircle.reserve((circleSubdivisions + 2) * 2);
|
|
|
|
for (unsigned int i = 0; i <= circleSubdivisions + 1; i++)
|
2008-03-20 15:36:17 -06:00
|
|
|
{
|
|
|
|
float theta = (float) (2.0 * PI) * (float) i / (float) circleSubdivisions;
|
2019-11-24 12:21:21 -07:00
|
|
|
float s, c;
|
|
|
|
sincos(theta, s, c);
|
2020-12-05 02:56:43 -07:00
|
|
|
Vector3f thisPointXY(c, s, 0.0f);
|
|
|
|
Vector3f thisPointXZ(c, 0.0f, s);
|
|
|
|
xyCircle.emplace_back(thisPointXY, -0.5f);
|
|
|
|
xyCircle.emplace_back(thisPointXY, 0.5f);
|
|
|
|
xzCircle.emplace_back(thisPointXZ, -0.5f);
|
|
|
|
xzCircle.emplace_back(thisPointXZ, 0.5f);
|
2008-03-20 15:36:17 -06:00
|
|
|
}
|
|
|
|
}
|