2008-04-08 18:26:07 -06:00
|
|
|
// visibleregion.cpp
|
|
|
|
//
|
|
|
|
// Visible region reference mark 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.
|
|
|
|
|
2020-06-14 10:06:05 -06:00
|
|
|
#include <cmath>
|
|
|
|
#include <Eigen/Geometry>
|
|
|
|
#include <celmath/intersect.h>
|
2008-04-08 18:26:07 -06:00
|
|
|
#include "body.h"
|
2020-06-14 10:06:05 -06:00
|
|
|
#include "render.h"
|
2008-04-08 18:26:07 -06:00
|
|
|
#include "selection.h"
|
|
|
|
#include "vecgl.h"
|
2019-07-07 03:29:31 -06:00
|
|
|
#include "vertexobject.h"
|
2020-06-14 10:06:05 -06:00
|
|
|
#include "visibleregion.h"
|
2009-07-14 22:00:22 -06:00
|
|
|
|
2021-03-03 11:34:35 -07:00
|
|
|
using namespace std;
|
2009-07-14 22:00:22 -06:00
|
|
|
using namespace Eigen;
|
2019-05-16 15:51:11 -06:00
|
|
|
using namespace celmath;
|
2008-04-08 18:26:07 -06:00
|
|
|
|
|
|
|
|
|
|
|
/*! Construct a new reference mark that shows the outline of the
|
|
|
|
* region on the surface of a body in which the target object is
|
|
|
|
* visible. The following are assumed:
|
|
|
|
* - target is a point
|
|
|
|
* - the body is an ellipsoid
|
2018-03-11 07:12:58 -06:00
|
|
|
*
|
2008-04-08 18:26:07 -06:00
|
|
|
* This reference mark is useful in a few situations. When the
|
|
|
|
* body is a planet or moon and target is the sun, the outline of
|
|
|
|
* the visible region is the terminator. If target is a satellite,
|
|
|
|
* the outline is its circle of visibility.
|
|
|
|
*/
|
|
|
|
VisibleRegion::VisibleRegion(const Body& body, const Selection& target) :
|
|
|
|
m_body(body),
|
|
|
|
m_target(target),
|
|
|
|
m_color(1.0f, 1.0f, 0.0f),
|
2008-04-09 18:12:51 -06:00
|
|
|
#ifdef USE_HDR
|
|
|
|
m_opacity(0.0f)
|
|
|
|
#else
|
2008-04-08 18:26:07 -06:00
|
|
|
m_opacity(1.0f)
|
2008-04-09 18:12:51 -06:00
|
|
|
#endif
|
2008-04-08 18:26:07 -06:00
|
|
|
{
|
|
|
|
setTag("visible region");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Color
|
|
|
|
VisibleRegion::color() const
|
|
|
|
{
|
|
|
|
return m_color;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
VisibleRegion::setColor(Color color)
|
|
|
|
{
|
|
|
|
m_color = color;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
VisibleRegion::opacity() const
|
|
|
|
{
|
|
|
|
return m_opacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
VisibleRegion::setOpacity(float opacity)
|
|
|
|
{
|
|
|
|
m_opacity = opacity;
|
2008-04-09 18:12:51 -06:00
|
|
|
#ifdef USE_HDR
|
|
|
|
m_opacity = 1.0f - opacity;
|
|
|
|
#endif
|
2008-04-08 18:26:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-07 03:29:31 -06:00
|
|
|
constexpr const unsigned maxSections = 360;
|
|
|
|
|
|
|
|
static void
|
2020-05-12 09:51:25 -06:00
|
|
|
renderTerminator(Renderer* renderer,
|
2020-12-05 02:56:43 -07:00
|
|
|
const vector<LineStripEnd>& pos,
|
2020-05-12 09:51:25 -06:00
|
|
|
const Color& color,
|
2020-09-29 20:27:22 -06:00
|
|
|
const Matrices& mvp)
|
2019-07-07 03:29:31 -06:00
|
|
|
{
|
|
|
|
/*!
|
|
|
|
* Proper terminator calculation requires double precision floats in GLSL
|
|
|
|
* which were introduced in ARB_gpu_shader_fp64 unavailable with GL2.1.
|
|
|
|
* Because of this we make calculations on a CPU and stream results to GPU.
|
|
|
|
*/
|
|
|
|
|
2020-12-12 19:11:35 -07:00
|
|
|
using AttributesType = celgl::VertexObject::AttributesType;
|
|
|
|
|
2020-04-22 10:44:09 -06:00
|
|
|
ShaderProperties shadprop;
|
2020-12-12 19:11:35 -07:00
|
|
|
shadprop.texUsage = ShaderProperties::VertexColors;
|
2020-04-22 10:44:09 -06:00
|
|
|
shadprop.lightModel = ShaderProperties::UnlitModel;
|
2020-12-12 19:11:35 -07:00
|
|
|
|
|
|
|
bool lineAsTriangles = renderer->shouldDrawLineAsTriangles();
|
|
|
|
if (lineAsTriangles)
|
|
|
|
shadprop.texUsage |= ShaderProperties::LineAsTriangles;
|
|
|
|
|
2020-04-22 10:44:09 -06:00
|
|
|
auto *prog = renderer->getShaderManager().getShader(shadprop);
|
2019-07-07 03:29:31 -06:00
|
|
|
if (prog == nullptr)
|
|
|
|
return;
|
|
|
|
|
2020-03-14 04:48:24 -06:00
|
|
|
auto &vo = renderer->getVertexObject(VOType::Terminator, GL_ARRAY_BUFFER, 0, GL_STREAM_DRAW);
|
|
|
|
|
2020-12-12 19:11:35 -07:00
|
|
|
vo.bindWritable(lineAsTriangles ? AttributesType::Default : AttributesType::Alternative1);
|
2019-07-07 03:29:31 -06:00
|
|
|
if (!vo.initialized())
|
|
|
|
{
|
2020-12-05 02:56:43 -07:00
|
|
|
vo.setBufferSize((maxSections + 2) * 2 * sizeof(LineStripEnd));
|
2019-07-07 03:29:31 -06:00
|
|
|
vo.allocate();
|
2020-12-12 19:11:35 -07:00
|
|
|
|
|
|
|
// Attributes for lines drawn as triangles
|
2020-12-05 02:56:43 -07:00
|
|
|
vo.setVertices(3, GL_FLOAT, false, sizeof(LineStripEnd), 0);
|
|
|
|
vo.setVertexAttribArray(CelestiaGLProgram::NextVCoordAttributeIndex,
|
|
|
|
3, GL_FLOAT, false, sizeof(LineStripEnd)
|
|
|
|
, 2 * sizeof(LineStripEnd) + offsetof(LineStripEnd, point));
|
|
|
|
vo.setVertexAttribArray(CelestiaGLProgram::ScaleFactorAttributeIndex,
|
|
|
|
1, GL_FLOAT, false, sizeof(LineStripEnd)
|
|
|
|
, offsetof(LineStripEnd, scale));
|
2020-12-12 19:11:35 -07:00
|
|
|
|
|
|
|
// Attributes for lines drawn as lines
|
|
|
|
vo.setVertices(3, GL_FLOAT, false, 2 * sizeof(LineStripEnd), 0, AttributesType::Alternative1);
|
2019-07-07 03:29:31 -06:00
|
|
|
}
|
|
|
|
|
2020-12-05 02:56:43 -07:00
|
|
|
vo.setBufferData(pos.data(), 0, pos.size() * sizeof(LineStripEnd));
|
2019-07-07 03:29:31 -06:00
|
|
|
|
|
|
|
prog->use();
|
2020-09-29 20:27:22 -06:00
|
|
|
prog->setMVPMatrices(*mvp.projection, *mvp.modelview);
|
2020-04-28 06:56:56 -06:00
|
|
|
glVertexAttrib(CelestiaGLProgram::ColorAttributeIndex, color);
|
2020-12-12 19:11:35 -07:00
|
|
|
if (lineAsTriangles)
|
|
|
|
{
|
|
|
|
prog->lineWidthX = renderer->getLineWidthX();
|
|
|
|
prog->lineWidthY = renderer->getLineWidthY();
|
|
|
|
vo.draw(GL_TRIANGLE_STRIP, pos.size() - 2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vo.draw(GL_LINE_STRIP, (pos.size() - 2) / 2, 0);
|
|
|
|
}
|
2019-07-07 03:29:31 -06:00
|
|
|
|
|
|
|
vo.unbind();
|
|
|
|
}
|
|
|
|
|
2008-04-08 18:26:07 -06:00
|
|
|
|
|
|
|
void
|
2019-07-07 03:29:31 -06:00
|
|
|
VisibleRegion::render(Renderer* renderer,
|
2020-05-12 09:51:25 -06:00
|
|
|
const Vector3f& position,
|
2008-04-08 18:26:07 -06:00
|
|
|
float discSizeInPixels,
|
2020-05-12 09:51:25 -06:00
|
|
|
double tdb,
|
|
|
|
const Matrices& m) const
|
2008-04-08 18:26:07 -06:00
|
|
|
{
|
2009-01-30 15:07:24 -07:00
|
|
|
// Don't render anything if the current time is not within the
|
|
|
|
// target object's time window.
|
2018-09-22 07:13:49 -06:00
|
|
|
if (m_target.body() != nullptr)
|
2009-01-30 15:07:24 -07:00
|
|
|
{
|
|
|
|
if (!m_target.body()->extant(tdb))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-04-08 18:26:07 -06:00
|
|
|
// Fade in the terminator when the planet is small
|
|
|
|
const float minDiscSize = 5.0f;
|
|
|
|
const float fullOpacityDiscSize = 10.0f;
|
|
|
|
float opacity = (discSizeInPixels - minDiscSize) / (fullOpacityDiscSize - minDiscSize);
|
|
|
|
|
|
|
|
// Don't render the terminator if the it's smaller than the minimum size
|
|
|
|
if (opacity <= 0.0f)
|
|
|
|
return;
|
|
|
|
opacity = min(opacity, 1.0f) * m_opacity;
|
|
|
|
|
|
|
|
// Base the amount of subdivision on the apparent size
|
2018-09-22 07:13:49 -06:00
|
|
|
auto nSections = (unsigned int) (30.0f + discSizeInPixels * 0.5f);
|
2019-07-07 03:29:31 -06:00
|
|
|
nSections = min(nSections, maxSections);
|
2008-04-08 18:26:07 -06:00
|
|
|
|
2009-07-16 17:37:48 -06:00
|
|
|
Quaterniond q = m_body.getEclipticToBodyFixed(tdb);
|
2009-07-14 22:00:22 -06:00
|
|
|
Quaternionf qf = q.cast<float>();
|
2008-04-08 18:26:07 -06:00
|
|
|
|
|
|
|
// The outline can't be rendered exactly on the planet sphere, or
|
2008-04-10 19:14:23 -06:00
|
|
|
// 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.0001f);
|
2008-04-08 18:26:07 -06:00
|
|
|
|
2009-07-16 17:37:48 -06:00
|
|
|
Vector3f semiAxes = m_body.getSemiAxes();
|
2008-04-08 18:26:07 -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->enableBlending();
|
2008-04-09 18:12:51 -06:00
|
|
|
#ifdef USE_HDR
|
2020-06-14 10:06:05 -06:00
|
|
|
renderer->setBlendingFactors(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
|
2008-04-09 18:12:51 -06:00
|
|
|
#else
|
2020-06-14 10:06:05 -06:00
|
|
|
renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
2008-04-09 18:12:51 -06:00
|
|
|
#endif
|
2008-04-08 18:26:07 -06:00
|
|
|
|
|
|
|
double maxSemiAxis = m_body.getRadius();
|
|
|
|
|
|
|
|
// In order to avoid precision problems and extremely large values, scale
|
|
|
|
// the target position and semiaxes such that the largest semiaxis is 1.0.
|
2009-07-24 00:59:54 -06:00
|
|
|
Vector3d lightDir = m_body.getPosition(tdb).offsetFromKm(m_target.getPosition(tdb));
|
|
|
|
lightDir = lightDir / maxSemiAxis;
|
2009-07-14 22:00:22 -06:00
|
|
|
lightDir = q * lightDir;
|
2008-04-08 18:26:07 -06:00
|
|
|
|
|
|
|
// Another measure to prevent precision problems: if the distance to the
|
|
|
|
// object is much greater than the largest semi axis, clamp it to 1e4 times
|
|
|
|
// the radius, as body-to-target rays at that distance are nearly parallel anyhow.
|
2009-07-14 22:00:22 -06:00
|
|
|
if (lightDir.norm() > 10000.0)
|
|
|
|
lightDir *= (10000.0 / lightDir.norm());
|
2008-04-08 18:26:07 -06:00
|
|
|
|
|
|
|
// Pick two orthogonal axes both normal to the light direction
|
2009-07-14 22:00:22 -06:00
|
|
|
Vector3d lightDirNorm = lightDir.normalized();
|
2008-04-08 18:26:07 -06:00
|
|
|
|
2009-12-01 20:35:59 -07:00
|
|
|
Vector3d uAxis = lightDirNorm.unitOrthogonal();
|
2009-07-14 22:00:22 -06:00
|
|
|
Vector3d vAxis = uAxis.cross(lightDirNorm);
|
2008-04-08 18:26:07 -06:00
|
|
|
|
2017-04-08 14:35:42 -06:00
|
|
|
Vector3d recipSemiAxes = maxSemiAxis * semiAxes.cast<double>().cwiseInverse();
|
2009-07-14 22:00:22 -06:00
|
|
|
Vector3d e = -lightDir;
|
2017-04-08 14:35:42 -06:00
|
|
|
Vector3d e_ = e.cwiseProduct(recipSemiAxes);
|
2009-07-14 22:00:22 -06:00
|
|
|
double ee = e_.squaredNorm();
|
2008-04-08 18:26:07 -06:00
|
|
|
|
2020-12-05 02:56:43 -07:00
|
|
|
vector<LineStripEnd> pos;
|
|
|
|
pos.reserve((nSections + 2) * 2);
|
2008-04-08 18:26:07 -06:00
|
|
|
|
2020-12-05 02:56:43 -07:00
|
|
|
for (unsigned i = 0; i <= nSections + 1; i++)
|
2008-04-08 18:26:07 -06:00
|
|
|
{
|
|
|
|
double theta = (double) i / (double) (nSections) * 2.0 * PI;
|
2009-07-14 22:00:22 -06:00
|
|
|
Vector3d w = cos(theta) * uAxis + sin(theta) * vAxis;
|
2018-03-11 07:12:58 -06:00
|
|
|
|
2009-07-14 22:00:22 -06:00
|
|
|
Vector3d toCenter = ellipsoidTangent(recipSemiAxes, w, e, e_, ee);
|
2008-04-08 18:26:07 -06:00
|
|
|
toCenter *= maxSemiAxis * scale;
|
2020-12-05 02:56:43 -07:00
|
|
|
Vector3f thisPoint = toCenter.cast<float>();
|
|
|
|
pos.emplace_back(thisPoint, -0.5);
|
|
|
|
pos.emplace_back(thisPoint, 0.5);
|
2008-04-08 18:26:07 -06:00
|
|
|
}
|
|
|
|
|
2020-05-12 09:51:25 -06:00
|
|
|
Affine3f transform = Translation3f(position) * qf.conjugate();
|
2020-09-29 20:27:22 -06:00
|
|
|
Matrix4f modelView = (*m.modelview) * transform.matrix();
|
|
|
|
renderTerminator(renderer, pos, Color(m_color, opacity), { m.projection, &modelView });
|
2008-04-08 18:26:07 -06:00
|
|
|
|
2020-06-14 10:06:05 -06:00
|
|
|
renderer->enableBlending();
|
|
|
|
renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE);
|
2008-04-08 18:26:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
VisibleRegion::boundingSphereRadius() const
|
|
|
|
{
|
|
|
|
return m_body.getRadius();
|
|
|
|
}
|