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
|
|
|
|
|
|
|
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,
|
|
|
|
const vector<Vector3f>& pos,
|
|
|
|
const Color& color,
|
|
|
|
const Matrix4f& 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-04-22 10:44:09 -06:00
|
|
|
ShaderProperties shadprop;
|
|
|
|
shadprop.texUsage = ShaderProperties::VertexColors;
|
|
|
|
shadprop.lightModel = ShaderProperties::UnlitModel;
|
|
|
|
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);
|
|
|
|
|
2019-07-07 03:29:31 -06:00
|
|
|
vo.bindWritable();
|
|
|
|
if (!vo.initialized())
|
|
|
|
{
|
2020-03-14 04:48:24 -06:00
|
|
|
vo.setBufferSize(maxSections * sizeof(Vector3f));
|
2019-07-07 03:29:31 -06:00
|
|
|
vo.allocate();
|
|
|
|
vo.setVertices(3, GL_FLOAT);
|
|
|
|
}
|
|
|
|
|
|
|
|
vo.setBufferData(pos.data(), 0, pos.size() * sizeof(Vector3f));
|
|
|
|
|
|
|
|
prog->use();
|
2020-05-12 09:51:25 -06:00
|
|
|
prog->MVPMatrix = mvp;
|
2020-04-28 06:56:56 -06:00
|
|
|
glVertexAttrib(CelestiaGLProgram::ColorAttributeIndex, color);
|
2019-07-07 03:29:31 -06:00
|
|
|
|
|
|
|
vo.draw(GL_LINE_LOOP, pos.size());
|
|
|
|
|
|
|
|
vo.unbind();
|
|
|
|
glUseProgram(0);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glDepthMask(GL_TRUE);
|
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
|
|
|
|
2019-07-07 03:29:31 -06:00
|
|
|
vector<Vector3f> pos;
|
|
|
|
pos.reserve(nSections);
|
2008-04-08 18:26:07 -06:00
|
|
|
|
|
|
|
for (unsigned i = 0; i < nSections; i++)
|
|
|
|
{
|
|
|
|
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;
|
2019-07-07 03:29:31 -06:00
|
|
|
pos.push_back(toCenter.cast<float>());
|
2008-04-08 18:26:07 -06:00
|
|
|
}
|
|
|
|
|
2020-05-12 09:51:25 -06:00
|
|
|
Affine3f transform = Translation3f(position) * qf.conjugate();
|
|
|
|
Matrix4f mvp = (*m.projection) * (*m.modelview) * transform.matrix();
|
|
|
|
renderTerminator(renderer, pos, Color(m_color, opacity), mvp);
|
2008-04-08 18:26:07 -06:00
|
|
|
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
glDepthMask(GL_FALSE);
|
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();
|
|
|
|
}
|