Celestia

Форк
0
/
visibleregion.cpp 
181 строка · 5.5 Кб
1
// visibleregion.cpp
2
//
3
// Visible region reference mark for ellipsoidal bodies.
4
//
5
// Copyright (C) 2008-present, 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

13
#include <cmath>
14
#include <Eigen/Geometry>
15
#include <celcompat/numbers.h>
16
#include <celmath/intersect.h>
17
#include <celrender/linerenderer.h>
18
#include "body.h"
19
#include "render.h"
20
#include "selection.h"
21
#include "visibleregion.h"
22

23
using namespace std;
24
using namespace Eigen;
25
using celestia::render::LineRenderer;
26
namespace math = celestia::math;
27

28

29
/*! Construct a new reference mark that shows the outline of the
30
 *  region on the surface of a body in which the target object is
31
 *  visible. The following are assumed:
32
 *     - target is a point
33
 *     - the body is an ellipsoid
34
 *
35
 *  This reference mark is useful in a few situations. When the
36
 *  body is a planet or moon and target is the sun, the outline of
37
 *  the visible region is the terminator. If target is a satellite,
38
 *  the outline is its circle of visibility.
39
 */
40
VisibleRegion::VisibleRegion(const Body& body, const Selection& target) :
41
    m_body(body),
42
    m_target(target),
43
    m_color(1.0f, 1.0f, 0.0f),
44
    m_opacity(1.0f)
45
{
46
    setTag("visible region");
47
}
48

49

50
Color
51
VisibleRegion::color() const
52
{
53
    return m_color;
54
}
55

56

57
void
58
VisibleRegion::setColor(Color color)
59
{
60
    m_color = color;
61
}
62

63

64
float
65
VisibleRegion::opacity() const
66
{
67
    return m_opacity;
68
}
69

70

71
void
72
VisibleRegion::setOpacity(float opacity)
73
{
74
    m_opacity = opacity;
75
}
76

77

78
constexpr const unsigned maxSections = 360;
79

80
void
81
VisibleRegion::render(Renderer* renderer,
82
                      const Vector3f& position,
83
                      float discSizeInPixels,
84
                      double tdb,
85
                      const Matrices& m) const
86
{
87
    // Proper terminator calculation requires double precision floats in GLSL
88
    // which were introduced in ARB_gpu_shader_fp64 unavailable with GL2.1.
89
    // Because of this we make calculations on a CPU and stream results to GPU.
90

91
    // Don't render anything if the current time is not within the
92
    // target object's time window.
93
    if (m_target.body() != nullptr)
94
    {
95
        if (!m_target.body()->extant(tdb))
96
            return;
97
    }
98

99
    // Fade in the terminator when the planet is small
100
    const float minDiscSize = 5.0f;
101
    const float fullOpacityDiscSize = 10.0f;
102
    float opacity = (discSizeInPixels - minDiscSize) / (fullOpacityDiscSize - minDiscSize);
103

104
    // Don't render the terminator if the it's smaller than the minimum size
105
    if (opacity <= 0.0f)
106
        return;
107
    opacity = min(opacity, 1.0f) * m_opacity;
108

109
    // Base the amount of subdivision on the apparent size
110
    auto nSections = (unsigned int) (30.0f + discSizeInPixels * 0.5f);
111
    nSections = min(nSections, maxSections);
112

113
    Quaterniond q = m_body.getEclipticToBodyFixed(tdb);
114
    Quaternionf qf = q.cast<float>();
115

116
    // The outline can't be rendered exactly on the planet sphere, or
117
    // there will be z-fighting problems. Render it at a height above the
118
    // planet that will place it about one pixel away from the planet.
119
    float scale = (discSizeInPixels + 1) / discSizeInPixels;
120
    scale = max(scale, 1.0001f);
121

122
    Vector3f semiAxes = m_body.getSemiAxes();
123
    double maxSemiAxis = m_body.getRadius();
124

125
    // In order to avoid precision problems and extremely large values, scale
126
    // the target position and semiaxes such that the largest semiaxis is 1.0.
127
    Vector3d lightDir = m_body.getPosition(tdb).offsetFromKm(m_target.getPosition(tdb));
128
    lightDir = lightDir / maxSemiAxis;
129
    lightDir = q * lightDir;
130

131
    // Another measure to prevent precision problems: if the distance to the
132
    // object is much greater than the largest semi axis, clamp it to 1e4 times
133
    // the radius, as body-to-target rays at that distance are nearly parallel anyhow.
134
    if (lightDir.norm() > 10000.0)
135
        lightDir *= (10000.0 / lightDir.norm());
136

137
    // Pick two orthogonal axes both normal to the light direction
138
    Vector3d lightDirNorm = lightDir.normalized();
139

140
    Vector3d uAxis = lightDirNorm.unitOrthogonal();
141
    Vector3d vAxis = uAxis.cross(lightDirNorm);
142

143
    Vector3d recipSemiAxes = maxSemiAxis * semiAxes.cast<double>().cwiseInverse();
144
    Vector3d e = -lightDir;
145
    Vector3d e_ = e.cwiseProduct(recipSemiAxes);
146
    double ee = e_.squaredNorm();
147

148
    LineRenderer lr(*renderer, 1.0f, LineRenderer::PrimType::LineStrip);
149
    lr.startUpdate();
150

151
    for (unsigned i = 0; i <= nSections + 1; i++)
152
    {
153
        double theta = (double) i / (double) (nSections) * 2.0 * celestia::numbers::pi;
154
        Vector3d w = cos(theta) * uAxis + sin(theta) * vAxis;
155

156
        Vector3d toCenter = math::ellipsoidTangent(recipSemiAxes, w, e, e_, ee);
157
        toCenter *= maxSemiAxis * scale;
158
        lr.addVertex(Vector3f(toCenter.cast<float>()));
159
    }
160

161
    Affine3f transform = Translation3f(position) * qf.conjugate();
162
    Matrix4f modelView = (*m.modelview) * transform.matrix();
163

164
    Renderer::PipelineState ps;
165
    ps.blending = true;
166
    ps.blendFunc = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA};
167
    ps.depthMask = true;
168
    ps.depthTest = true;
169
    ps.smoothLines = true;
170
    renderer->setPipelineState(ps);
171

172
    lr.render({ m.projection, &modelView }, Color(m_color, opacity), nSections+1, 0);
173
    lr.finish();
174
}
175

176

177
float
178
VisibleRegion::boundingSphereRadius() const
179
{
180
    return m_body.getRadius();
181
}
182

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.